Arquivos
hhvm/hphp/runtime/base/server/libevent_transport.cpp
T
Sean Cannella 3d0c614b9a convert enums to enum classes, part 3
C++11 cleanup (clean up easy enums)

This is for runtime/base/... and ended up touching a lot of files
because it turns out we have a lot of reasonably behaved enums.
2013-06-25 13:19:06 -07:00

306 linhas
8.8 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/base/server/libevent_transport.h"
#include "hphp/runtime/base/server/libevent_server.h"
#include "hphp/runtime/base/server/server.h"
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/util/util.h"
#include "hphp/util/logger.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// libevent is not exposing this data structure, but we need it.
struct m_evkeyvalq {
struct evkeyval *tqh_first;
};
LibEventTransport::LibEventTransport(LibEventServer *server,
evhttp_request *request,
int workerId)
: m_server(server), m_request(request), m_eventBasePostData(nullptr),
m_workerId(workerId), m_sendStarted(false), m_sendEnded(false) {
// HttpProtocol::PrepareSystemVariables needs this
evbuffer *buf = m_request->input_buffer;
assert(buf);
m_requestSize = EVBUFFER_LENGTH(buf);
if (m_requestSize) {
evbuffer_expand(buf, m_requestSize + 1); // allowing NULL termination
// EVBUFFER_DATA(buf) might change after evbuffer_expand
((char*)EVBUFFER_DATA(buf))[m_requestSize] = '\0';
}
m_remote_host = m_request->remote_host;
m_remote_port = m_request->remote_port;
{
char buf[6];
snprintf(buf, 6, "%d.%d", m_request->major, m_request->minor);
m_http_version = buf;
}
switch (m_request->type) {
case EVHTTP_REQ_GET:
m_method = Transport::Method::GET;
m_requestSize += 3;
break;
case EVHTTP_REQ_POST:
m_method = Transport::Method::POST;
m_requestSize += 4;
break;
case EVHTTP_REQ_HEAD:
m_method = Transport::Method::HEAD;
m_requestSize += 4;
break;
default:
assert(false);
m_method = Transport::Method::Unknown;
break;
}
m_extended_method = m_request->ext_method;
assert(m_request->input_headers);
for (evkeyval *p = ((m_evkeyvalq*)m_request->input_headers)->tqh_first; p;
p = p->next.tqe_next) {
if (p->key && p->value) {
m_requestHeaders[p->key].push_back(p->value);
//key, value, ": " and CR/LF
m_requestSize += strlen(p->key) + strlen(p->value) + 4;
}
}
m_url = m_request->uri;
m_requestSize += m_url.size();
m_requestSize += m_http_version.size(); //version number in "HTTP/x.y"
m_requestSize += 11; // HTTP/=5, 2 spaces for url, and CR/LF x2 (first+last)
}
const char *LibEventTransport::getUrl() {
return m_url.c_str();
}
const char *LibEventTransport::getRemoteHost() {
return m_remote_host.c_str();
}
uint16_t LibEventTransport::getRemotePort() {
return m_remote_port;
}
const void *LibEventTransport::getPostData(int &size) {
if (m_sendEnded) {
size = 0;
return 0;
}
evbuffer *buf = m_request->input_buffer;
assert(buf);
size = EVBUFFER_LENGTH(buf);
return EVBUFFER_DATA(buf);
}
bool LibEventTransport::hasMorePostData() {
#ifdef EVHTTP_PORTABLE_READ_LIMITING
if (m_request->ntoread <= 0) {
if (m_eventBasePostData != nullptr) {
event_base_free(m_eventBasePostData);
m_eventBasePostData = nullptr;
}
return false;
}
return true;
#else
return false;
#endif
}
const void *LibEventTransport::getMorePostData(int &size) {
#ifdef EVHTTP_PORTABLE_READ_LIMITING
if (m_request->ntoread == 0) {
if (m_eventBasePostData != nullptr) {
event_base_free(m_eventBasePostData);
m_eventBasePostData = nullptr;
}
size = 0;
return nullptr;
}
evbuffer *buf = m_request->input_buffer;
assert(buf);
evbuffer_drain(buf, EVBUFFER_LENGTH(buf));
if (evhttp_get_more_post_data(m_request, &m_eventBasePostData,
&m_moreDataRead)) {
buf = m_request->input_buffer;
assert(buf);
size = EVBUFFER_LENGTH(buf);
evbuffer_expand(buf, size + 1); // allowing NULL termination
// EVBUFFER_DATA(buf) might change after evbuffer_expand
((char*)EVBUFFER_DATA(buf))[size] = '\0';
if (m_request->ntoread == 0) {
if (m_eventBasePostData != nullptr) {
event_base_free(m_eventBasePostData);
m_eventBasePostData = nullptr;
}
evhttp_get_post_data_done(m_request);
}
return EVBUFFER_DATA(buf);
}
if (m_eventBasePostData != nullptr) {
event_base_free(m_eventBasePostData);
m_eventBasePostData = nullptr;
}
evhttp_get_post_data_done(m_request);
size = 0;
return nullptr;
#else
size = 0;
return nullptr;
#endif
}
Transport::Method LibEventTransport::getMethod() {
return m_method;
}
const char *LibEventTransport::getExtendedMethod() {
return m_extended_method;
}
std::string LibEventTransport::getHTTPVersion() const {
return m_http_version;
}
int LibEventTransport::getRequestSize() const {
return m_requestSize;
}
std::string LibEventTransport::getHeader(const char *name) {
assert(name && *name);
HeaderMap::const_iterator iter = m_requestHeaders.find(name);
if (iter != m_requestHeaders.end()) {
return iter->second[0];
}
return "";
}
void LibEventTransport::getHeaders(HeaderMap &headers) {
if (&m_requestHeaders != &headers) {
headers = m_requestHeaders;
}
}
void LibEventTransport::addHeaderImpl(const char *name, const char *value) {
assert(name && *name);
assert(value);
assert(m_request->output_headers);
if (m_sendStarted) {
Logger::Error("trying to add header '%s: %s' after 1st chunk",
name, value);
return;
}
int ret = evhttp_add_header(m_request->output_headers, name, value);
if (ret < 0) {
Logger::Error("failed to add header '%s: %s'", name, value);
}
}
void LibEventTransport::removeHeaderImpl(const char *name) {
assert(name && *name);
assert(m_request->output_headers);
if (m_sendStarted) {
Logger::Error("trying to remove header '%s' after 1st chunk", name);
return;
}
evhttp_remove_header(m_request->output_headers, name);
}
void LibEventTransport::addRequestHeaderImpl(const char *name,
const char *value) {
assert(name && *name);
assert(value);
assert(m_request->input_headers);
int ret = evhttp_add_header(m_request->input_headers, name, value);
if (ret < 0) {
Logger::Error("failed to add header '%s: %s'", name, value);
return;
}
m_requestHeaders[name].push_back(value);
}
void LibEventTransport::removeRequestHeaderImpl(const char *name) {
assert(name && *name);
assert(m_request->input_headers);
evhttp_remove_header(m_request->input_headers, name);
m_requestHeaders.erase(name);
}
bool LibEventTransport::isServerStopping() {
return m_server->getStatus() == Server::RunStatus::STOPPED;
}
void LibEventTransport::sendImpl(const void *data, int size, int code,
bool chunked) {
assert(data);
assert(!m_sendEnded);
assert(!m_sendStarted || chunked);
if (chunked) {
assert(m_method != Method::HEAD);
evbuffer *chunk = evbuffer_new();
evbuffer_add(chunk, data, size);
/*
* Chunked replies are sent async, so there is no way to know the
* time it took to flush the response, but tracking the bytes sent is
* very useful.
*/
onChunkedProgress(size);
m_server->onChunkedResponse(m_workerId, m_request, code, chunk,
!m_sendStarted);
} else {
if (m_method != Method::HEAD) {
evbuffer_add(m_request->output_buffer, data, size);
} else if (!evhttp_find_header(m_request->output_headers,
"Content-Length")) {
char buf[11];
snprintf(buf, sizeof(buf), "%d", size);
addHeaderImpl("Content-Length", buf);
}
m_server->onResponse(m_workerId, m_request, code, this);
m_sendEnded = true;
}
m_sendStarted = true;
}
void LibEventTransport::onSendEndImpl() {
if (m_chunkedEncoding) {
m_server->onChunkedResponseEnd(m_workerId, m_request);
m_sendEnded = true;
} else {
assert(m_sendEnded); // otherwise, we didn't call send for this request
}
}
///////////////////////////////////////////////////////////////////////////////
}