2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010 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 +----------------------------------------------------------------------+
17 #include <runtime/base/server/libevent_transport.h>
18 #include <runtime/base/server/libevent_server.h>
19 #include <runtime/base/server/server.h>
20 #include <runtime/base/runtime_option.h>
21 #include <util/util.h>
24 ///////////////////////////////////////////////////////////////////////////////
26 // libevent is not exposing this data structure, but we need it.
28 struct evkeyval
*tqh_first
;
31 LibEventTransport::LibEventTransport(LibEventServer
*server
,
32 evhttp_request
*request
,
34 : m_server(server
), m_request(request
), m_eventBasePostData(NULL
),
35 m_workerId(workerId
), m_sendStarted(false), m_sendEnded(false) {
36 // HttpProtocol::PrepareSystemVariables needs this
37 evbuffer
*buf
= m_request
->input_buffer
;
39 int size
= EVBUFFER_LENGTH(buf
);
41 evbuffer_expand(buf
, size
+ 1); // allowing NULL termination
42 // EVBUFFER_DATA(buf) might change after evbuffer_expand
43 ((char*)EVBUFFER_DATA(buf
))[size
] = '\0';
46 m_remote_host
= m_request
->remote_host
;
50 snprintf(buf
, 6, "%d.%d", m_request
->major
, m_request
->minor
);
54 switch (m_request
->type
) {
56 m_method
= Transport::GET
;
59 m_method
= Transport::POST
;
62 m_method
= Transport::HEAD
;
66 m_method
= Transport::UnknownMethod
;
69 m_extended_method
= m_request
->ext_method
;
71 ASSERT(m_request
->input_headers
);
72 for (evkeyval
*p
= ((m_evkeyvalq
*)m_request
->input_headers
)->tqh_first
; p
;
73 p
= p
->next
.tqe_next
) {
74 if (p
->key
&& p
->value
) {
75 m_requestHeaders
[p
->key
].push_back(p
->value
);
79 m_url
= m_request
->uri
;
82 const char *LibEventTransport::getUrl() {
86 const char *LibEventTransport::getRemoteHost() {
87 return m_remote_host
.c_str();
90 const void *LibEventTransport::getPostData(int &size
) {
91 evbuffer
*buf
= m_request
->input_buffer
;
94 size
= EVBUFFER_LENGTH(buf
);
95 return EVBUFFER_DATA(buf
);
98 bool LibEventTransport::hasMorePostData() {
99 #ifdef EVHTTP_PORTABLE_READ_LIMITING
100 if (m_request
->ntoread
<= 0) {
101 if (m_eventBasePostData
!= NULL
) {
102 event_base_free(m_eventBasePostData
);
103 m_eventBasePostData
= NULL
;
113 const void *LibEventTransport::getMorePostData(int &size
) {
114 #ifdef EVHTTP_PORTABLE_READ_LIMITING
115 if (m_request
->ntoread
== 0) {
116 if (m_eventBasePostData
!= NULL
) {
117 event_base_free(m_eventBasePostData
);
118 m_eventBasePostData
= NULL
;
124 evbuffer
*buf
= m_request
->input_buffer
;
126 evbuffer_drain(buf
, EVBUFFER_LENGTH(buf
));
128 if (evhttp_get_more_post_data(m_request
, &m_eventBasePostData
,
130 buf
= m_request
->input_buffer
;
132 size
= EVBUFFER_LENGTH(buf
);
133 evbuffer_expand(buf
, size
+ 1); // allowing NULL termination
134 // EVBUFFER_DATA(buf) might change after evbuffer_expand
135 ((char*)EVBUFFER_DATA(buf
))[size
] = '\0';
136 if (m_request
->ntoread
== 0) {
137 if (m_eventBasePostData
!= NULL
) {
138 event_base_free(m_eventBasePostData
);
139 m_eventBasePostData
= NULL
;
141 evhttp_get_post_data_done(m_request
);
143 return EVBUFFER_DATA(buf
);
145 if (m_eventBasePostData
!= NULL
) {
146 event_base_free(m_eventBasePostData
);
147 m_eventBasePostData
= NULL
;
149 evhttp_get_post_data_done(m_request
);
158 Transport::Method
LibEventTransport::getMethod() {
162 const char *LibEventTransport::getExtendedMethod() {
163 return m_extended_method
;
166 std::string
LibEventTransport::getHTTPVersion() const {
167 return m_http_version
;
170 std::string
LibEventTransport::getHeader(const char *name
) {
171 ASSERT(name
&& *name
);
173 HeaderMap::const_iterator iter
= m_requestHeaders
.find(name
);
174 if (iter
!= m_requestHeaders
.end()) {
175 return iter
->second
[0];
180 void LibEventTransport::getHeaders(HeaderMap
&headers
) {
181 if (&m_requestHeaders
!= &headers
) {
182 headers
= m_requestHeaders
;
186 void LibEventTransport::addHeaderImpl(const char *name
, const char *value
) {
187 ASSERT(name
&& *name
);
189 ASSERT(m_request
->output_headers
);
192 Logger::Error("trying to add header '%s: %s' after 1st chunk",
197 int ret
= evhttp_add_header(m_request
->output_headers
, name
, value
);
199 throw InvalidHeaderException(name
, value
);
203 void LibEventTransport::removeHeaderImpl(const char *name
) {
204 ASSERT(name
&& *name
);
205 ASSERT(m_request
->output_headers
);
208 Logger::Error("trying to remove header '%s' after 1st chunk", name
);
212 evhttp_remove_header(m_request
->output_headers
, name
);
215 void LibEventTransport::addRequestHeaderImpl(const char *name
,
217 ASSERT(name
&& *name
);
219 ASSERT(m_request
->input_headers
);
221 int ret
= evhttp_add_header(m_request
->input_headers
, name
, value
);
223 throw InvalidHeaderException(name
, value
);
225 m_requestHeaders
[name
].push_back(value
);
228 void LibEventTransport::removeRequestHeaderImpl(const char *name
) {
229 ASSERT(name
&& *name
);
230 ASSERT(m_request
->input_headers
);
231 evhttp_remove_header(m_request
->input_headers
, name
);
232 m_requestHeaders
.erase(name
);
235 bool LibEventTransport::isServerStopping() {
236 return m_server
->getStatus() == Server::STOPPED
;
239 void LibEventTransport::sendImpl(const void *data
, int size
, int code
,
242 ASSERT(!m_sendEnded
);
243 ASSERT(!m_sendStarted
|| chunked
);
246 ASSERT(m_method
!= HEAD
);
247 evbuffer
*chunk
= evbuffer_new();
248 evbuffer_add(chunk
, data
, size
);
249 m_server
->onChunkedResponse(m_workerId
, m_request
, code
, chunk
,
252 if (m_method
!= HEAD
) {
253 evbuffer_add(m_request
->output_buffer
, data
, size
);
256 snprintf(buf
, sizeof(buf
), "%d", size
);
257 addHeaderImpl("Content-Length", buf
);
259 m_server
->onResponse(m_workerId
, m_request
, code
, this);
262 m_sendStarted
= true;
265 void LibEventTransport::onSendEndImpl() {
266 if (m_chunkedEncoding
) {
267 m_server
->onChunkedResponseEnd(m_workerId
, m_request
);
270 ASSERT(m_sendEnded
); // otherwise, we didn't call send for this request
274 ///////////////////////////////////////////////////////////////////////////////