diff options
author | byungchul@chromium.org <byungchul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-22 18:10:13 +0000 |
---|---|---|
committer | byungchul@chromium.org <byungchul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-22 18:12:05 +0000 |
commit | 3626bc39d629245a782c61ba0bc668583778e392 (patch) | |
tree | 7156999fdfb9044dd14d5463568b8f6ce01543fe /net/server/http_server.cc | |
parent | 1c2f071e2a14a39b215a897434d30834ae959560 (diff) | |
download | chromium_src-3626bc39d629245a782c61ba0bc668583778e392.zip chromium_src-3626bc39d629245a782c61ba0bc668583778e392.tar.gz chromium_src-3626bc39d629245a782c61ba0bc668583778e392.tar.bz2 |
Replace StreamListenSocket with StreamSocket in HttpServer.
1) HttpServer gets ServerSocket instead of StreamListenSocket.
2) HttpConnection is just a container for socket, websocket, and pending read/write buffers.
3) HttpServer handles data buffering and asynchronous read/write.
4) HttpConnection has limit in data buffering, up to 1Mbytes by default.
5) For devtools, send buffer limit is 100Mbytes.
6) Unittests for buffer handling in HttpConnection.
BUG=371906
Review URL: https://codereview.chromium.org/296053012
Cr-Commit-Position: refs/heads/master@{#291447}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291447 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/server/http_server.cc')
-rw-r--r-- | net/server/http_server.cc | 257 |
1 files changed, 186 insertions, 71 deletions
diff --git a/net/server/http_server.cc b/net/server/http_server.cc index 043e625..fb0dab3 100644 --- a/net/server/http_server.cc +++ b/net/server/http_server.cc @@ -17,14 +17,25 @@ #include "net/server/http_server_request_info.h" #include "net/server/http_server_response_info.h" #include "net/server/web_socket.h" -#include "net/socket/tcp_listen_socket.h" +#include "net/socket/server_socket.h" +#include "net/socket/stream_socket.h" +#include "net/socket/tcp_server_socket.h" namespace net { -HttpServer::HttpServer(const StreamListenSocketFactory& factory, +HttpServer::HttpServer(scoped_ptr<ServerSocket> server_socket, HttpServer::Delegate* delegate) - : delegate_(delegate), - server_(factory.CreateAndListen(this)) { + : server_socket_(server_socket.Pass()), + delegate_(delegate), + last_id_(0), + weak_ptr_factory_(this) { + DCHECK(server_socket_); + DoAcceptLoop(); +} + +HttpServer::~HttpServer() { + STLDeleteContainerPairSecondPointers( + id_to_connection_.begin(), id_to_connection_.end()); } void HttpServer::AcceptWebSocket( @@ -33,9 +44,8 @@ void HttpServer::AcceptWebSocket( HttpConnection* connection = FindConnection(connection_id); if (connection == NULL) return; - - DCHECK(connection->web_socket_.get()); - connection->web_socket_->Accept(request); + DCHECK(connection->web_socket()); + connection->web_socket()->Accept(request); } void HttpServer::SendOverWebSocket(int connection_id, @@ -43,23 +53,23 @@ void HttpServer::SendOverWebSocket(int connection_id, HttpConnection* connection = FindConnection(connection_id); if (connection == NULL) return; - DCHECK(connection->web_socket_.get()); - connection->web_socket_->Send(data); + DCHECK(connection->web_socket()); + connection->web_socket()->Send(data); } void HttpServer::SendRaw(int connection_id, const std::string& data) { HttpConnection* connection = FindConnection(connection_id); if (connection == NULL) return; - connection->Send(data); + + bool writing_in_progress = !connection->write_buf()->IsEmpty(); + if (connection->write_buf()->Append(data) && !writing_in_progress) + DoWriteLoop(connection); } void HttpServer::SendResponse(int connection_id, const HttpServerResponseInfo& response) { - HttpConnection* connection = FindConnection(connection_id); - if (connection == NULL) - return; - connection->Send(response); + SendRaw(connection_id, response.Serialize()); } void HttpServer::Send(int connection_id, @@ -67,8 +77,9 @@ void HttpServer::Send(int connection_id, const std::string& data, const std::string& content_type) { HttpServerResponseInfo response(status_code); - response.SetBody(data, content_type); + response.SetContentHeaders(data.size(), content_type); SendResponse(connection_id, response); + SendRaw(connection_id, data); } void HttpServer::Send200(int connection_id, @@ -90,108 +101,209 @@ void HttpServer::Close(int connection_id) { if (connection == NULL) return; - // Initiating close from server-side does not lead to the DidClose call. - // Do it manually here. - DidClose(connection->socket_.get()); + id_to_connection_.erase(connection_id); + delegate_->OnClose(connection_id); + + // The call stack might have callbacks which still have the pointer of + // connection. Instead of referencing connection with ID all the time, + // destroys the connection in next run loop to make sure any pending + // callbacks in the call stack return. + base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, connection); } int HttpServer::GetLocalAddress(IPEndPoint* address) { - if (!server_) - return ERR_SOCKET_NOT_CONNECTED; - return server_->GetLocalAddress(address); + return server_socket_->GetLocalAddress(address); +} + +void HttpServer::SetReceiveBufferSize(int connection_id, int32 size) { + HttpConnection* connection = FindConnection(connection_id); + DCHECK(connection); + connection->read_buf()->set_max_buffer_size(size); } -void HttpServer::DidAccept(StreamListenSocket* server, - scoped_ptr<StreamListenSocket> socket) { - HttpConnection* connection = new HttpConnection(this, socket.Pass()); +void HttpServer::SetSendBufferSize(int connection_id, int32 size) { + HttpConnection* connection = FindConnection(connection_id); + DCHECK(connection); + connection->write_buf()->set_max_buffer_size(size); +} + +void HttpServer::DoAcceptLoop() { + int rv; + do { + rv = server_socket_->Accept(&accepted_socket_, + base::Bind(&HttpServer::OnAcceptCompleted, + weak_ptr_factory_.GetWeakPtr())); + if (rv == ERR_IO_PENDING) + return; + rv = HandleAcceptResult(rv); + } while (rv == OK); +} + +void HttpServer::OnAcceptCompleted(int rv) { + if (HandleAcceptResult(rv) == OK) + DoAcceptLoop(); +} + +int HttpServer::HandleAcceptResult(int rv) { + if (rv < 0) { + LOG(ERROR) << "Accept error: rv=" << rv; + return rv; + } + + HttpConnection* connection = + new HttpConnection(++last_id_, accepted_socket_.Pass()); id_to_connection_[connection->id()] = connection; - // TODO(szym): Fix socket access. Make HttpConnection the Delegate. - socket_to_connection_[connection->socket_.get()] = connection; + DoReadLoop(connection); + return OK; } -void HttpServer::DidRead(StreamListenSocket* socket, - const char* data, - int len) { - HttpConnection* connection = FindConnection(socket); - DCHECK(connection != NULL); - if (connection == NULL) +void HttpServer::DoReadLoop(HttpConnection* connection) { + int rv; + do { + HttpConnection::ReadIOBuffer* read_buf = connection->read_buf(); + // Increases read buffer size if necessary. + if (read_buf->RemainingCapacity() == 0 && !read_buf->IncreaseCapacity()) { + Close(connection->id()); + return; + } + + rv = connection->socket()->Read( + read_buf, + read_buf->RemainingCapacity(), + base::Bind(&HttpServer::OnReadCompleted, + weak_ptr_factory_.GetWeakPtr(), connection->id())); + if (rv == ERR_IO_PENDING) + return; + rv = HandleReadResult(connection, rv); + } while (rv == OK); +} + +void HttpServer::OnReadCompleted(int connection_id, int rv) { + HttpConnection* connection = FindConnection(connection_id); + if (!connection) // It might be closed right before by write error. return; - connection->recv_data_.append(data, len); - while (connection->recv_data_.length()) { - if (connection->web_socket_.get()) { + if (HandleReadResult(connection, rv) == OK) + DoReadLoop(connection); +} + +int HttpServer::HandleReadResult(HttpConnection* connection, int rv) { + if (rv <= 0) { + Close(connection->id()); + return rv == 0 ? ERR_CONNECTION_CLOSED : rv; + } + + HttpConnection::ReadIOBuffer* read_buf = connection->read_buf(); + read_buf->DidRead(rv); + + // Handles http requests or websocket messages. + while (read_buf->GetSize() > 0) { + if (connection->web_socket()) { std::string message; - WebSocket::ParseResult result = connection->web_socket_->Read(&message); + WebSocket::ParseResult result = connection->web_socket()->Read(&message); if (result == WebSocket::FRAME_INCOMPLETE) break; if (result == WebSocket::FRAME_CLOSE || result == WebSocket::FRAME_ERROR) { Close(connection->id()); - break; + return ERR_CONNECTION_CLOSED; } delegate_->OnWebSocketMessage(connection->id(), message); + if (HasClosedConnection(connection)) + return ERR_CONNECTION_CLOSED; continue; } HttpServerRequestInfo request; size_t pos = 0; - if (!ParseHeaders(connection, &request, &pos)) + if (!ParseHeaders(read_buf->StartOfBuffer(), read_buf->GetSize(), + &request, &pos)) { break; + } // Sets peer address if exists. - socket->GetPeerAddress(&request.peer); + connection->socket()->GetPeerAddress(&request.peer); if (request.HasHeaderValue("connection", "upgrade")) { - connection->web_socket_.reset(WebSocket::CreateWebSocket(connection, - request, - &pos)); - - if (!connection->web_socket_.get()) // Not enough data was received. + scoped_ptr<WebSocket> websocket( + WebSocket::CreateWebSocket(this, connection, request, &pos)); + if (!websocket) // Not enough data was received. break; + connection->SetWebSocket(websocket.Pass()); + read_buf->DidConsume(pos); delegate_->OnWebSocketRequest(connection->id(), request); - connection->Shift(pos); + if (HasClosedConnection(connection)) + return ERR_CONNECTION_CLOSED; continue; } const char kContentLength[] = "content-length"; - if (request.headers.count(kContentLength)) { + if (request.headers.count(kContentLength) > 0) { size_t content_length = 0; const size_t kMaxBodySize = 100 << 20; if (!base::StringToSizeT(request.GetHeaderValue(kContentLength), &content_length) || content_length > kMaxBodySize) { - connection->Send(HttpServerResponseInfo::CreateFor500( - "request content-length too big or unknown: " + - request.GetHeaderValue(kContentLength))); - DidClose(socket); - break; + SendResponse(connection->id(), + HttpServerResponseInfo::CreateFor500( + "request content-length too big or unknown: " + + request.GetHeaderValue(kContentLength))); + Close(connection->id()); + return ERR_CONNECTION_CLOSED; } - if (connection->recv_data_.length() - pos < content_length) + if (read_buf->GetSize() - pos < content_length) break; // Not enough data was received yet. - request.data = connection->recv_data_.substr(pos, content_length); + request.data.assign(read_buf->StartOfBuffer() + pos, content_length); pos += content_length; } + read_buf->DidConsume(pos); delegate_->OnHttpRequest(connection->id(), request); - connection->Shift(pos); + if (HasClosedConnection(connection)) + return ERR_CONNECTION_CLOSED; } + + return OK; } -void HttpServer::DidClose(StreamListenSocket* socket) { - HttpConnection* connection = FindConnection(socket); - DCHECK(connection != NULL); - id_to_connection_.erase(connection->id()); - socket_to_connection_.erase(connection->socket_.get()); - delete connection; +void HttpServer::DoWriteLoop(HttpConnection* connection) { + int rv = OK; + HttpConnection::QueuedWriteIOBuffer* write_buf = connection->write_buf(); + while (rv == OK && write_buf->GetSizeToWrite() > 0) { + rv = connection->socket()->Write( + write_buf, + write_buf->GetSizeToWrite(), + base::Bind(&HttpServer::OnWriteCompleted, + weak_ptr_factory_.GetWeakPtr(), connection->id())); + if (rv == ERR_IO_PENDING || rv == OK) + return; + rv = HandleWriteResult(connection, rv); + } } -HttpServer::~HttpServer() { - STLDeleteContainerPairSecondPointers( - id_to_connection_.begin(), id_to_connection_.end()); +void HttpServer::OnWriteCompleted(int connection_id, int rv) { + HttpConnection* connection = FindConnection(connection_id); + if (!connection) // It might be closed right before by read error. + return; + + if (HandleWriteResult(connection, rv) == OK) + DoWriteLoop(connection); } +int HttpServer::HandleWriteResult(HttpConnection* connection, int rv) { + if (rv < 0) { + Close(connection->id()); + return rv; + } + + connection->write_buf()->DidConsume(rv); + return OK; +} + +namespace { + // // HTTP Request Parser // This HTTP request parser uses a simple state machine to quickly parse @@ -255,17 +367,19 @@ int charToInput(char ch) { return INPUT_DEFAULT; } -bool HttpServer::ParseHeaders(HttpConnection* connection, +} // namespace + +bool HttpServer::ParseHeaders(const char* data, + size_t data_len, HttpServerRequestInfo* info, size_t* ppos) { size_t& pos = *ppos; - size_t data_len = connection->recv_data_.length(); int state = ST_METHOD; std::string buffer; std::string header_name; std::string header_value; while (pos < data_len) { - char ch = connection->recv_data_[pos++]; + char ch = data[pos++]; int input = charToInput(ch); int next_state = parser_state[state][input]; @@ -337,11 +451,12 @@ HttpConnection* HttpServer::FindConnection(int connection_id) { return it->second; } -HttpConnection* HttpServer::FindConnection(StreamListenSocket* socket) { - SocketToConnectionMap::iterator it = socket_to_connection_.find(socket); - if (it == socket_to_connection_.end()) - return NULL; - return it->second; +// This is called after any delegate callbacks are called to check if Close() +// has been called during callback processing. Using the pointer of connection, +// |connection| is safe here because Close() deletes the connection in next run +// loop. +bool HttpServer::HasClosedConnection(HttpConnection* connection) { + return FindConnection(connection->id()) != connection; } } // namespace net |