blob: 6af8104b6498e11cd95b3f62434b12cbc901d25a [file] [log] [blame]
David Reiss5ddabb82010-10-06 17:09:37 +00001/*
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
Roger Meier4285ba22013-06-10 21:17:23 +020020#include <thrift/async/TEvhttpClientChannel.h>
David Reiss5ddabb82010-10-06 17:09:37 +000021#include <evhttp.h>
Ben Craig7207c222015-07-06 08:40:35 -050022#include <event2/buffer.h>
23#include <event2/buffer_compat.h>
Roger Meier49ff8b12012-04-13 09:12:31 +000024#include <thrift/transport/TBufferTransports.h>
25#include <thrift/protocol/TProtocolException.h>
Roger Meier08077bf2011-09-11 07:28:54 +000026
27#include <iostream>
28#include <sstream>
29
30using namespace apache::thrift::protocol;
31using apache::thrift::transport::TTransportException;
David Reiss5ddabb82010-10-06 17:09:37 +000032
Konrad Grochowski16a23a62014-11-13 15:33:38 +010033namespace apache {
34namespace thrift {
35namespace async {
David Reiss5ddabb82010-10-06 17:09:37 +000036
Konrad Grochowski16a23a62014-11-13 15:33:38 +010037TEvhttpClientChannel::TEvhttpClientChannel(const std::string& host,
38 const std::string& path,
39 const char* address,
40 int port,
zhouweikangc4af6332017-12-06 15:24:42 +080041 struct event_base* eb,
42 struct evdns_base* dnsbase)
43
Sebastian Zenker39e505c2015-12-18 16:15:08 +010044 : host_(host), path_(path), conn_(NULL) {
zhouweikangc4af6332017-12-06 15:24:42 +080045 conn_ = evhttp_connection_base_new(eb, dnsbase, address, port);
David Reiss5ddabb82010-10-06 17:09:37 +000046 if (conn_ == NULL) {
Roger Meier08077bf2011-09-11 07:28:54 +000047 throw TException("evhttp_connection_new failed");
David Reiss5ddabb82010-10-06 17:09:37 +000048 }
David Reiss5ddabb82010-10-06 17:09:37 +000049}
50
David Reiss5ddabb82010-10-06 17:09:37 +000051TEvhttpClientChannel::~TEvhttpClientChannel() {
52 if (conn_ != NULL) {
53 evhttp_connection_free(conn_);
54 }
55}
56
Konrad Grochowski16a23a62014-11-13 15:33:38 +010057void TEvhttpClientChannel::sendAndRecvMessage(const VoidCallback& cob,
58 apache::thrift::transport::TMemoryBuffer* sendBuf,
59 apache::thrift::transport::TMemoryBuffer* recvBuf) {
David Reiss5ddabb82010-10-06 17:09:37 +000060 struct evhttp_request* req = evhttp_request_new(response, this);
61 if (req == NULL) {
Roger Meier08077bf2011-09-11 07:28:54 +000062 throw TException("evhttp_request_new failed");
David Reiss5ddabb82010-10-06 17:09:37 +000063 }
64
65 int rv;
66
67 rv = evhttp_add_header(req->output_headers, "Host", host_.c_str());
68 if (rv != 0) {
Roger Meier08077bf2011-09-11 07:28:54 +000069 throw TException("evhttp_add_header failed");
David Reiss5ddabb82010-10-06 17:09:37 +000070 }
71
72 rv = evhttp_add_header(req->output_headers, "Content-Type", "application/x-thrift");
73 if (rv != 0) {
Roger Meier08077bf2011-09-11 07:28:54 +000074 throw TException("evhttp_add_header failed");
David Reiss5ddabb82010-10-06 17:09:37 +000075 }
76
77 uint8_t* obuf;
78 uint32_t sz;
79 sendBuf->getBuffer(&obuf, &sz);
80 rv = evbuffer_add(req->output_buffer, obuf, sz);
81 if (rv != 0) {
Roger Meier08077bf2011-09-11 07:28:54 +000082 throw TException("evbuffer_add failed");
David Reiss5ddabb82010-10-06 17:09:37 +000083 }
84
85 rv = evhttp_make_request(conn_, req, EVHTTP_REQ_POST, path_.c_str());
86 if (rv != 0) {
Roger Meier08077bf2011-09-11 07:28:54 +000087 throw TException("evhttp_make_request failed");
David Reiss5ddabb82010-10-06 17:09:37 +000088 }
Sebastian Zenker39e505c2015-12-18 16:15:08 +010089
90 completionQueue_.push(Completion(cob, recvBuf));
David Reiss5ddabb82010-10-06 17:09:37 +000091}
92
Konrad Grochowski16a23a62014-11-13 15:33:38 +010093void TEvhttpClientChannel::sendMessage(const VoidCallback& cob,
94 apache::thrift::transport::TMemoryBuffer* message) {
95 (void)cob;
96 (void)message;
Roger Meier08077bf2011-09-11 07:28:54 +000097 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
Konrad Grochowski16a23a62014-11-13 15:33:38 +010098 "Unexpected call to TEvhttpClientChannel::sendMessage");
David Reiss5ddabb82010-10-06 17:09:37 +000099}
100
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100101void TEvhttpClientChannel::recvMessage(const VoidCallback& cob,
102 apache::thrift::transport::TMemoryBuffer* message) {
103 (void)cob;
104 (void)message;
Roger Meier08077bf2011-09-11 07:28:54 +0000105 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100106 "Unexpected call to TEvhttpClientChannel::recvMessage");
David Reiss5ddabb82010-10-06 17:09:37 +0000107}
108
David Reiss5ddabb82010-10-06 17:09:37 +0000109void TEvhttpClientChannel::finish(struct evhttp_request* req) {
Sebastian Zenker39e505c2015-12-18 16:15:08 +0100110 assert(!completionQueue_.empty());
111 Completion completion = completionQueue_.front();
112 completionQueue_.pop();
David Reiss5ddabb82010-10-06 17:09:37 +0000113 if (req == NULL) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100114 try {
Sebastian Zenker39e505c2015-12-18 16:15:08 +0100115 completion.first();
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100116 } catch (const TTransportException& e) {
117 if (e.getType() == TTransportException::END_OF_FILE)
118 throw TException("connect failed");
119 else
120 throw;
121 }
122 return;
Konrad Grochowski240120c2014-11-18 11:33:31 +0100123 } else if (req->response_code != 200) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100124 try {
Sebastian Zenker39e505c2015-12-18 16:15:08 +0100125 completion.first();
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100126 } catch (const TTransportException& e) {
127 std::stringstream ss;
128 ss << "server returned code " << req->response_code;
129 if (req->response_code_line)
130 ss << ": " << req->response_code_line;
131 if (e.getType() == TTransportException::END_OF_FILE)
132 throw TException(ss.str());
133 else
134 throw;
135 }
136 return;
Konrad Grochowski240120c2014-11-18 11:33:31 +0100137 }
Sebastian Zenker39e505c2015-12-18 16:15:08 +0100138 completion.second->resetBuffer(EVBUFFER_DATA(req->input_buffer),
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100139 static_cast<uint32_t>(EVBUFFER_LENGTH(req->input_buffer)));
Sebastian Zenker39e505c2015-12-18 16:15:08 +0100140 completion.first();
Roger Meier285cfaa2011-07-28 17:51:36 +0000141 return;
David Reiss5ddabb82010-10-06 17:09:37 +0000142}
143
David Reiss5ddabb82010-10-06 17:09:37 +0000144/* static */ void TEvhttpClientChannel::response(struct evhttp_request* req, void* arg) {
145 TEvhttpClientChannel* self = (TEvhttpClientChannel*)arg;
Roger Meier08077bf2011-09-11 07:28:54 +0000146 try {
147 self->finish(req);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100148 } catch (std::exception& e) {
Roger Meier08077bf2011-09-11 07:28:54 +0000149 // don't propagate a C++ exception in C code (e.g. libevent)
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100150 std::cerr << "TEvhttpClientChannel::response exception thrown (ignored): " << e.what()
151 << std::endl;
Roger Meier08077bf2011-09-11 07:28:54 +0000152 }
David Reiss5ddabb82010-10-06 17:09:37 +0000153}
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100154}
155}
156} // apache::thrift::async