ext/common/ServerKit/HttpServer.h in passenger-5.0.9 vs ext/common/ServerKit/HttpServer.h in passenger-5.0.10

- old
+ new

@@ -655,225 +655,10 @@ req->bodyChannel.feedError(req->bodyError); } } protected: - /***** Protected API *****/ - - /** Increase request reference count. */ - void refRequest(Request *req, const char *file, unsigned int line) { - int oldRefcount = req->refcount.fetch_add(1, boost::memory_order_relaxed); - SKC_TRACE_WITH_POS(static_cast<Client *>(req->client), 3, file, line, - "Request refcount increased; it is now " << (oldRefcount + 1)); - } - - /** Decrease request reference count. Adds request to the - * freelist if reference count drops to 0. - */ - void unrefRequest(Request *req, const char *file, unsigned int line) { - int oldRefcount = req->refcount.fetch_sub(1, boost::memory_order_release); - assert(oldRefcount >= 1); - - SKC_TRACE_WITH_POS(static_cast<Client *>(req->client), 3, file, line, - "Request refcount decreased; it is now " << (oldRefcount - 1)); - if (oldRefcount == 1) { - boost::atomic_thread_fence(boost::memory_order_acquire); - - if (this->getContext()->libev->onEventLoopThread()) { - requestReachedZeroRefcount(req); - } else { - // Let the event loop handle the request reaching the 0 refcount. - passRequestToEventLoopThread(req); - } - } - } - - object_pool<HttpHeaderParserState> &getHeaderParserStatePool() { - return headerParserStatePool; - } - - bool canKeepAlive(Request *req) const { - return req->wantKeepAlive - && req->bodyFullyRead() - && HttpServer::serverState < HttpServer::SHUTTING_DOWN; - } - - void writeResponse(Client *client, const MemoryKit::mbuf &buffer) { - client->currentRequest->responseBegun = true; - client->output.feedWithoutRefGuard(buffer); - } - - void writeResponse(Client *client, const char *data, unsigned int size) { - writeResponse(client, MemoryKit::mbuf(data, size)); - } - - void writeResponse(Client *client, const StaticString &data) { - writeResponse(client, data.data(), data.size()); - } - - void - writeSimpleResponse(Client *client, int code, const HeaderTable *headers, - const StaticString &body) - { - unsigned int headerBufSize = 300; - - if (headers != NULL) { - HeaderTable::ConstIterator it(*headers); - while (*it != NULL) { - headerBufSize += it->header->key.size + sizeof(": ") - 1; - headerBufSize += it->header->val.size + sizeof("\r\n") - 1; - it.next(); - } - } - - Request *req = client->currentRequest; - char *header = (char *) psg_pnalloc(req->pool, headerBufSize); - char statusBuffer[50]; - char *pos = header; - const char *end = header + headerBufSize; - const char *status; - const LString *value; - - status = getStatusCodeAndReasonPhrase(code); - if (status == NULL) { - snprintf(statusBuffer, sizeof(statusBuffer), "%d Unknown Reason-Phrase", code); - status = statusBuffer; - } - - pos += snprintf(pos, end - pos, - "HTTP/%d.%d %s\r\n" - "Status: %s\r\n", - (int) req->httpMajor, (int) req->httpMinor, status, status); - - value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("content-type")) : NULL; - if (value == NULL) { - pos = appendData(pos, end, P_STATIC_STRING("Content-Type: text/html; charset=UTF-8\r\n")); - } else { - pos = appendData(pos, end, P_STATIC_STRING("Content-Type: ")); - pos = appendLStringData(pos, end, value); - pos = appendData(pos, end, P_STATIC_STRING("\r\n")); - } - - value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("date")) : NULL; - pos = appendData(pos, end, P_STATIC_STRING("Date: ")); - if (value == NULL) { - time_t the_time = time(NULL); - struct tm the_tm; - gmtime_r(&the_time, &the_tm); - pos += strftime(pos, end - pos, "%a, %d %b %Y %H:%M:%S %z", &the_tm); - } else { - pos = appendLStringData(pos, end, value); - } - pos = appendData(pos, end, P_STATIC_STRING("\r\n")); - - value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("connection")) : NULL; - if (value == NULL) { - if (canKeepAlive(req)) { - pos = appendData(pos, end, P_STATIC_STRING("Connection: keep-alive\r\n")); - } else { - pos = appendData(pos, end, P_STATIC_STRING("Connection: close\r\n")); - } - } else { - pos = appendData(pos, end, P_STATIC_STRING("Connection: ")); - pos = appendLStringData(pos, end, value); - pos = appendData(pos, end, P_STATIC_STRING("\r\n")); - if (!psg_lstr_cmp(value, P_STATIC_STRING("Keep-Alive")) - && !psg_lstr_cmp(value, P_STATIC_STRING("keep-alive"))) - { - req->wantKeepAlive = false; - } - } - - value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("content-length")) : NULL; - pos = appendData(pos, end, P_STATIC_STRING("Content-Length: ")); - if (value == NULL) { - pos += snprintf(pos, end - pos, "%u", (unsigned int) body.size()); - } else { - pos = appendLStringData(pos, end, value); - } - pos = appendData(pos, end, P_STATIC_STRING("\r\n")); - - if (headers != NULL) { - HeaderTable::ConstIterator it(*headers); - while (*it != NULL) { - if (!psg_lstr_cmp(&it->header->key, P_STATIC_STRING("content-type")) - && !psg_lstr_cmp(&it->header->key, P_STATIC_STRING("date")) - && !psg_lstr_cmp(&it->header->key, P_STATIC_STRING("connection")) - && !psg_lstr_cmp(&it->header->key, P_STATIC_STRING("content-length"))) - { - pos = appendLStringData(pos, end, &it->header->key); - pos = appendData(pos, end, P_STATIC_STRING(": ")); - pos = appendLStringData(pos, end, &it->header->val); - pos = appendData(pos, end, P_STATIC_STRING("\r\n")); - } - it.next(); - } - } - - pos = appendData(pos, end, P_STATIC_STRING("\r\n")); - - writeResponse(client, header, pos - header); - if (!req->ended() && req->method != HTTP_HEAD) { - writeResponse(client, body.data(), body.size()); - } - } - - bool endRequest(Client **client, Request **request) { - Client *c = *client; - Request *req = *request; - psg_pool_t *pool; - - *client = NULL; - *request = NULL; - - if (req->ended()) { - return false; - } - - SKC_TRACE(c, 2, "Ending request"); - assert(c->currentRequest == req); - - if (OXT_UNLIKELY(!req->responseBegun)) { - writeDefault500Response(c, req); - if (req->ended()) { - return false; - } - } - - // The memory buffers that we're writing out during the - // FLUSHING_OUTPUT state might live in the palloc pool, - // so we want to deinitialize the request while preserving - // the pool. We'll destroy the pool when the output is - // flushed. - pool = req->pool; - req->pool = NULL; - deinitializeRequestAndAddToFreelist(c, req); - req->pool = pool; - - if (!c->output.ended()) { - c->output.feedWithoutRefGuard(MemoryKit::mbuf()); - } - if (c->output.endAcked()) { - doneWithCurrentRequest(&c); - } else { - // Call doneWithCurrentRequest() when data flushed - SKC_TRACE(c, 2, "Waiting until output is flushed"); - req->httpState = Request::FLUSHING_OUTPUT; - // If the request body is not fully read at this time, - // then ensure that onClientDataReceived() discards any - // request body data that we receive from now on. - req->wantKeepAlive = canKeepAlive(req); - } - - return true; - } - - void endAsBadRequest(Client **client, Request **req, const StaticString &body) { - endWithErrorResponse(client, req, 400, body); - } - - /***** Hook overrides *****/ virtual void onClientObjectCreated(Client *client) { ParentClass::onClientObjectCreated(client); client->output.setDataFlushedCallback(_onClientOutputDataFlushed); @@ -1077,10 +862,13 @@ headerParserStatePool(16, 256) { STAILQ_INIT(&freeRequests); } + + /***** Server management *****/ + virtual void compact(int logLevel = LVL_NOTICE) { ParentClass::compact(); unsigned int count = freeRequestCount; while (!STAILQ_EMPTY(&freeRequests)) { @@ -1098,10 +886,224 @@ SKS_LOG(logLevel, __FILE__, __LINE__, "Freed " << count << " spare request objects"); } + + /***** Request manipulation *****/ + + /** Increase request reference count. */ + void refRequest(Request *req, const char *file, unsigned int line) { + int oldRefcount = req->refcount.fetch_add(1, boost::memory_order_relaxed); + SKC_TRACE_WITH_POS(static_cast<Client *>(req->client), 3, file, line, + "Request refcount increased; it is now " << (oldRefcount + 1)); + } + + /** Decrease request reference count. Adds request to the + * freelist if reference count drops to 0. + */ + void unrefRequest(Request *req, const char *file, unsigned int line) { + int oldRefcount = req->refcount.fetch_sub(1, boost::memory_order_release); + assert(oldRefcount >= 1); + + SKC_TRACE_WITH_POS(static_cast<Client *>(req->client), 3, file, line, + "Request refcount decreased; it is now " << (oldRefcount - 1)); + if (oldRefcount == 1) { + boost::atomic_thread_fence(boost::memory_order_acquire); + + if (this->getContext()->libev->onEventLoopThread()) { + requestReachedZeroRefcount(req); + } else { + // Let the event loop handle the request reaching the 0 refcount. + passRequestToEventLoopThread(req); + } + } + } + + bool canKeepAlive(Request *req) const { + return req->wantKeepAlive + && req->bodyFullyRead() + && HttpServer::serverState < HttpServer::SHUTTING_DOWN; + } + + void writeResponse(Client *client, const MemoryKit::mbuf &buffer) { + client->currentRequest->responseBegun = true; + client->output.feedWithoutRefGuard(buffer); + } + + void writeResponse(Client *client, const char *data, unsigned int size) { + writeResponse(client, MemoryKit::mbuf(data, size)); + } + + void writeResponse(Client *client, const StaticString &data) { + writeResponse(client, data.data(), data.size()); + } + + void + writeSimpleResponse(Client *client, int code, const HeaderTable *headers, + const StaticString &body) + { + unsigned int headerBufSize = 300; + + if (headers != NULL) { + HeaderTable::ConstIterator it(*headers); + while (*it != NULL) { + headerBufSize += it->header->key.size + sizeof(": ") - 1; + headerBufSize += it->header->val.size + sizeof("\r\n") - 1; + it.next(); + } + } + + Request *req = client->currentRequest; + char *header = (char *) psg_pnalloc(req->pool, headerBufSize); + char statusBuffer[50]; + char *pos = header; + const char *end = header + headerBufSize; + const char *status; + const LString *value; + + status = getStatusCodeAndReasonPhrase(code); + if (status == NULL) { + snprintf(statusBuffer, sizeof(statusBuffer), "%d Unknown Reason-Phrase", code); + status = statusBuffer; + } + + pos += snprintf(pos, end - pos, + "HTTP/%d.%d %s\r\n" + "Status: %s\r\n", + (int) req->httpMajor, (int) req->httpMinor, status, status); + + value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("content-type")) : NULL; + if (value == NULL) { + pos = appendData(pos, end, P_STATIC_STRING("Content-Type: text/html; charset=UTF-8\r\n")); + } else { + pos = appendData(pos, end, P_STATIC_STRING("Content-Type: ")); + pos = appendLStringData(pos, end, value); + pos = appendData(pos, end, P_STATIC_STRING("\r\n")); + } + + value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("date")) : NULL; + pos = appendData(pos, end, P_STATIC_STRING("Date: ")); + if (value == NULL) { + time_t the_time = time(NULL); + struct tm the_tm; + gmtime_r(&the_time, &the_tm); + pos += strftime(pos, end - pos, "%a, %d %b %Y %H:%M:%S %z", &the_tm); + } else { + pos = appendLStringData(pos, end, value); + } + pos = appendData(pos, end, P_STATIC_STRING("\r\n")); + + value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("connection")) : NULL; + if (value == NULL) { + if (canKeepAlive(req)) { + pos = appendData(pos, end, P_STATIC_STRING("Connection: keep-alive\r\n")); + } else { + pos = appendData(pos, end, P_STATIC_STRING("Connection: close\r\n")); + } + } else { + pos = appendData(pos, end, P_STATIC_STRING("Connection: ")); + pos = appendLStringData(pos, end, value); + pos = appendData(pos, end, P_STATIC_STRING("\r\n")); + if (!psg_lstr_cmp(value, P_STATIC_STRING("Keep-Alive")) + && !psg_lstr_cmp(value, P_STATIC_STRING("keep-alive"))) + { + req->wantKeepAlive = false; + } + } + + value = (headers != NULL) ? headers->lookup(P_STATIC_STRING("content-length")) : NULL; + pos = appendData(pos, end, P_STATIC_STRING("Content-Length: ")); + if (value == NULL) { + pos += snprintf(pos, end - pos, "%u", (unsigned int) body.size()); + } else { + pos = appendLStringData(pos, end, value); + } + pos = appendData(pos, end, P_STATIC_STRING("\r\n")); + + if (headers != NULL) { + HeaderTable::ConstIterator it(*headers); + while (*it != NULL) { + if (!psg_lstr_cmp(&it->header->key, P_STATIC_STRING("content-type")) + && !psg_lstr_cmp(&it->header->key, P_STATIC_STRING("date")) + && !psg_lstr_cmp(&it->header->key, P_STATIC_STRING("connection")) + && !psg_lstr_cmp(&it->header->key, P_STATIC_STRING("content-length"))) + { + pos = appendLStringData(pos, end, &it->header->origKey); + pos = appendData(pos, end, P_STATIC_STRING(": ")); + pos = appendLStringData(pos, end, &it->header->val); + pos = appendData(pos, end, P_STATIC_STRING("\r\n")); + } + it.next(); + } + } + + pos = appendData(pos, end, P_STATIC_STRING("\r\n")); + + writeResponse(client, header, pos - header); + if (!req->ended() && req->method != HTTP_HEAD) { + writeResponse(client, body.data(), body.size()); + } + } + + bool endRequest(Client **client, Request **request) { + Client *c = *client; + Request *req = *request; + psg_pool_t *pool; + + *client = NULL; + *request = NULL; + + if (req->ended()) { + return false; + } + + SKC_TRACE(c, 2, "Ending request"); + assert(c->currentRequest == req); + + if (OXT_UNLIKELY(!req->responseBegun)) { + writeDefault500Response(c, req); + if (req->ended()) { + return false; + } + } + + // The memory buffers that we're writing out during the + // FLUSHING_OUTPUT state might live in the palloc pool, + // so we want to deinitialize the request while preserving + // the pool. We'll destroy the pool when the output is + // flushed. + pool = req->pool; + req->pool = NULL; + deinitializeRequestAndAddToFreelist(c, req); + req->pool = pool; + + if (!c->output.ended()) { + c->output.feedWithoutRefGuard(MemoryKit::mbuf()); + } + if (c->output.endAcked()) { + doneWithCurrentRequest(&c); + } else { + // Call doneWithCurrentRequest() when data flushed + SKC_TRACE(c, 2, "Waiting until output is flushed"); + req->httpState = Request::FLUSHING_OUTPUT; + // If the request body is not fully read at this time, + // then ensure that onClientDataReceived() discards any + // request body data that we receive from now on. + req->wantKeepAlive = canKeepAlive(req); + } + + return true; + } + + void endAsBadRequest(Client **client, Request **req, const StaticString &body) { + endWithErrorResponse(client, req, 400, body); + } + + + /***** Configuration and introspection *****/ + virtual void configure(const Json::Value &doc) { ParentClass::configure(doc); if (doc.isMember("request_freelist_limit")) { requestFreelistLimit = doc["request_freelist_limit"].asUInt(); } @@ -1181,9 +1183,17 @@ } } return doc; } + + + /***** Miscellaneous *****/ + + object_pool<HttpHeaderParserState> &getHeaderParserStatePool() { + return headerParserStatePool; + } + /***** Friend-public methods and hook implementations *****/ void _refRequest(Request *request, const char *file, unsigned int line) { refRequest(request, file, line);