diff --git a/sling/http/http-server.cc b/sling/http/http-server.cc index ef118dbd..4b0824f7 100644 --- a/sling/http/http-server.cc +++ b/sling/http/http-server.cc @@ -329,11 +329,15 @@ void HTTPServer::Worker() { // Get new events. int rc = epoll_wait(pollfd_, events, max_events, 2000); if (stop_) break; - if (rc == 0 || errno == EAGAIN) continue; + if (errno == EAGAIN) continue; if (rc < 0) { LOG(ERROR) << Error("epoll_wait"); break; } + if (rc == 0) { + ShutdownIdleConnections(); + continue; + } // Process events. for (int i = 0; i < rc; ++i) { @@ -364,14 +368,20 @@ void HTTPServer::Worker() { conn->state_ = HTTP_STATE_TERMINATE; } - // Update expected events. - ev->events = 0; - if (conn->AwaitsInput()) ev->events |= EPOLLIN; - if (conn->HasOutput()) ev->events |= EPOLLOUT; - rc = epoll_ctl(pollfd_, EPOLL_CTL_MOD, conn->sock_, ev); - if (rc < 0) LOG(ERROR) << Error("epoll_ctl"); - VLOG(5) << "Done processing in state " << conn->state_ - << ", events " << ev->events; + if (conn->state_ == HTTP_STATE_TERMINATE) { + conn->Shutdown(); + VLOG(5) << "Shutdown HTTP connection"; + } else { + // Update expected events. + ev->events = 0; + if (conn->AwaitsInput()) ev->events |= EPOLLIN; + if (conn->HasOutput()) ev->events |= EPOLLOUT; + rc = epoll_ctl(pollfd_, EPOLL_CTL_MOD, conn->sock_, ev); + if (rc < 0) LOG(ERROR) << Error("epoll_ctl"); + conn->last_ = time(0); + VLOG(5) << "Done processing in state " << conn->state_ + << ", events " << ev->events; + } } } } @@ -437,6 +447,20 @@ void HTTPServer::RemoveConnection(HTTPConnection *conn) { conn->next_ = conn->prev_ = nullptr; } +void HTTPServer::ShutdownIdleConnections() { + if (options_.max_idle <= 0) return; + MutexLock lock(&mu_); + time_t expire = time(0) - options_.max_idle; + HTTPConnection *conn = connections_; + while (conn != nullptr) { + if (conn->last_ < expire) { + conn->Shutdown(); + VLOG(5) << "Shut down idle connection"; + } + conn = conn->next_; + } +} + HTTPConnection::HTTPConnection(HTTPServer *server, int sock) : server_(server), sock_(sock) { next_ = prev_ = nullptr; @@ -446,6 +470,7 @@ HTTPConnection::HTTPConnection(HTTPServer *server, int sock) state_ = HTTP_STATE_IDLE; header_state_ = HDR_STATE_FIRSTWORD; keep_ = false; + last_ = time(0); } HTTPConnection::~HTTPConnection() { @@ -514,10 +539,9 @@ Status HTTPConnection::Process(int events) { // Fall through case HTTP_STATE_READ_BODY: + // Check if any input data is ready. + if ((events & EPOLLIN) == 0) return Status::OK; while (request_body_.size() < request_->content_length()) { - // Check if any input data is ready. - if ((events & EPOLLIN) == 0) return Status::OK; - // Receive more data. Status st = Recv(&request_body_, &done); if (!st.ok()) return st; @@ -656,6 +680,10 @@ Status HTTPConnection::Send(HTTPBuffer *buffer, bool *done) { return Status::OK; } +void HTTPConnection::Shutdown() { + shutdown(sock_, SHUT_RDWR); +} + void HTTPConnection::Dispatch() { // Allocate response object. delete response_; diff --git a/sling/http/http-server.h b/sling/http/http-server.h index 1f359209..d7b2996b 100644 --- a/sling/http/http-server.h +++ b/sling/http/http-server.h @@ -17,6 +17,7 @@ #include #include +#include #include #include "sling/base/status.h" @@ -122,6 +123,9 @@ struct HTTPServerOptions { // Number of events per worker poll. int max_events = 1; + // Maximum idle time (in seconds) before connection is shut down. + int max_idle = 600; + // Initial buffer size. int initial_bufsiz = 1 << 10; @@ -192,6 +196,9 @@ class HTTPServer { // Remove connection from server. void RemoveConnection(HTTPConnection *conn); + // Shut down idle connections. + void ShutdownIdleConnections(); + // Server configuration. HTTPServerOptions options_; @@ -277,12 +284,18 @@ class HTTPConnection { // be sent without blocking has been sent. Status Send(HTTPBuffer *buffer, bool *done); + // Shut down connection. + void Shutdown(); + // HTTP server for connection. HTTPServer *server_; // Socket for connection. int sock_; + // Last time event was received on connection. + time_t last_; + // HTTP connection list. HTTPConnection *next_; HTTPConnection *prev_;