| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 1 | /* | 
 | 2 |  * Licensed to the Apache Software Foundation (ASF) under one | 
 | 3 |  * or more contributor license agreements. See the NOTICE file | 
 | 4 |  * distributed with this work for additional information | 
 | 5 |  * regarding copyright ownership. The ASF licenses this file | 
 | 6 |  * to you under the Apache License, Version 2.0 (the | 
 | 7 |  * "License"); you may not use this file except in compliance | 
 | 8 |  * with the License. You may obtain a copy of the License at | 
 | 9 |  * | 
 | 10 |  *   http://www.apache.org/licenses/LICENSE-2.0 | 
 | 11 |  * | 
 | 12 |  * Unless required by applicable law or agreed to in writing, | 
 | 13 |  * software distributed under the License is distributed on an | 
 | 14 |  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | 15 |  * KIND, either express or implied. See the License for the | 
 | 16 |  * specific language governing permissions and limitations | 
 | 17 |  * under the License. | 
 | 18 |  */ | 
 | 19 |  | 
 | 20 | #include "TEvhttpClientChannel.h" | 
 | 21 | #include <evhttp.h> | 
| Roger Meier | 7e056e7 | 2011-07-17 07:28:28 +0000 | [diff] [blame] | 22 | #include "transport/TBufferTransports.h" | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 23 | #include <protocol/TProtocolException.h> | 
 | 24 |  | 
 | 25 | #include <iostream> | 
 | 26 | #include <sstream> | 
 | 27 |  | 
 | 28 | using namespace apache::thrift::protocol; | 
 | 29 | using apache::thrift::transport::TTransportException; | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 30 |  | 
 | 31 | namespace apache { namespace thrift { namespace async { | 
 | 32 |  | 
 | 33 |  | 
 | 34 | TEvhttpClientChannel::TEvhttpClientChannel( | 
 | 35 |     const std::string& host, | 
 | 36 |     const std::string& path, | 
 | 37 |     const char* address, | 
 | 38 |     int port, | 
 | 39 |     struct event_base* eb) | 
 | 40 |   : host_(host) | 
 | 41 |   , path_(path) | 
 | 42 |   , recvBuf_(NULL) | 
 | 43 |   , conn_(NULL) | 
 | 44 | { | 
 | 45 |   conn_ = evhttp_connection_new(address, port); | 
 | 46 |   if (conn_ == NULL) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 47 |     throw TException("evhttp_connection_new failed"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 48 |   } | 
 | 49 |   evhttp_connection_set_base(conn_, eb); | 
 | 50 | } | 
 | 51 |  | 
 | 52 |  | 
 | 53 | TEvhttpClientChannel::~TEvhttpClientChannel() { | 
 | 54 |   if (conn_ != NULL) { | 
 | 55 |     evhttp_connection_free(conn_); | 
 | 56 |   } | 
 | 57 | } | 
 | 58 |  | 
 | 59 |  | 
| Roger Meier | 285cfaa | 2011-07-28 17:51:36 +0000 | [diff] [blame] | 60 | void TEvhttpClientChannel::sendAndRecvMessage( | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 61 |     const VoidCallback& cob, | 
 | 62 |     apache::thrift::transport::TMemoryBuffer* sendBuf, | 
 | 63 |     apache::thrift::transport::TMemoryBuffer* recvBuf) { | 
 | 64 |   cob_ = cob; | 
 | 65 |   recvBuf_ = recvBuf; | 
 | 66 |  | 
 | 67 |   struct evhttp_request* req = evhttp_request_new(response, this); | 
 | 68 |   if (req == NULL) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 69 |     throw TException("evhttp_request_new failed"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 70 |   } | 
 | 71 |  | 
 | 72 |   int rv; | 
 | 73 |  | 
 | 74 |   rv = evhttp_add_header(req->output_headers, "Host", host_.c_str()); | 
 | 75 |   if (rv != 0) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 76 |     throw TException("evhttp_add_header failed"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 77 |   } | 
 | 78 |  | 
 | 79 |   rv = evhttp_add_header(req->output_headers, "Content-Type", "application/x-thrift"); | 
 | 80 |   if (rv != 0) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 81 |     throw TException("evhttp_add_header failed"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 82 |   } | 
 | 83 |  | 
 | 84 |   uint8_t* obuf; | 
 | 85 |   uint32_t sz; | 
 | 86 |   sendBuf->getBuffer(&obuf, &sz); | 
 | 87 |   rv = evbuffer_add(req->output_buffer, obuf, sz); | 
 | 88 |   if (rv != 0) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 89 |     throw TException("evbuffer_add failed"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 90 |   } | 
 | 91 |  | 
 | 92 |   rv = evhttp_make_request(conn_, req, EVHTTP_REQ_POST, path_.c_str()); | 
 | 93 |   if (rv != 0) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 94 |     throw TException("evhttp_make_request failed"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 95 |   } | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 96 | } | 
 | 97 |  | 
 | 98 |  | 
| Roger Meier | 285cfaa | 2011-07-28 17:51:36 +0000 | [diff] [blame] | 99 | void TEvhttpClientChannel::sendMessage( | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 100 |     const VoidCallback& cob, apache::thrift::transport::TMemoryBuffer* message) { | 
| Roger Meier | a8cef6e | 2011-07-17 18:55:59 +0000 | [diff] [blame] | 101 |   (void) cob; | 
 | 102 |   (void) message; | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 103 |   throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, | 
 | 104 | 			   "Unexpected call to TEvhttpClientChannel::sendMessage"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 105 | } | 
 | 106 |  | 
 | 107 |  | 
| Roger Meier | 285cfaa | 2011-07-28 17:51:36 +0000 | [diff] [blame] | 108 | void TEvhttpClientChannel::recvMessage( | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 109 |     const VoidCallback& cob, apache::thrift::transport::TMemoryBuffer* message) { | 
| Roger Meier | a8cef6e | 2011-07-17 18:55:59 +0000 | [diff] [blame] | 110 |   (void) cob; | 
 | 111 |   (void) message; | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 112 |   throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, | 
 | 113 | 			   "Unexpected call to TEvhttpClientChannel::recvMessage"); | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 114 | } | 
 | 115 |  | 
 | 116 |  | 
 | 117 | void TEvhttpClientChannel::finish(struct evhttp_request* req) { | 
 | 118 |   if (req == NULL) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 119 |   try { | 
| Roger Meier | 285cfaa | 2011-07-28 17:51:36 +0000 | [diff] [blame] | 120 |     cob_(); | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 121 |   } catch(const TTransportException& e) { | 
 | 122 |     if(e.getType() == TTransportException::END_OF_FILE) | 
 | 123 |       throw TException("connect failed"); | 
 | 124 |     else | 
 | 125 |       throw; | 
 | 126 |   } | 
 | 127 |   return; | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 128 |   } else if (req->response_code != 200) { | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 129 |   try { | 
| Roger Meier | 285cfaa | 2011-07-28 17:51:36 +0000 | [diff] [blame] | 130 |     cob_(); | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 131 |   } catch(const TTransportException& e) { | 
 | 132 |     std::stringstream ss; | 
 | 133 |     ss << "server returned code " << req->response_code; | 
 | 134 | 	if(req->response_code_line) | 
 | 135 | 		ss << ": " << req->response_code_line; | 
 | 136 |     if(e.getType() == TTransportException::END_OF_FILE) | 
 | 137 |       throw TException(ss.str()); | 
 | 138 |     else | 
 | 139 |       throw; | 
 | 140 |   } | 
 | 141 |   return; | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 142 |   } | 
 | 143 |   recvBuf_->resetBuffer( | 
 | 144 |       EVBUFFER_DATA(req->input_buffer), | 
 | 145 |       EVBUFFER_LENGTH(req->input_buffer)); | 
| Roger Meier | 285cfaa | 2011-07-28 17:51:36 +0000 | [diff] [blame] | 146 |   cob_(); | 
 | 147 |   return; | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 148 | } | 
 | 149 |  | 
 | 150 |  | 
 | 151 | /* static */ void TEvhttpClientChannel::response(struct evhttp_request* req, void* arg) { | 
 | 152 |   TEvhttpClientChannel* self = (TEvhttpClientChannel*)arg; | 
| Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame^] | 153 |   try { | 
 | 154 |     self->finish(req); | 
 | 155 |   } catch(std::exception& e) { | 
 | 156 |     // don't propagate a C++ exception in C code (e.g. libevent) | 
 | 157 |     std::cerr << "TEvhttpClientChannel::response exception thrown (ignored): " << e.what() << std::endl; | 
 | 158 |   } | 
| David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 159 | } | 
 | 160 |  | 
 | 161 |  | 
 | 162 | }}} // apache::thrift::async |