2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/libevent-http-client.h"
20 #include <folly/Conv.h>
22 #include "hphp/runtime/server/server-stats.h"
23 #include "hphp/runtime/base/runtime-option.h"
24 #include "hphp/util/compression.h"
25 #include "hphp/util/logger.h"
26 #include "hphp/util/timer.h"
28 // libevent is not exposing this data structure, but we need it.
30 struct evkeyval
*tqh_first
;
33 ///////////////////////////////////////////////////////////////////////////////
34 // static handlers delegating work to instance ones
36 static void on_request_completed(struct evhttp_request
*req
, void *obj
) {
38 ((HPHP::LibEventHttpClient
*)obj
)->onRequestCompleted(req
);
41 static void on_connection_closed(struct evhttp_connection
*conn
, void *obj
) {
43 ((HPHP::LibEventHttpClient
*)obj
)->onConnectionClosed();
46 static void timer_callback(int fd
, short events
, void *context
) {
47 event_base_loopbreak((struct event_base
*)context
);
51 ///////////////////////////////////////////////////////////////////////////////
54 static std::string
get_hash(const std::string
&address
, int port
) {
55 std::string hash
= address
;
58 hash
+= folly::to
<std::string
>(port
);
63 ReadWriteMutex
LibEventHttpClient::ConnectionPoolMutex
;
64 std::map
<std::string
,int> LibEventHttpClient::ConnectionPoolConfig
;
65 std::map
<std::string
,std::vector
<LibEventHttpClientPtr
>>
66 LibEventHttpClient::ConnectionPool
;
68 void LibEventHttpClient::SetCache(const std::string
&address
, int port
,
70 auto const hash
= get_hash(address
, port
);
72 WriteLock
lock(ConnectionPoolMutex
);
73 if (maxConnection
> 0) {
74 ConnectionPoolConfig
[hash
] = maxConnection
;
76 ConnectionPoolConfig
.erase(hash
);
80 LibEventHttpClientPtr
LibEventHttpClient::Get(const std::string
&address
,
82 auto const hash
= get_hash(address
, port
);
84 int maxConnection
= 0;
86 ReadLock
lock(ConnectionPoolMutex
);
87 auto iter
= ConnectionPoolConfig
.find(hash
);
88 if (iter
== ConnectionPoolConfig
.end()) {
89 // not configured to cache
90 ServerStats::Log("evhttp.skip", 1);
91 ServerStats::Log("evhttp.skip." + hash
, 1);
92 return LibEventHttpClientPtr(new LibEventHttpClient(address
, port
));
94 maxConnection
= iter
->second
;
97 WriteLock
lock(ConnectionPoolMutex
);
98 auto& pool
= ConnectionPool
[hash
];
99 for (unsigned int i
= 0; i
< pool
.size(); i
++) {
100 auto client
= pool
[i
];
101 if (!client
->m_busy
) {
102 client
->m_busy
= true;
103 ServerStats::Log("evhttp.hit", 1);
104 ServerStats::Log("evhttp.hit." + hash
, 1);
109 LibEventHttpClientPtr
ret(new LibEventHttpClient(address
, port
));
110 if ((int)pool
.size() < maxConnection
) {
112 pool
.reserve(maxConnection
);
116 ServerStats::Log("evhttp.miss", 1);
117 ServerStats::Log("evhttp.miss." + hash
, 1);
121 ///////////////////////////////////////////////////////////////////////////////
122 // constructor and destructor
124 LibEventHttpClient::LibEventHttpClient(const std::string
&address
, int port
)
125 : m_busy(true), m_address(address
), m_port(port
), m_requests(0),
126 m_conn(nullptr), m_thread(nullptr), m_code(0), m_response(nullptr), m_len(0) {
127 m_eventBase
= event_base_new();
130 LibEventHttpClient::~LibEventHttpClient() {
133 // reset all per-connection data structures
135 evhttp_connection_free(m_conn
);
138 event_base_free(m_eventBase
);
142 void LibEventHttpClient::clear() {
143 // reset all per-request data structures
145 m_thread
->waitForEnd();
155 m_response
= nullptr;
157 m_responseHeaders
.clear();
160 void LibEventHttpClient::release() {
165 ///////////////////////////////////////////////////////////////////////////////
167 bool LibEventHttpClient::send(const std::string
&url
,
168 const std::vector
<std::string
> &headers
,
169 int timeoutSeconds
, bool async
,
170 const void *data
/* = NULL */,
171 int size
/* = 0 */) {
175 evhttp_request
* request
= evhttp_request_new(on_request_completed
, this);
178 bool keepalive
= true;
180 for (unsigned int i
= 0; i
< headers
.size(); i
++) {
181 const std::string
&header
= headers
[i
];
182 size_t pos
= header
.find(':');
183 if (pos
!= std::string::npos
&& header
[pos
+ 1] == ' ') {
184 std::string name
= header
.substr(0, pos
);
185 if (strcasecmp(name
.c_str(), "Connection") == 0) {
187 } else if (strcasecmp(name
.c_str(), "Host") == 0) {
190 int ret
= evhttp_add_header(request
->output_headers
,
191 name
.c_str(), header
.c_str() + pos
+ 2);
196 Logger::Error("invalid request header: [%s]", header
.c_str());
199 evhttp_add_header(request
->output_headers
, "Connection", "keep-alive");
202 // REVIEW: libevent never sends a Host header (nor does it properly send
203 // HTTP 400 for HTTP/1.1 requests without such a header), in blatant
204 // violation of RFC2616; this should perhaps be fixed in the library
205 // proper. For now, add it if it wasn't set by the caller.
207 evhttp_add_header(request
->output_headers
, "Host", m_address
.c_str());
209 std::ostringstream ss
;
210 ss
<< m_address
<< ":" << m_port
;
211 evhttp_add_header(request
->output_headers
, "Host", ss
.str().c_str());
217 evbuffer_add(request
->output_buffer
, data
, size
);
221 evhttp_cmd_type cmd
= data
? EVHTTP_REQ_POST
: EVHTTP_REQ_GET
;
223 // if we have a cached connection, we need to pump the event loop to clear
224 // any "connection closed" events that may be sitting there patiently.
226 event_base_loop(m_eventBase
, EVLOOP_NONBLOCK
);
229 // even if we had an m_conn immediately above, it may have been cleared out
230 // by onConnectionClosed().
231 if (m_conn
== nullptr) {
232 m_conn
= evhttp_connection_new(m_address
.c_str(), m_port
);
233 evhttp_connection_set_closecb(m_conn
, on_connection_closed
, this);
234 evhttp_connection_set_base(m_conn
, m_eventBase
);
237 int ret
= evhttp_make_request(m_conn
, request
, cmd
, url
.c_str());
239 Logger::Error("evhttp_make_request failed");
243 if (timeoutSeconds
> 0) {
244 struct timeval timeout
;
245 timeout
.tv_sec
= timeoutSeconds
;
248 event_set(&m_eventTimeout
, -1, 0, timer_callback
, m_eventBase
);
249 event_base_set(m_eventBase
, &m_eventTimeout
);
250 event_add(&m_eventTimeout
, &timeout
);
254 m_thread
= new AsyncFunc
<LibEventHttpClient
>
255 (this, &LibEventHttpClient::sendImpl
);
258 IOStatusHelper
io("libevent_http", m_address
.c_str(), m_port
);
264 void LibEventHttpClient::sendImpl() {
265 SlowTimer
timer(RuntimeOption::HttpSlowQueryThreshold
, "evhttp",
267 event_base_dispatch(m_eventBase
);
268 event_del(&m_eventTimeout
);
271 void LibEventHttpClient::onRequestCompleted(evhttp_request
* request
) {
273 // eek -- this is just a clean-up notification because the connection's
274 // been closed, but we already dealt with it in onConnectionClosed
278 // response code line
279 m_code
= request
->response_code
;
280 if (request
->response_code_line
) {
281 m_codeLine
= request
->response_code_line
;
286 for (evkeyval
*p
= ((evkeyvalq_
*)request
->input_headers
)->tqh_first
; p
;
287 p
= p
->next
.tqe_next
) {
288 if (p
->key
&& p
->value
) {
289 if (strcasecmp(p
->key
, "Content-Encoding") == 0 &&
290 strncmp(p
->value
, "gzip", 4) == 0 &&
291 (!p
->value
[4] || isspace(p
->value
[4]))) {
292 // in the (illegal) case of multiple Content-Encoding headers, any one
293 // with the value 'gzip' means we treat it as gzip.
296 m_responseHeaders
.push_back(std::string(p
->key
) + ": " + p
->value
);
301 m_len
= EVBUFFER_LENGTH(request
->input_buffer
);
304 gzdecode((const char*)EVBUFFER_DATA(request
->input_buffer
), m_len
);
306 m_response
= (char*)malloc(m_len
+ 1);
307 strncpy(m_response
, (char*)EVBUFFER_DATA(request
->input_buffer
), m_len
);
308 m_response
[m_len
] = '\0';
312 event_base_loopbreak(m_eventBase
);
315 void LibEventHttpClient::onConnectionClosed() {
320 char *LibEventHttpClient::recv(int &len
) {
322 m_thread
->waitForEnd();
327 char *ret
= m_response
;
329 m_response
= nullptr;
334 ///////////////////////////////////////////////////////////////////////////////