| Index: net/server/http_server.cc
|
| diff --git a/net/server/http_server.cc b/net/server/http_server.cc
|
| index 043e625d6db35eb53ca6f0f1894da648efd5e329..fb0dab37a4f44702ea25f2e603a5cba491f41d06 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
|
|
|