| Index: net/server/http_server.cc
|
| diff --git a/net/server/http_server.cc b/net/server/http_server.cc
|
| deleted file mode 100644
|
| index a3f25ef911aa0bed72a8fb37352487a98afaf795..0000000000000000000000000000000000000000
|
| --- a/net/server/http_server.cc
|
| +++ /dev/null
|
| @@ -1,471 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "net/server/http_server.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/compiler_specific.h"
|
| -#include "base/location.h"
|
| -#include "base/logging.h"
|
| -#include "base/message_loop/message_loop_proxy.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/sys_byteorder.h"
|
| -#include "build/build_config.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/server/http_connection.h"
|
| -#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/server_socket.h"
|
| -#include "net/socket/stream_socket.h"
|
| -#include "net/socket/tcp_server_socket.h"
|
| -
|
| -namespace net {
|
| -
|
| -HttpServer::HttpServer(scoped_ptr<ServerSocket> server_socket,
|
| - HttpServer::Delegate* delegate)
|
| - : server_socket_(server_socket.Pass()),
|
| - delegate_(delegate),
|
| - last_id_(0),
|
| - weak_ptr_factory_(this) {
|
| - DCHECK(server_socket_);
|
| - // Start accepting connections in next run loop in case when delegate is not
|
| - // ready to get callbacks.
|
| - base::MessageLoopProxy::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&HttpServer::DoAcceptLoop, weak_ptr_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -HttpServer::~HttpServer() {
|
| - STLDeleteContainerPairSecondPointers(
|
| - id_to_connection_.begin(), id_to_connection_.end());
|
| -}
|
| -
|
| -void HttpServer::AcceptWebSocket(
|
| - int connection_id,
|
| - const HttpServerRequestInfo& request) {
|
| - HttpConnection* connection = FindConnection(connection_id);
|
| - if (connection == NULL)
|
| - return;
|
| - DCHECK(connection->web_socket());
|
| - connection->web_socket()->Accept(request);
|
| -}
|
| -
|
| -void HttpServer::SendOverWebSocket(int connection_id,
|
| - const std::string& data) {
|
| - HttpConnection* connection = FindConnection(connection_id);
|
| - if (connection == NULL)
|
| - return;
|
| - 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;
|
| -
|
| - 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) {
|
| - SendRaw(connection_id, response.Serialize());
|
| -}
|
| -
|
| -void HttpServer::Send(int connection_id,
|
| - HttpStatusCode status_code,
|
| - const std::string& data,
|
| - const std::string& content_type) {
|
| - HttpServerResponseInfo response(status_code);
|
| - response.SetContentHeaders(data.size(), content_type);
|
| - SendResponse(connection_id, response);
|
| - SendRaw(connection_id, data);
|
| -}
|
| -
|
| -void HttpServer::Send200(int connection_id,
|
| - const std::string& data,
|
| - const std::string& content_type) {
|
| - Send(connection_id, HTTP_OK, data, content_type);
|
| -}
|
| -
|
| -void HttpServer::Send404(int connection_id) {
|
| - SendResponse(connection_id, HttpServerResponseInfo::CreateFor404());
|
| -}
|
| -
|
| -void HttpServer::Send500(int connection_id, const std::string& message) {
|
| - SendResponse(connection_id, HttpServerResponseInfo::CreateFor500(message));
|
| -}
|
| -
|
| -void HttpServer::Close(int connection_id) {
|
| - HttpConnection* connection = FindConnection(connection_id);
|
| - if (connection == NULL)
|
| - return;
|
| -
|
| - 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) {
|
| - 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::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;
|
| - delegate_->OnConnect(connection->id());
|
| - if (!HasClosedConnection(connection))
|
| - DoReadLoop(connection);
|
| - return OK;
|
| -}
|
| -
|
| -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;
|
| -
|
| - 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);
|
| - if (result == WebSocket::FRAME_INCOMPLETE)
|
| - break;
|
| -
|
| - if (result == WebSocket::FRAME_CLOSE ||
|
| - result == WebSocket::FRAME_ERROR) {
|
| - Close(connection->id());
|
| - 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(read_buf->StartOfBuffer(), read_buf->GetSize(),
|
| - &request, &pos)) {
|
| - break;
|
| - }
|
| -
|
| - // Sets peer address if exists.
|
| - connection->socket()->GetPeerAddress(&request.peer);
|
| -
|
| - if (request.HasHeaderValue("connection", "upgrade")) {
|
| - 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);
|
| - if (HasClosedConnection(connection))
|
| - return ERR_CONNECTION_CLOSED;
|
| - continue;
|
| - }
|
| -
|
| - const char kContentLength[] = "content-length";
|
| - 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) {
|
| - SendResponse(connection->id(),
|
| - HttpServerResponseInfo::CreateFor500(
|
| - "request content-length too big or unknown: " +
|
| - request.GetHeaderValue(kContentLength)));
|
| - Close(connection->id());
|
| - return ERR_CONNECTION_CLOSED;
|
| - }
|
| -
|
| - if (read_buf->GetSize() - pos < content_length)
|
| - break; // Not enough data was received yet.
|
| - request.data.assign(read_buf->StartOfBuffer() + pos, content_length);
|
| - pos += content_length;
|
| - }
|
| -
|
| - read_buf->DidConsume(pos);
|
| - delegate_->OnHttpRequest(connection->id(), request);
|
| - if (HasClosedConnection(connection))
|
| - return ERR_CONNECTION_CLOSED;
|
| - }
|
| -
|
| - return OK;
|
| -}
|
| -
|
| -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);
|
| - }
|
| -}
|
| -
|
| -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
|
| -// through the headers. The parser is not 100% complete, as it is designed
|
| -// for use in this simple test driver.
|
| -//
|
| -// Known issues:
|
| -// - does not handle whitespace on first HTTP line correctly. Expects
|
| -// a single space between the method/url and url/protocol.
|
| -
|
| -// Input character types.
|
| -enum header_parse_inputs {
|
| - INPUT_LWS,
|
| - INPUT_CR,
|
| - INPUT_LF,
|
| - INPUT_COLON,
|
| - INPUT_DEFAULT,
|
| - MAX_INPUTS,
|
| -};
|
| -
|
| -// Parser states.
|
| -enum header_parse_states {
|
| - ST_METHOD, // Receiving the method
|
| - ST_URL, // Receiving the URL
|
| - ST_PROTO, // Receiving the protocol
|
| - ST_HEADER, // Starting a Request Header
|
| - ST_NAME, // Receiving a request header name
|
| - ST_SEPARATOR, // Receiving the separator between header name and value
|
| - ST_VALUE, // Receiving a request header value
|
| - ST_DONE, // Parsing is complete and successful
|
| - ST_ERR, // Parsing encountered invalid syntax.
|
| - MAX_STATES
|
| -};
|
| -
|
| -// State transition table
|
| -int parser_state[MAX_STATES][MAX_INPUTS] = {
|
| -/* METHOD */ { ST_URL, ST_ERR, ST_ERR, ST_ERR, ST_METHOD },
|
| -/* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_URL },
|
| -/* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_PROTO },
|
| -/* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR },
|
| -/* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_VALUE, ST_NAME },
|
| -/* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_VALUE, ST_ERR },
|
| -/* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_VALUE },
|
| -/* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE },
|
| -/* ERR */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR }
|
| -};
|
| -
|
| -// Convert an input character to the parser's input token.
|
| -int charToInput(char ch) {
|
| - switch(ch) {
|
| - case ' ':
|
| - case '\t':
|
| - return INPUT_LWS;
|
| - case '\r':
|
| - return INPUT_CR;
|
| - case '\n':
|
| - return INPUT_LF;
|
| - case ':':
|
| - return INPUT_COLON;
|
| - }
|
| - return INPUT_DEFAULT;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -bool HttpServer::ParseHeaders(const char* data,
|
| - size_t data_len,
|
| - HttpServerRequestInfo* info,
|
| - size_t* ppos) {
|
| - size_t& pos = *ppos;
|
| - int state = ST_METHOD;
|
| - std::string buffer;
|
| - std::string header_name;
|
| - std::string header_value;
|
| - while (pos < data_len) {
|
| - char ch = data[pos++];
|
| - int input = charToInput(ch);
|
| - int next_state = parser_state[state][input];
|
| -
|
| - bool transition = (next_state != state);
|
| - HttpServerRequestInfo::HeadersMap::iterator it;
|
| - if (transition) {
|
| - // Do any actions based on state transitions.
|
| - switch (state) {
|
| - case ST_METHOD:
|
| - info->method = buffer;
|
| - buffer.clear();
|
| - break;
|
| - case ST_URL:
|
| - info->path = buffer;
|
| - buffer.clear();
|
| - break;
|
| - case ST_PROTO:
|
| - // TODO(mbelshe): Deal better with parsing protocol.
|
| - DCHECK(buffer == "HTTP/1.1");
|
| - buffer.clear();
|
| - break;
|
| - case ST_NAME:
|
| - header_name = base::StringToLowerASCII(buffer);
|
| - buffer.clear();
|
| - break;
|
| - case ST_VALUE:
|
| - base::TrimWhitespaceASCII(buffer, base::TRIM_LEADING, &header_value);
|
| - it = info->headers.find(header_name);
|
| - // See last paragraph ("Multiple message-header fields...")
|
| - // of www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
| - if (it == info->headers.end()) {
|
| - info->headers[header_name] = header_value;
|
| - } else {
|
| - it->second.append(",");
|
| - it->second.append(header_value);
|
| - }
|
| - buffer.clear();
|
| - break;
|
| - case ST_SEPARATOR:
|
| - break;
|
| - }
|
| - state = next_state;
|
| - } else {
|
| - // Do any actions based on current state
|
| - switch (state) {
|
| - case ST_METHOD:
|
| - case ST_URL:
|
| - case ST_PROTO:
|
| - case ST_VALUE:
|
| - case ST_NAME:
|
| - buffer.append(&ch, 1);
|
| - break;
|
| - case ST_DONE:
|
| - DCHECK(input == INPUT_LF);
|
| - return true;
|
| - case ST_ERR:
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - // No more characters, but we haven't finished parsing yet.
|
| - return false;
|
| -}
|
| -
|
| -HttpConnection* HttpServer::FindConnection(int connection_id) {
|
| - IdToConnectionMap::iterator it = id_to_connection_.find(connection_id);
|
| - if (it == id_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
|
|
|