[Server] Support reporting flush time in psp
[hiphop-php.git] / src / runtime / base / server / libevent_transport.cpp
blob70d5982222bbed2ffcb7c43349d2ecb4b17e9758
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
23 namespace HPHP {
24 ///////////////////////////////////////////////////////////////////////////////
26 // libevent is not exposing this data structure, but we need it.
27 struct m_evkeyvalq {
28 struct evkeyval *tqh_first;
31 LibEventTransport::LibEventTransport(LibEventServer *server,
32 evhttp_request *request,
33 int workerId)
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;
38 ASSERT(buf);
39 int size = EVBUFFER_LENGTH(buf);
40 if (size) {
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;
49 char buf[6];
50 snprintf(buf, 6, "%d.%d", m_request->major, m_request->minor);
51 m_http_version = buf;
54 switch (m_request->type) {
55 case EVHTTP_REQ_GET:
56 m_method = Transport::GET;
57 break;
58 case EVHTTP_REQ_POST:
59 m_method = Transport::POST;
60 break;
61 case EVHTTP_REQ_HEAD:
62 m_method = Transport::HEAD;
63 break;
64 default:
65 ASSERT(false);
66 m_method = Transport::UnknownMethod;
67 break;
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() {
83 return m_url.c_str();
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;
93 ASSERT(buf);
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;
105 return false;
107 return true;
108 #else
109 return false;
110 #endif
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;
120 size = 0;
121 return NULL;
124 evbuffer *buf = m_request->input_buffer;
125 ASSERT(buf);
126 evbuffer_drain(buf, EVBUFFER_LENGTH(buf));
128 if (evhttp_get_more_post_data(m_request, &m_eventBasePostData,
129 &m_moreDataRead)) {
130 buf = m_request->input_buffer;
131 ASSERT(buf);
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);
150 size = 0;
151 return NULL;
152 #else
153 size = 0;
154 return NULL;
155 #endif
158 Transport::Method LibEventTransport::getMethod() {
159 return m_method;
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];
177 return "";
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);
188 ASSERT(value);
189 ASSERT(m_request->output_headers);
191 if (m_sendStarted) {
192 Logger::Error("trying to add header '%s: %s' after 1st chunk",
193 name, value);
194 return;
197 int ret = evhttp_add_header(m_request->output_headers, name, value);
198 if (ret < 0) {
199 throw InvalidHeaderException(name, value);
203 void LibEventTransport::removeHeaderImpl(const char *name) {
204 ASSERT(name && *name);
205 ASSERT(m_request->output_headers);
207 if (m_sendStarted) {
208 Logger::Error("trying to remove header '%s' after 1st chunk", name);
209 return;
212 evhttp_remove_header(m_request->output_headers, name);
215 void LibEventTransport::addRequestHeaderImpl(const char *name,
216 const char *value) {
217 ASSERT(name && *name);
218 ASSERT(value);
219 ASSERT(m_request->input_headers);
221 int ret = evhttp_add_header(m_request->input_headers, name, value);
222 if (ret < 0) {
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,
240 bool chunked) {
241 ASSERT(data);
242 ASSERT(!m_sendEnded);
243 ASSERT(!m_sendStarted || chunked);
245 if (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,
250 !m_sendStarted);
251 } else {
252 if (m_method != HEAD) {
253 evbuffer_add(m_request->output_buffer, data, size);
254 } else {
255 char buf[11];
256 snprintf(buf, sizeof(buf), "%d", size);
257 addHeaderImpl("Content-Length", buf);
259 m_server->onResponse(m_workerId, m_request, code, this);
260 m_sendEnded = true;
262 m_sendStarted = true;
265 void LibEventTransport::onSendEndImpl() {
266 if (m_chunkedEncoding) {
267 m_server->onChunkedResponseEnd(m_workerId, m_request);
268 m_sendEnded = true;
269 } else {
270 ASSERT(m_sendEnded); // otherwise, we didn't call send for this request
274 ///////////////////////////////////////////////////////////////////////////////