bool HttpConnection::onMessageBegin(const BufferRef& method, const BufferRef& uri, int versionMajor, int versionMinor) { TRACE("onMessageBegin: '%s', '%s', HTTP/%d.%d", method.str().c_str(), uri.str().c_str(), versionMajor, versionMinor); request_->method = method; request_->uri = uri; url_decode(input_, request_->uri); std::size_t n = request_->uri.find("?"); if (n != std::string::npos) { request_->path = request_->uri.ref(0, n); request_->query = request_->uri.ref(n + 1); } else { request_->path = request_->uri; } request_->httpVersionMajor = versionMajor; request_->httpVersionMinor = versionMinor; if (request_->supportsProtocol(1, 1)) // FIXME? HTTP/1.1 is keeping alive by default. pass "Connection: close" to close explicitely setShouldKeepAlive(true); else setShouldKeepAlive(false); // limit request uri length if (request_->uri.size() > worker().server().maxRequestUriSize()) { request_->status = HttpError::RequestUriTooLong; request_->finish(); return false; } return true; }
bool HttpConnection::onMessageBegin(const BufferRef& method, const BufferRef& uri, int versionMajor, int versionMinor) { TRACE(1, "onMessageBegin: '%s', '%s', HTTP/%d.%d", method.str().c_str(), uri.str().c_str(), versionMajor, versionMinor); request_->method = method; if (!request_->setUri(uri)) { abort(HttpStatus::BadRequest); return false; } request_->httpVersionMajor = versionMajor; request_->httpVersionMinor = versionMinor; if (request_->supportsProtocol(1, 1)) // FIXME? HTTP/1.1 is keeping alive by default. pass "Connection: close" to close explicitely setShouldKeepAlive(true); else setShouldKeepAlive(false); // limit request uri length if (request_->unparsedUri.size() > worker().server().maxRequestUriSize()) { request_->status = HttpStatus::RequestUriTooLong; request_->finish(); return false; } return true; }
bool HttpConnection::onMessageHeader(const BufferRef& name, const BufferRef& value) { if (request_->isFinished()) { // this can happen when the request has failed some checks and thus, // a client error message has been sent already. // we need to "parse" the remaining content anyways. TRACE("onMessageHeader() skip \"%s\": \"%s\"", name.str().c_str(), value.str().c_str()); return true; } TRACE("onMessageHeader() \"%s\": \"%s\"", name.str().c_str(), value.str().c_str()); if (iequals(name, "Host")) { auto i = value.find(':'); if (i != BufferRef::npos) request_->hostname = value.ref(0, i); else request_->hostname = value; TRACE(" -- hostname set to \"%s\"", request_->hostname.str().c_str()); } else if (iequals(name, "Connection")) { if (iequals(value, "close")) setShouldKeepAlive(false); else if (iequals(value, "keep-alive")) setShouldKeepAlive(true); } // limit the size of a single request header if (name.size() + value.size() > worker().server().maxRequestHeaderSize()) { TRACE("header too long. got %ld / %ld", name.size() + value.size(), worker().server().maxRequestHeaderSize()); request_->status = HttpError::RequestEntityTooLarge; request_->finish(); return false; } // limit the number of request headers if (request_->requestHeaders.size() > worker().server().maxRequestHeaderCount()) { TRACE("header count exceeded. got %ld / %ld", request_->requestHeaders.size(), worker().server().maxRequestHeaderCount()); request_->status = HttpError::RequestEntityTooLarge; request_->finish(); return false; } request_->requestHeaders.push_back(HttpRequestHeader(name, value)); return true; }
/** * Aborts processing current request with given HTTP status code. * * This method is supposed to be invoked within the Request parsing state. * It will reply the current request with the given \p status code and close the connection. * This is kind of a soft abort, as the client will be notified about the soft error with a proper reply. * * \param status the HTTP status code (should be a 4xx client error). * * \see abort() */ void HttpConnection::abort(HttpStatus status) { ++requestCount_; flags_ |= IsHandlingRequest; setStatus(ProcessingRequest); setShouldKeepAlive(false); request_->status = status; request_->finish(); }
/** processes a (partial) request from buffer's given \p offset of \p count bytes. */ bool HttpConnection::process() { TRACE("process: offset=%ld, size=%ld (before processing)", inputOffset_, input_.size()); while (state() != MESSAGE_BEGIN || status() == ReadingRequest || status() == KeepAliveRead) { BufferRef chunk(input_.ref(inputOffset_)); if (chunk.empty()) break; // ensure status is up-to-date, in case we came from keep-alive-read if (status_ == KeepAliveRead) { TRACE("process: status=keep-alive-read, resetting to reading-request"); status_ = ReadingRequest; if (request_->isFinished()) { TRACE("process: finalizing request"); request_->finalize(); } } TRACE("process: (size: %ld, isHandlingRequest:%d, state:%s status:%s", chunk.size(), (flags_ & IsHandlingRequest) != 0, state_str(), status_str()); //TRACE("%s", input_.ref(input_.size() - rv).str().c_str()); HttpMessageProcessor::process(chunk, &inputOffset_); TRACE("process: done process()ing; fd=%d, request=%p state:%s status:%s", socket_->handle(), request_, state_str(), status_str()); if (isAborted()) return false; if (state() == SYNTAX_ERROR) { if (!request_->isFinished()) { setShouldKeepAlive(false); request_->status = HttpError::BadRequest; request_->finish(); } return false; } } TRACE("process: offset=%ld, bs=%ld, state=%s (after processing) io.timer:%d", inputOffset_, input_.size(), state_str(), socket_->timerActive()); return true; }
void HttpConnection::timeout(Socket *) { TRACE("timedout: status=%s", status_str()); switch (status()) { case StartingUp: TRACE("timeout: BUG. we should have never reached here."); break; case ReadingRequest: // we do not want further out-timing requests on this conn: just close it. setShouldKeepAlive(false); request_->status = HttpError::RequestTimeout; status_ = SendingReply; request_->finish(); break; case KeepAliveRead: close(); break; case SendingReply: abort(); break; } }