From a33721a77396c8f7607d330cd3d07b82f4ebc0ed Mon Sep 17 00:00:00 2001 From: "pfeldman@chromium.org" Date: Thu, 3 Feb 2011 17:23:24 +0000 Subject: DevTools: split http_listen_socket into http_server and connection. BUG= TEST= Review URL: http://codereview.chromium.org/6410033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73631 0039d316-1c4b-4281-b951-d872f2087c98 --- net/server/http_listen_socket.cc | 357 --------------------------------- net/server/http_listen_socket.h | 72 ------- net/server/http_server.cc | 416 +++++++++++++++++++++++++++++++++++++++ net/server/http_server.h | 96 +++++++++ 4 files changed, 512 insertions(+), 429 deletions(-) delete mode 100644 net/server/http_listen_socket.cc delete mode 100644 net/server/http_listen_socket.h create mode 100644 net/server/http_server.cc create mode 100644 net/server/http_server.h (limited to 'net/server') diff --git a/net/server/http_listen_socket.cc b/net/server/http_listen_socket.cc deleted file mode 100644 index a946bd9..0000000 --- a/net/server/http_listen_socket.cc +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) 2010 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. - -#ifdef _WIN32 -#include -#else -#include -#endif - -#include - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/md5.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/stringprintf.h" -#include "net/server/http_listen_socket.h" -#include "net/server/http_server_request_info.h" - -// must run in the IO thread -HttpListenSocket::HttpListenSocket(SOCKET s, - HttpListenSocket::Delegate* delegate) - : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)), - delegate_(delegate), - is_web_socket_(false) { -} - -// must run in the IO thread -HttpListenSocket::~HttpListenSocket() { -} - -void HttpListenSocket::Accept() { - SOCKET conn = ListenSocket::Accept(socket_); - DCHECK_NE(conn, ListenSocket::kInvalidSocket); - if (conn == ListenSocket::kInvalidSocket) { - // TODO - } else { - scoped_refptr sock( - new HttpListenSocket(conn, delegate_)); -#if defined(OS_POSIX) - sock->WatchSocket(WAITING_READ); -#endif - // it's up to the delegate to AddRef if it wants to keep it around - DidAccept(this, sock); - } -} - -HttpListenSocket* HttpListenSocket::Listen( - const std::string& ip, - int port, - HttpListenSocket::Delegate* delegate) { - SOCKET s = ListenSocket::Listen(ip, port); - if (s == ListenSocket::kInvalidSocket) { - // TODO (ibrar): error handling - } else { - HttpListenSocket *serv = new HttpListenSocket(s, delegate); - serv->Listen(); - return serv; - } - return NULL; -} - -std::string GetHeaderValue( - const HttpServerRequestInfo& request, - const std::string& header_name) { - HttpServerRequestInfo::HeadersMap::iterator it = - request.headers.find(header_name); - if (it != request.headers.end()) - return it->second; - return ""; -} - -uint32 WebSocketKeyFingerprint(const std::string& str) { - std::string result; - const char* pChar = str.c_str(); - int length = str.length(); - int spaces = 0; - for (int i = 0; i < length; ++i) { - if (pChar[i] >= '0' && pChar[i] <= '9') - result.append(&pChar[i], 1); - else if (pChar[i] == ' ') - spaces++; - } - if (spaces == 0) - return 0; - int64 number = 0; - if (!base::StringToInt64(result, &number)) - return 0; - return htonl(static_cast(number / spaces)); -} - -void HttpListenSocket::AcceptWebSocket(const HttpServerRequestInfo& request) { - std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1"); - std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2"); - - uint32 fp1 = WebSocketKeyFingerprint(key1); - uint32 fp2 = WebSocketKeyFingerprint(key2); - - char data[16]; - memcpy(data, &fp1, 4); - memcpy(data + 4, &fp2, 4); - memcpy(data + 8, &request.data[0], 8); - - MD5Digest digest; - MD5Sum(data, 16, &digest); - - std::string origin = GetHeaderValue(request, "Origin"); - std::string host = GetHeaderValue(request, "Host"); - std::string location = "ws://" + host + request.path; - is_web_socket_ = true; - Send(base::StringPrintf("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Origin: %s\r\n" - "Sec-WebSocket-Location: %s\r\n" - "\r\n", - origin.c_str(), - location.c_str())); - Send(reinterpret_cast(digest.a), 16); -} - -void HttpListenSocket::SendOverWebSocket(const std::string& data) { - DCHECK(is_web_socket_); - char message_start = 0; - char message_end = -1; - Send(&message_start, 1); - Send(data); - Send(&message_end, 1); -} - -void HttpListenSocket::Send200(const std::string& data, - const std::string& content_type) { - Send(base::StringPrintf("HTTP/1.1 200 OK\r\n" - "Content-Type:%s\r\n" - "Content-Length:%d\r\n" - "\r\n", - content_type.c_str(), - static_cast(data.length()))); - Send(data); -} - -void HttpListenSocket::Send404() { - Send("HTTP/1.1 404 Not Found\r\n" - "Content-Length: 0\r\n" - "\r\n"); -} - -void HttpListenSocket::Send500(const std::string& message) { - Send(base::StringPrintf("HTTP/1.1 500 Internal Error\r\n" - "Content-Type:text/html\r\n" - "Content-Length:%d\r\n" - "\r\n" - "%s", - static_cast(message.length()), - message.c_str())); -} - -// -// 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_SPACE, - INPUT_CR, - INPUT_LF, - INPUT_COLON, - INPUT_00, - INPUT_FF, - 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_WS_READY, // Ready to receive web socket frame - ST_WS_FRAME, // Receiving WebSocket frame - ST_WS_CLOSE, // Closing the connection WebSocket connection - 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_ERR, ST_ERR, ST_METHOD }, -/* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_ERR, ST_ERR, ST_URL }, -/* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_PROTO }, -/* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_ERR }, -/* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_NAME }, -/* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_VALUE }, -/* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_ERR, ST_ERR, ST_VALUE }, -/* WS_READY */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_FRAME, ST_WS_CLOSE, ST_ERR}, -/* WS_FRAME */ { ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_ERR, ST_WS_READY, ST_WS_FRAME }, -/* WS_CLOSE */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_CLOSE, ST_ERR, ST_ERR }, -/* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE }, -/* ERR */ { ST_ERR, ST_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 ' ': - return INPUT_SPACE; - case '\r': - return INPUT_CR; - case '\n': - return INPUT_LF; - case ':': - return INPUT_COLON; - case 0x0: - return INPUT_00; - case static_cast(-1): - return INPUT_FF; - } - return INPUT_DEFAULT; -} - -bool HttpListenSocket::ParseHeaders(HttpServerRequestInfo* info) { - int pos = 0; - int data_len = recv_data_.length(); - int state = is_web_socket_ ? ST_WS_READY : ST_METHOD; - std::string buffer; - std::string header_name; - std::string header_value; - while (pos < data_len) { - char ch = recv_data_[pos++]; - int input = charToInput(ch); - int next_state = parser_state[state][input]; - - bool transition = (next_state != state); - 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 = buffer; - buffer.clear(); - break; - case ST_VALUE: - header_value = buffer; - // TODO(mbelshe): Deal better with duplicate headers - DCHECK(info->headers.find(header_name) == info->headers.end()); - info->headers[header_name] = header_value; - buffer.clear(); - break; - case ST_SEPARATOR: - buffer.append(&ch, 1); - break; - case ST_WS_FRAME: - recv_data_ = recv_data_.substr(pos); - info->data = buffer; - buffer.clear(); - return true; - 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: - case ST_WS_FRAME: - buffer.append(&ch, 1); - break; - case ST_DONE: - recv_data_ = recv_data_.substr(pos); - info->data = recv_data_; - recv_data_.clear(); - return true; - case ST_WS_CLOSE: - is_web_socket_ = false; - return false; - case ST_ERR: - return false; - } - } - } - // No more characters, but we haven't finished parsing yet. - return false; -} - -void HttpListenSocket::Close() { - ListenSocket::Close(); -} - -void HttpListenSocket::Listen() { - ListenSocket::Listen(); -} - -void HttpListenSocket::DidAccept(ListenSocket* server, - ListenSocket* connection) { - connection->AddRef(); -} - -void HttpListenSocket::DidRead(ListenSocket*, - const char* data, - int len) { - recv_data_.append(data, len); - while (recv_data_.length()) { - HttpServerRequestInfo request; - if (!ParseHeaders(&request)) - break; - - if (is_web_socket_) { - delegate_->OnWebSocketMessage(this, request.data); - continue; - } - - std::string connection = GetHeaderValue(request, "Connection"); - if (connection == "Upgrade") { - // Is this WebSocket and if yes, upgrade the connection. - std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1"); - std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2"); - if (!key1.empty() && !key2.empty()) { - delegate_->OnWebSocketRequest(this, request); - continue; - } - } - delegate_->OnHttpRequest(this, request); - } -} - -void HttpListenSocket::DidClose(ListenSocket* sock) { - delegate_->OnClose(this); - sock->Release(); -} diff --git a/net/server/http_listen_socket.h b/net/server/http_listen_socket.h deleted file mode 100644 index 8b0bbc2..0000000 --- a/net/server/http_listen_socket.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2010 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. - -#ifndef NET_SERVER_HTTP_LISTEN_SOCKET_H_ -#define NET_SERVER_HTTP_LISTEN_SOCKET_H_ -#pragma once - -#include "net/base/listen_socket.h" - -class HttpServerRequestInfo; - -// Implements a simple HTTP listen socket on top of the raw socket interface. -class HttpListenSocket : public ListenSocket, - public ListenSocket::ListenSocketDelegate { - public: - class Delegate { - public: - virtual void OnHttpRequest(HttpListenSocket* socket, - const HttpServerRequestInfo& info) = 0; - - virtual void OnWebSocketRequest(HttpListenSocket* socket, - const HttpServerRequestInfo& info) = 0; - - virtual void OnWebSocketMessage(HttpListenSocket* socket, - const std::string& data) = 0; - - virtual void OnClose(HttpListenSocket* socket) = 0; - protected: - virtual ~Delegate() {} - }; - - virtual void Accept(); - - static HttpListenSocket* Listen(const std::string& ip, - int port, - HttpListenSocket::Delegate* delegate); - - void AcceptWebSocket(const HttpServerRequestInfo& request); - - void SendOverWebSocket(const std::string& data); - - void Send200(const std::string& data, const std::string& mime_type); - void Send404(); - void Send500(const std::string& message); - - virtual void Close(); - virtual void Listen(); - - // ListenSocketDelegate - virtual void DidAccept(ListenSocket* server, ListenSocket* connection); - virtual void DidRead(ListenSocket* connection, const char* data, int len); - virtual void DidClose(ListenSocket* sock); - - private: - static const int kReadBufSize = 16 * 1024; - HttpListenSocket(SOCKET s, HttpListenSocket::Delegate* del); - virtual ~HttpListenSocket(); - - // Expects the raw data to be stored in recv_data_. If parsing is successful, - // will remove the data parsed from recv_data_, leaving only the unused - // recv data. - bool ParseHeaders(HttpServerRequestInfo* info); - - HttpListenSocket::Delegate* delegate_; - bool is_web_socket_; - std::string recv_data_; - - DISALLOW_COPY_AND_ASSIGN(HttpListenSocket); -}; - -#endif // NET_SERVER_HTTP_LISTEN_SOCKET_H_ diff --git a/net/server/http_server.cc b/net/server/http_server.cc new file mode 100644 index 0000000..87b50fa --- /dev/null +++ b/net/server/http_server.cc @@ -0,0 +1,416 @@ +// Copyright (c) 2010 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 + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/md5.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "net/server/http_server.h" +#include "net/server/http_server_request_info.h" + +int HttpServer::Connection::lastId_ = 0; +HttpServer::HttpServer(const std::string& host, + int port, + HttpServer::Delegate* del) + : delegate_(del) { + server_ = ListenSocket::Listen(host, port, this); +} + +HttpServer::~HttpServer() { + IdToConnectionMap copy = id_to_connection_; + for (IdToConnectionMap::iterator it = copy.begin(); it != copy.end(); ++it) + delete it->second; + + server_ = NULL; +} + +std::string GetHeaderValue( + const HttpServerRequestInfo& request, + const std::string& header_name) { + HttpServerRequestInfo::HeadersMap::iterator it = + request.headers.find(header_name); + if (it != request.headers.end()) + return it->second; + return ""; +} + +uint32 WebSocketKeyFingerprint(const std::string& str) { + std::string result; + const char* pChar = str.c_str(); + int length = str.length(); + int spaces = 0; + for (int i = 0; i < length; ++i) { + if (pChar[i] >= '0' && pChar[i] <= '9') + result.append(&pChar[i], 1); + else if (pChar[i] == ' ') + spaces++; + } + if (spaces == 0) + return 0; + int64 number = 0; + if (!base::StringToInt64(result, &number)) + return 0; + return htonl(static_cast(number / spaces)); +} + +void HttpServer::AcceptWebSocket( + int connection_id, + const HttpServerRequestInfo& request) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1"); + std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2"); + + uint32 fp1 = WebSocketKeyFingerprint(key1); + uint32 fp2 = WebSocketKeyFingerprint(key2); + + char data[16]; + memcpy(data, &fp1, 4); + memcpy(data + 4, &fp2, 4); + memcpy(data + 8, &request.data[0], 8); + + MD5Digest digest; + MD5Sum(data, 16, &digest); + + std::string origin = GetHeaderValue(request, "Origin"); + std::string host = GetHeaderValue(request, "Host"); + std::string location = "ws://" + host + request.path; + connection->is_web_socket_ = true; + connection->socket_->Send(base::StringPrintf( + "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Origin: %s\r\n" + "Sec-WebSocket-Location: %s\r\n" + "\r\n", + origin.c_str(), + location.c_str())); + connection->socket_->Send(reinterpret_cast(digest.a), 16); +} + +void HttpServer::SendOverWebSocket(int connection_id, + const std::string& data) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + DCHECK(connection->is_web_socket_); + char message_start = 0; + char message_end = -1; + connection->socket_->Send(&message_start, 1); + connection->socket_->Send(data); + connection->socket_->Send(&message_end, 1); +} + +void HttpServer::Send(int connection_id, const std::string& data) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + connection->socket_->Send(data); +} + +void HttpServer::Send(int connection_id, const char* bytes, int len) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + connection->socket_->Send(bytes, len); +} + +void HttpServer::Send200(int connection_id, + const std::string& data, + const std::string& content_type) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + connection->socket_->Send(base::StringPrintf( + "HTTP/1.1 200 OK\r\n" + "Content-Type:%s\r\n" + "Content-Length:%d\r\n" + "\r\n", + content_type.c_str(), + static_cast(data.length()))); + connection->socket_->Send(data); +} + +void HttpServer::Send404(int connection_id) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + connection->socket_->Send( + "HTTP/1.1 404 Not Found\r\n" + "Content-Length: 0\r\n" + "\r\n"); +} + +void HttpServer::Send500(int connection_id, const std::string& message) { + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + connection->socket_->Send(base::StringPrintf( + "HTTP/1.1 500 Internal Error\r\n" + "Content-Type:text/html\r\n" + "Content-Length:%d\r\n" + "\r\n" + "%s", + static_cast(message.length()), + message.c_str())); +} + +void HttpServer::Close(int connection_id) +{ + Connection* connection = FindConnection(connection_id); + if (connection == NULL) + return; + + connection->DetachSocket(); +} + +HttpServer::Connection::Connection(ListenSocket* sock) + : socket_(sock), + is_web_socket_(false) { + id_ = lastId_++; +} + +HttpServer::Connection::~Connection() { + DetachSocket(); +} + +void HttpServer::Connection::DetachSocket() { + socket_ = NULL; +} + +// +// 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_SPACE, + INPUT_CR, + INPUT_LF, + INPUT_COLON, + INPUT_00, + INPUT_FF, + 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_WS_READY, // Ready to receive web socket frame + ST_WS_FRAME, // Receiving WebSocket frame + ST_WS_CLOSE, // Closing the connection WebSocket connection + 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_ERR, ST_ERR, ST_METHOD }, +/* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_ERR, ST_ERR, ST_URL }, +/* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_PROTO }, +/* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_ERR }, +/* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_NAME }, +/* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_VALUE }, +/* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_ERR, ST_ERR, ST_VALUE }, +/* WS_READY */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_FRAME, ST_WS_CLOSE, ST_ERR}, +/* WS_FRAME */ { ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_ERR, ST_WS_READY, ST_WS_FRAME }, +/* WS_CLOSE */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_CLOSE, ST_ERR, ST_ERR }, +/* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE }, +/* ERR */ { ST_ERR, ST_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 ' ': + return INPUT_SPACE; + case '\r': + return INPUT_CR; + case '\n': + return INPUT_LF; + case ':': + return INPUT_COLON; + case 0x0: + return INPUT_00; + case static_cast(-1): + return INPUT_FF; + } + return INPUT_DEFAULT; +} + +bool HttpServer::ParseHeaders(Connection* connection, + HttpServerRequestInfo* info) { + int pos = 0; + int data_len = connection->recv_data_.length(); + int state = connection->is_web_socket_ ? ST_WS_READY : ST_METHOD; + std::string buffer; + std::string header_name; + std::string header_value; + while (pos < data_len) { + char ch = connection->recv_data_[pos++]; + int input = charToInput(ch); + int next_state = parser_state[state][input]; + + bool transition = (next_state != state); + 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 = buffer; + buffer.clear(); + break; + case ST_VALUE: + header_value = buffer; + // TODO(mbelshe): Deal better with duplicate headers + DCHECK(info->headers.find(header_name) == info->headers.end()); + info->headers[header_name] = header_value; + buffer.clear(); + break; + case ST_SEPARATOR: + buffer.append(&ch, 1); + break; + case ST_WS_FRAME: + connection->recv_data_ = connection->recv_data_.substr(pos); + info->data = buffer; + buffer.clear(); + return true; + 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: + case ST_WS_FRAME: + buffer.append(&ch, 1); + break; + case ST_DONE: + connection->recv_data_ = connection->recv_data_.substr(pos); + info->data = connection->recv_data_; + connection->recv_data_.clear(); + return true; + case ST_WS_CLOSE: + connection->is_web_socket_ = false; + return false; + case ST_ERR: + return false; + } + } + } + // No more characters, but we haven't finished parsing yet. + return false; +} + +void HttpServer::DidAccept(ListenSocket* server, + ListenSocket* socket) { + Connection* connection = new Connection(socket); + id_to_connection_[connection->id_] = connection; + socket_to_connection_[socket] = connection; +} + +void HttpServer::DidRead(ListenSocket* socket, + const char* data, + int len) { + Connection* connection = FindConnection(socket); + DCHECK(connection != NULL); + if (connection == NULL) + return; + + connection->recv_data_.append(data, len); + while (connection->recv_data_.length()) { + HttpServerRequestInfo request; + if (!ParseHeaders(connection, &request)) + break; + + if (connection->is_web_socket_) { + delegate_->OnWebSocketMessage(connection->id_, request.data); + continue; + } + + std::string connection_header = GetHeaderValue(request, "Connection"); + if (connection_header == "Upgrade") { + // Is this WebSocket and if yes, upgrade the connection. + std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1"); + std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2"); + if (!key1.empty() && !key2.empty()) { + delegate_->OnWebSocketRequest(connection->id_, request); + continue; + } + } + delegate_->OnHttpRequest(connection->id_, request); + } + +} + +void HttpServer::DidClose(ListenSocket* socket) { + Connection* connection = FindConnection(socket); + DCHECK(connection != NULL); + delegate_->OnClose(connection->id_); + id_to_connection_.erase(connection->id_); + socket_to_connection_.erase(connection->socket_); + delete connection; +} + +HttpServer::Connection* 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; +} + +HttpServer::Connection* HttpServer::FindConnection(ListenSocket* socket) { + SocketToConnectionMap::iterator it = socket_to_connection_.find(socket); + if (it == socket_to_connection_.end()) + return NULL; + return it->second; +} diff --git a/net/server/http_server.h b/net/server/http_server.h new file mode 100644 index 0000000..fc87cdb --- /dev/null +++ b/net/server/http_server.h @@ -0,0 +1,96 @@ +// Copyright (c) 2010 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. + +#ifndef NET_SERVER_HTTP_SERVER_H_ +#define NET_SERVER_HTTP_SERVER_H_ +#pragma once + +#include +#include + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "net/base/listen_socket.h" + +class HttpServerRequestInfo; + +class HttpServer : public ListenSocket::ListenSocketDelegate, + public base::RefCountedThreadSafe { + public: + class Delegate { + public: + virtual void OnHttpRequest(int connection_id, + const HttpServerRequestInfo& info) = 0; + + virtual void OnWebSocketRequest(int connection_id, + const HttpServerRequestInfo& info) = 0; + + virtual void OnWebSocketMessage(int connection_id, + const std::string& data) = 0; + + virtual void OnClose(int connection_id) = 0; + protected: + virtual ~Delegate() {} + }; + + HttpServer(const std::string& host, int port, HttpServer::Delegate* del); + virtual ~HttpServer(); + + void AcceptWebSocket(int connection_id, + const HttpServerRequestInfo& request); + void SendOverWebSocket(int connection_id, const std::string& data); + void Send(int connection_id, const std::string& data); + void Send(int connection_id, const char* bytes, int len); + void Send200(int connection_id, + const std::string& data, + const std::string& mime_type); + void Send404(int connection_id); + void Send500(int connection_id, const std::string& message); + void Close(int connection_id); + +private: + friend class base::RefCountedThreadSafe; + class Connection { + private: + static int lastId_; + friend class HttpServer; + + explicit Connection(ListenSocket* sock); + ~Connection(); + + void DetachSocket(); + + scoped_refptr socket_; + bool is_web_socket_; + std::string recv_data_; + int id_; + + DISALLOW_COPY_AND_ASSIGN(Connection); + }; + + + // ListenSocketDelegate + virtual void DidAccept(ListenSocket* server, ListenSocket* socket); + virtual void DidRead(ListenSocket* socket, const char* data, int len); + virtual void DidClose(ListenSocket* socket); + + // Expects the raw data to be stored in recv_data_. If parsing is successful, + // will remove the data parsed from recv_data_, leaving only the unused + // recv data. + bool ParseHeaders(Connection* connection, HttpServerRequestInfo* info); + + Connection* FindConnection(int connection_id); + Connection* FindConnection(ListenSocket* socket); + + HttpServer::Delegate* delegate_; + scoped_refptr server_; + typedef std::map IdToConnectionMap; + IdToConnectionMap id_to_connection_; + typedef std::map SocketToConnectionMap; + SocketToConnectionMap socket_to_connection_; + + DISALLOW_COPY_AND_ASSIGN(HttpServer); +}; + +#endif // NET_SERVER_HTTP_SERVER_H_ -- cgit v1.1