summaryrefslogtreecommitdiff
path: root/chromium/net/server
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/server
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/net/server')
-rw-r--r--chromium/net/server/http_connection.cc51
-rw-r--r--chromium/net/server/http_connection.h53
-rw-r--r--chromium/net/server/http_server.cc330
-rw-r--r--chromium/net/server/http_server.h103
-rw-r--r--chromium/net/server/http_server_request_info.cc25
-rw-r--r--chromium/net/server/http_server_request_info.h43
-rw-r--r--chromium/net/server/http_server_response_info.cc67
-rw-r--r--chromium/net/server/http_server_response_info.h46
-rw-r--r--chromium/net/server/http_server_response_info_unittest.cc51
-rw-r--r--chromium/net/server/http_server_unittest.cc319
-rw-r--r--chromium/net/server/web_socket.cc406
-rw-r--r--chromium/net/server/web_socket.h53
12 files changed, 1547 insertions, 0 deletions
diff --git a/chromium/net/server/http_connection.cc b/chromium/net/server/http_connection.cc
new file mode 100644
index 00000000000..d964cb0738b
--- /dev/null
+++ b/chromium/net/server/http_connection.cc
@@ -0,0 +1,51 @@
+// 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_connection.h"
+
+#include "net/server/http_server.h"
+#include "net/server/http_server_response_info.h"
+#include "net/server/web_socket.h"
+#include "net/socket/stream_listen_socket.h"
+
+namespace net {
+
+int HttpConnection::last_id_ = 0;
+
+void HttpConnection::Send(const std::string& data) {
+ if (!socket_.get())
+ return;
+ socket_->Send(data);
+}
+
+void HttpConnection::Send(const char* bytes, int len) {
+ if (!socket_.get())
+ return;
+ socket_->Send(bytes, len);
+}
+
+void HttpConnection::Send(const HttpServerResponseInfo& response) {
+ Send(response.Serialize());
+}
+
+HttpConnection::HttpConnection(HttpServer* server, StreamListenSocket* sock)
+ : server_(server),
+ socket_(sock) {
+ id_ = last_id_++;
+}
+
+HttpConnection::~HttpConnection() {
+ DetachSocket();
+ server_->delegate_->OnClose(id_);
+}
+
+void HttpConnection::DetachSocket() {
+ socket_ = NULL;
+}
+
+void HttpConnection::Shift(int num_bytes) {
+ recv_data_ = recv_data_.substr(num_bytes);
+}
+
+} // namespace net
diff --git a/chromium/net/server/http_connection.h b/chromium/net/server/http_connection.h
new file mode 100644
index 00000000000..b0e37663d4f
--- /dev/null
+++ b/chromium/net/server/http_connection.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef NET_SERVER_HTTP_CONNECTION_H_
+#define NET_SERVER_HTTP_CONNECTION_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/http/http_status_code.h"
+
+namespace net {
+
+class HttpServer;
+class HttpServerResponseInfo;
+class StreamListenSocket;
+class WebSocket;
+
+class HttpConnection {
+ public:
+ ~HttpConnection();
+
+ void Send(const std::string& data);
+ void Send(const char* bytes, int len);
+ void Send(const HttpServerResponseInfo& response);
+
+ void Shift(int num_bytes);
+
+ const std::string& recv_data() const { return recv_data_; }
+ int id() const { return id_; }
+
+ private:
+ friend class HttpServer;
+ static int last_id_;
+
+ HttpConnection(HttpServer* server, StreamListenSocket* sock);
+
+ void DetachSocket();
+
+ HttpServer* server_;
+ scoped_refptr<StreamListenSocket> socket_;
+ scoped_ptr<WebSocket> web_socket_;
+ std::string recv_data_;
+ int id_;
+ DISALLOW_COPY_AND_ASSIGN(HttpConnection);
+};
+
+} // namespace net
+
+#endif // NET_SERVER_HTTP_CONNECTION_H_
diff --git a/chromium/net/server/http_server.cc b/chromium/net/server/http_server.cc
new file mode 100644
index 00000000000..373025c4aa6
--- /dev/null
+++ b/chromium/net/server/http_server.cc
@@ -0,0 +1,330 @@
+// 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/compiler_specific.h"
+#include "base/logging.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/tcp_listen_socket.h"
+
+namespace net {
+
+HttpServer::HttpServer(const StreamListenSocketFactory& factory,
+ HttpServer::Delegate* delegate)
+ : delegate_(delegate),
+ server_(factory.CreateAndListen(this)) {
+}
+
+void HttpServer::AcceptWebSocket(
+ int connection_id,
+ const HttpServerRequestInfo& request) {
+ HttpConnection* connection = FindConnection(connection_id);
+ if (connection == NULL)
+ return;
+
+ DCHECK(connection->web_socket_.get());
+ 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_.get());
+ connection->web_socket_->Send(data);
+}
+
+void HttpServer::SendResponse(int connection_id,
+ const HttpServerResponseInfo& response) {
+ HttpConnection* connection = FindConnection(connection_id);
+ if (connection == NULL)
+ return;
+ connection->Send(response);
+}
+
+void HttpServer::Send(int connection_id,
+ HttpStatusCode status_code,
+ const std::string& data,
+ const std::string& content_type) {
+ HttpServerResponseInfo response(status_code);
+ response.SetBody(data, content_type);
+ SendResponse(connection_id, response);
+}
+
+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;
+
+ // Initiating close from server-side does not lead to the DidClose call.
+ // Do it manually here.
+ DidClose(connection->socket_.get());
+}
+
+int HttpServer::GetLocalAddress(IPEndPoint* address) {
+ if (!server_)
+ return ERR_SOCKET_NOT_CONNECTED;
+ return server_->GetLocalAddress(address);
+}
+
+void HttpServer::DidAccept(StreamListenSocket* server,
+ StreamListenSocket* socket) {
+ HttpConnection* connection = new HttpConnection(this, socket);
+ id_to_connection_[connection->id()] = connection;
+ socket_to_connection_[socket] = connection;
+}
+
+void HttpServer::DidRead(StreamListenSocket* socket,
+ const char* data,
+ int len) {
+ HttpConnection* connection = FindConnection(socket);
+ DCHECK(connection != NULL);
+ if (connection == NULL)
+ return;
+
+ connection->recv_data_.append(data, len);
+ while (connection->recv_data_.length()) {
+ if (connection->web_socket_.get()) {
+ 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());
+ break;
+ }
+ delegate_->OnWebSocketMessage(connection->id(), message);
+ continue;
+ }
+
+ HttpServerRequestInfo request;
+ size_t pos = 0;
+ if (!ParseHeaders(connection, &request, &pos))
+ break;
+
+ std::string connection_header = request.GetHeaderValue("connection");
+ if (connection_header == "Upgrade") {
+ connection->web_socket_.reset(WebSocket::CreateWebSocket(connection,
+ request,
+ &pos));
+
+ if (!connection->web_socket_.get()) // Not enough data was received.
+ break;
+ delegate_->OnWebSocketRequest(connection->id(), request);
+ connection->Shift(pos);
+ continue;
+ }
+
+ const char kContentLength[] = "content-length";
+ if (request.headers.count(kContentLength)) {
+ 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;
+ }
+
+ if (connection->recv_data_.length() - pos < content_length)
+ break; // Not enough data was received yet.
+ request.data = connection->recv_data_.substr(pos, content_length);
+ pos += content_length;
+ }
+
+ delegate_->OnHttpRequest(connection->id(), request);
+ connection->Shift(pos);
+ }
+}
+
+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;
+}
+
+HttpServer::~HttpServer() {
+ STLDeleteContainerPairSecondPointers(
+ id_to_connection_.begin(), id_to_connection_.end());
+ server_ = 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_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 ' ':
+ return INPUT_SPACE;
+ case '\r':
+ return INPUT_CR;
+ case '\n':
+ return INPUT_LF;
+ case ':':
+ return INPUT_COLON;
+ }
+ return INPUT_DEFAULT;
+}
+
+bool HttpServer::ParseHeaders(HttpConnection* connection,
+ 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++];
+ 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 = StringToLowerASCII(buffer);
+ buffer.clear();
+ break;
+ case ST_VALUE:
+ TrimWhitespaceASCII(buffer, TRIM_LEADING, &header_value);
+ // 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:
+ 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;
+}
+
+HttpConnection* HttpServer::FindConnection(StreamListenSocket* socket) {
+ SocketToConnectionMap::iterator it = socket_to_connection_.find(socket);
+ if (it == socket_to_connection_.end())
+ return NULL;
+ return it->second;
+}
+
+} // namespace net
diff --git a/chromium/net/server/http_server.h b/chromium/net/server/http_server.h
new file mode 100644
index 00000000000..f4345752e2d
--- /dev/null
+++ b/chromium/net/server/http_server.h
@@ -0,0 +1,103 @@
+// 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.
+
+#ifndef NET_SERVER_HTTP_SERVER_H_
+#define NET_SERVER_HTTP_SERVER_H_
+
+#include <list>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "net/http/http_status_code.h"
+#include "net/socket/stream_listen_socket.h"
+
+namespace net {
+
+class HttpConnection;
+class HttpServerRequestInfo;
+class HttpServerResponseInfo;
+class IPEndPoint;
+class WebSocket;
+
+class HttpServer : public StreamListenSocket::Delegate,
+ public base::RefCountedThreadSafe<HttpServer> {
+ 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 StreamListenSocketFactory& socket_factory,
+ HttpServer::Delegate* delegate);
+
+ void AcceptWebSocket(int connection_id,
+ const HttpServerRequestInfo& request);
+ void SendOverWebSocket(int connection_id, const std::string& data);
+ void SendResponse(int connection_id, const HttpServerResponseInfo& response);
+ void Send(int connection_id,
+ HttpStatusCode status_code,
+ const std::string& data,
+ const std::string& mime_type);
+ 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);
+
+ // Copies the local address to |address|. Returns a network error code.
+ int GetLocalAddress(IPEndPoint* address);
+
+ // ListenSocketDelegate
+ virtual void DidAccept(StreamListenSocket* server,
+ StreamListenSocket* socket) OVERRIDE;
+ virtual void DidRead(StreamListenSocket* socket,
+ const char* data,
+ int len) OVERRIDE;
+ virtual void DidClose(StreamListenSocket* socket) OVERRIDE;
+
+ protected:
+ virtual ~HttpServer();
+
+ private:
+ friend class base::RefCountedThreadSafe<HttpServer>;
+ friend class HttpConnection;
+
+ // 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(HttpConnection* connection,
+ HttpServerRequestInfo* info,
+ size_t* pos);
+
+ HttpConnection* FindConnection(int connection_id);
+ HttpConnection* FindConnection(StreamListenSocket* socket);
+
+ HttpServer::Delegate* delegate_;
+ scoped_refptr<StreamListenSocket> server_;
+ typedef std::map<int, HttpConnection*> IdToConnectionMap;
+ IdToConnectionMap id_to_connection_;
+ typedef std::map<StreamListenSocket*, HttpConnection*> SocketToConnectionMap;
+ SocketToConnectionMap socket_to_connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpServer);
+};
+
+} // namespace net
+
+#endif // NET_SERVER_HTTP_SERVER_H_
diff --git a/chromium/net/server/http_server_request_info.cc b/chromium/net/server/http_server_request_info.cc
new file mode 100644
index 00000000000..67965f29aa3
--- /dev/null
+++ b/chromium/net/server/http_server_request_info.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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_request_info.h"
+
+#include "base/strings/string_util.h"
+
+namespace net {
+
+HttpServerRequestInfo::HttpServerRequestInfo() {}
+
+HttpServerRequestInfo::~HttpServerRequestInfo() {}
+
+std::string HttpServerRequestInfo::GetHeaderValue(
+ const std::string& header_name) const {
+ DCHECK_EQ(StringToLowerASCII(header_name), header_name);
+ HttpServerRequestInfo::HeadersMap::const_iterator it =
+ headers.find(header_name);
+ if (it != headers.end())
+ return it->second;
+ return std::string();
+}
+
+} // namespace net
diff --git a/chromium/net/server/http_server_request_info.h b/chromium/net/server/http_server_request_info.h
new file mode 100644
index 00000000000..62824187901
--- /dev/null
+++ b/chromium/net/server/http_server_request_info.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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_REQUEST_INFO_H_
+#define NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
+
+#include <map>
+#include <string>
+
+namespace net {
+
+// Meta information about an HTTP request.
+// This is geared toward servers in that it keeps a map of the headers and
+// values rather than just a list of header strings (which net::HttpRequestInfo
+// does).
+class HttpServerRequestInfo {
+ public:
+ HttpServerRequestInfo();
+ ~HttpServerRequestInfo();
+
+ // Returns header value for given header name. |header_name| should be
+ // lower case.
+ std::string GetHeaderValue(const std::string& header_name) const;
+
+ // Request method.
+ std::string method;
+
+ // Request line.
+ std::string path;
+
+ // Request data.
+ std::string data;
+
+ // A map of the names -> values for HTTP headers. These should always
+ // contain lower case field names.
+ typedef std::map<std::string, std::string> HeadersMap;
+ mutable HeadersMap headers;
+};
+
+} // namespace net
+
+#endif // NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
diff --git a/chromium/net/server/http_server_response_info.cc b/chromium/net/server/http_server_response_info.cc
new file mode 100644
index 00000000000..e4c6043aa8d
--- /dev/null
+++ b/chromium/net/server/http_server_response_info.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 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_response_info.h"
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "net/http/http_request_headers.h"
+
+namespace net {
+
+HttpServerResponseInfo::HttpServerResponseInfo() : status_code_(HTTP_OK) {}
+
+HttpServerResponseInfo::HttpServerResponseInfo(HttpStatusCode status_code)
+ : status_code_(status_code) {}
+
+HttpServerResponseInfo::~HttpServerResponseInfo() {}
+
+// static
+HttpServerResponseInfo HttpServerResponseInfo::CreateFor404() {
+ HttpServerResponseInfo response(HTTP_NOT_FOUND);
+ response.SetBody(std::string(), "text/html");
+ return response;
+}
+
+// static
+HttpServerResponseInfo HttpServerResponseInfo::CreateFor500(
+ const std::string& body) {
+ HttpServerResponseInfo response(HTTP_INTERNAL_SERVER_ERROR);
+ response.SetBody(body, "text/html");
+ return response;
+}
+
+void HttpServerResponseInfo::AddHeader(const std::string& name,
+ const std::string& value) {
+ headers_.push_back(std::make_pair(name, value));
+}
+
+void HttpServerResponseInfo::SetBody(const std::string& body,
+ const std::string& content_type) {
+ DCHECK(body_.empty());
+ body_ = body;
+ AddHeader(HttpRequestHeaders::kContentLength,
+ base::StringPrintf("%" PRIuS, body.length()));
+ AddHeader(HttpRequestHeaders::kContentType, content_type);
+}
+
+std::string HttpServerResponseInfo::Serialize() const {
+ std::string response = base::StringPrintf(
+ "HTTP/1.1 %d %s\r\n", status_code_, GetHttpReasonPhrase(status_code_));
+ Headers::const_iterator header;
+ for (header = headers_.begin(); header != headers_.end(); ++header)
+ response += header->first + ":" + header->second + "\r\n";
+
+ return response + "\r\n" + body_;
+}
+
+HttpStatusCode HttpServerResponseInfo::status_code() const {
+ return status_code_;
+}
+
+const std::string& HttpServerResponseInfo::body() const {
+ return body_;
+}
+
+} // namespace net
diff --git a/chromium/net/server/http_server_response_info.h b/chromium/net/server/http_server_response_info.h
new file mode 100644
index 00000000000..d6cedaa8446
--- /dev/null
+++ b/chromium/net/server/http_server_response_info.h
@@ -0,0 +1,46 @@
+// Copyright 2013 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_RESPONSE_INFO_H_
+#define NET_SERVER_HTTP_SERVER_RESPONSE_INFO_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/http/http_status_code.h"
+
+namespace net {
+
+class HttpServerResponseInfo {
+ public:
+ // Creates a 200 OK HttpServerResponseInfo.
+ HttpServerResponseInfo();
+ explicit HttpServerResponseInfo(HttpStatusCode status_code);
+ ~HttpServerResponseInfo();
+
+ static HttpServerResponseInfo CreateFor404();
+ static HttpServerResponseInfo CreateFor500(const std::string& body);
+
+ void AddHeader(const std::string& name, const std::string& value);
+
+ // This also adds an appropriate Content-Length header.
+ void SetBody(const std::string& body, const std::string& content_type);
+
+ std::string Serialize() const;
+
+ HttpStatusCode status_code() const;
+ const std::string& body() const;
+
+ private:
+ typedef std::vector<std::pair<std::string, std::string> > Headers;
+
+ HttpStatusCode status_code_;
+ Headers headers_;
+ std::string body_;
+};
+
+} // namespace net
+
+#endif // NET_SERVER_HTTP_SERVER_RESPONSE_INFO_H_
diff --git a/chromium/net/server/http_server_response_info_unittest.cc b/chromium/net/server/http_server_response_info_unittest.cc
new file mode 100644
index 00000000000..f7b8e251d34
--- /dev/null
+++ b/chromium/net/server/http_server_response_info_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright 2013 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/http/http_status_code.h"
+#include "net/server/http_server_response_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(HttpServerResponseInfoTest, StatusLine) {
+ HttpServerResponseInfo response;
+ ASSERT_EQ(HTTP_OK, response.status_code());
+ ASSERT_EQ("HTTP/1.1 200 OK\r\n\r\n", response.Serialize());
+}
+
+TEST(HttpServerResponseInfoTest, Headers) {
+ HttpServerResponseInfo response;
+ response.AddHeader("A", "1");
+ response.AddHeader("A", "2");
+ ASSERT_EQ("HTTP/1.1 200 OK\r\nA:1\r\nA:2\r\n\r\n", response.Serialize());
+}
+
+TEST(HttpServerResponseInfoTest, Body) {
+ HttpServerResponseInfo response;
+ ASSERT_EQ(std::string(), response.body());
+ response.SetBody("body", "type");
+ ASSERT_EQ("body", response.body());
+ ASSERT_EQ(
+ "HTTP/1.1 200 OK\r\nContent-Length:4\r\nContent-Type:type\r\n\r\nbody",
+ response.Serialize());
+}
+
+TEST(HttpServerResponseInfoTest, CreateFor404) {
+ HttpServerResponseInfo response = HttpServerResponseInfo::CreateFor404();
+ ASSERT_EQ(
+ "HTTP/1.1 404 Not Found\r\n"
+ "Content-Length:0\r\nContent-Type:text/html\r\n\r\n",
+ response.Serialize());
+}
+
+TEST(HttpServerResponseInfoTest, CreateFor500) {
+ HttpServerResponseInfo response =
+ HttpServerResponseInfo::CreateFor500("mess");
+ ASSERT_EQ(
+ "HTTP/1.1 500 Internal Server Error\r\n"
+ "Content-Length:4\r\nContent-Type:text/html\r\n\r\nmess",
+ response.Serialize());
+}
+
+} // namespace net
diff --git a/chromium/net/server/http_server_unittest.cc b/chromium/net/server/http_server_unittest.cc
new file mode 100644
index 00000000000..48a2ce7571f
--- /dev/null
+++ b/chromium/net/server/http_server_unittest.cc
@@ -0,0 +1,319 @@
+// Copyright 2013 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 <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/format_macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "net/base/address_list.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/server/http_server.h"
+#include "net/server/http_server_request_info.h"
+#include "net/socket/tcp_client_socket.h"
+#include "net/socket/tcp_listen_socket.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+void SetTimedOutAndQuitLoop(const base::WeakPtr<bool> timed_out,
+ const base::Closure& quit_loop_func) {
+ if (timed_out) {
+ *timed_out = true;
+ quit_loop_func.Run();
+ }
+}
+
+bool RunLoopWithTimeout(base::RunLoop* run_loop) {
+ bool timed_out = false;
+ base::WeakPtrFactory<bool> timed_out_weak_factory(&timed_out);
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&SetTimedOutAndQuitLoop,
+ timed_out_weak_factory.GetWeakPtr(),
+ run_loop->QuitClosure()),
+ base::TimeDelta::FromSeconds(1));
+ run_loop->Run();
+ return !timed_out;
+}
+
+class TestHttpClient {
+ public:
+ TestHttpClient() : connect_result_(OK) {}
+
+ int ConnectAndWait(const IPEndPoint& address) {
+ AddressList addresses(address);
+ NetLog::Source source;
+ socket_.reset(new TCPClientSocket(addresses, NULL, source));
+
+ base::RunLoop run_loop;
+ connect_result_ = socket_->Connect(base::Bind(&TestHttpClient::OnConnect,
+ base::Unretained(this),
+ run_loop.QuitClosure()));
+ if (connect_result_ != OK && connect_result_ != ERR_IO_PENDING)
+ return connect_result_;
+
+ if (!RunLoopWithTimeout(&run_loop))
+ return ERR_TIMED_OUT;
+ return connect_result_;
+ }
+
+ void Send(const std::string& data) {
+ write_buffer_ =
+ new DrainableIOBuffer(new StringIOBuffer(data), data.length());
+ Write();
+ }
+
+ private:
+ void OnConnect(const base::Closure& quit_loop, int result) {
+ connect_result_ = result;
+ quit_loop.Run();
+ }
+
+ void Write() {
+ int result = socket_->Write(
+ write_buffer_.get(),
+ write_buffer_->BytesRemaining(),
+ base::Bind(&TestHttpClient::OnWrite, base::Unretained(this)));
+ if (result != ERR_IO_PENDING)
+ OnWrite(result);
+ }
+
+ void OnWrite(int result) {
+ ASSERT_GT(result, 0);
+ write_buffer_->DidConsume(result);
+ if (write_buffer_->BytesRemaining())
+ Write();
+ }
+
+ scoped_refptr<DrainableIOBuffer> write_buffer_;
+ scoped_ptr<TCPClientSocket> socket_;
+ int connect_result_;
+};
+
+} // namespace
+
+class HttpServerTest : public testing::Test,
+ public HttpServer::Delegate {
+ public:
+ HttpServerTest() : quit_after_request_count_(0) {}
+
+ virtual void SetUp() OVERRIDE {
+ TCPListenSocketFactory socket_factory("127.0.0.1", 0);
+ server_ = new HttpServer(socket_factory, this);
+ ASSERT_EQ(OK, server_->GetLocalAddress(&server_address_));
+ }
+
+ virtual void OnHttpRequest(int connection_id,
+ const HttpServerRequestInfo& info) OVERRIDE {
+ requests_.push_back(info);
+ if (requests_.size() == quit_after_request_count_)
+ run_loop_quit_func_.Run();
+ }
+
+ virtual void OnWebSocketRequest(int connection_id,
+ const HttpServerRequestInfo& info) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnWebSocketMessage(int connection_id,
+ const std::string& data) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnClose(int connection_id) OVERRIDE {}
+
+ bool RunUntilRequestsReceived(size_t count) {
+ quit_after_request_count_ = count;
+ if (requests_.size() == count)
+ return true;
+
+ base::RunLoop run_loop;
+ run_loop_quit_func_ = run_loop.QuitClosure();
+ bool success = RunLoopWithTimeout(&run_loop);
+ run_loop_quit_func_.Reset();
+ return success;
+ }
+
+ protected:
+ scoped_refptr<HttpServer> server_;
+ IPEndPoint server_address_;
+ base::Closure run_loop_quit_func_;
+ std::vector<HttpServerRequestInfo> requests_;
+
+ private:
+ size_t quit_after_request_count_;
+};
+
+TEST_F(HttpServerTest, Request) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ client.Send("GET /test HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ ASSERT_EQ("GET", requests_[0].method);
+ ASSERT_EQ("/test", requests_[0].path);
+ ASSERT_EQ("", requests_[0].data);
+ ASSERT_EQ(0u, requests_[0].headers.size());
+}
+
+TEST_F(HttpServerTest, RequestWithHeaders) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ const char* kHeaders[][3] = {
+ {"Header", ": ", "1"},
+ {"HeaderWithNoWhitespace", ":", "1"},
+ {"HeaderWithWhitespace", " : \t ", "1 1 1 \t "},
+ {"HeaderWithColon", ": ", "1:1"},
+ {"EmptyHeader", ":", ""},
+ {"EmptyHeaderWithWhitespace", ": \t ", ""},
+ {"HeaderWithNonASCII", ": ", "\u00f7"},
+ };
+ std::string headers;
+ for (size_t i = 0; i < arraysize(kHeaders); ++i) {
+ headers +=
+ std::string(kHeaders[i][0]) + kHeaders[i][1] + kHeaders[i][2] + "\r\n";
+ }
+
+ client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ ASSERT_EQ("", requests_[0].data);
+
+ for (size_t i = 0; i < arraysize(kHeaders); ++i) {
+ std::string field = StringToLowerASCII(std::string(kHeaders[i][0]));
+ std::string value = kHeaders[i][2];
+ ASSERT_EQ(1u, requests_[0].headers.count(field)) << field;
+ ASSERT_EQ(value, requests_[0].headers[field]) << kHeaders[i][0];
+ }
+}
+
+TEST_F(HttpServerTest, RequestWithBody) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ std::string body = "a" + std::string(1 << 10, 'b') + "c";
+ client.Send(base::StringPrintf(
+ "GET /test HTTP/1.1\r\n"
+ "SomeHeader: 1\r\n"
+ "Content-Length: %" PRIuS "\r\n\r\n%s",
+ body.length(),
+ body.c_str()));
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ ASSERT_EQ(2u, requests_[0].headers.size());
+ ASSERT_EQ(body.length(), requests_[0].data.length());
+ ASSERT_EQ('a', body[0]);
+ ASSERT_EQ('c', *body.rbegin());
+}
+
+TEST_F(HttpServerTest, RequestWithTooLargeBody) {
+ class TestURLFetcherDelegate : public URLFetcherDelegate {
+ public:
+ TestURLFetcherDelegate(const base::Closure& quit_loop_func)
+ : quit_loop_func_(quit_loop_func) {}
+ virtual ~TestURLFetcherDelegate() {}
+
+ virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE {
+ EXPECT_EQ(HTTP_INTERNAL_SERVER_ERROR, source->GetResponseCode());
+ quit_loop_func_.Run();
+ }
+
+ private:
+ base::Closure quit_loop_func_;
+ };
+
+ base::RunLoop run_loop;
+ TestURLFetcherDelegate delegate(run_loop.QuitClosure());
+
+ scoped_refptr<URLRequestContextGetter> request_context_getter(
+ new TestURLRequestContextGetter(base::MessageLoopProxy::current()));
+ scoped_ptr<URLFetcher> fetcher(
+ URLFetcher::Create(GURL(base::StringPrintf("http://127.0.0.1:%d/test",
+ server_address_.port())),
+ URLFetcher::GET,
+ &delegate));
+ fetcher->SetRequestContext(request_context_getter.get());
+ fetcher->AddExtraRequestHeader(
+ base::StringPrintf("content-length:%d", 1 << 30));
+ fetcher->Start();
+
+ ASSERT_TRUE(RunLoopWithTimeout(&run_loop));
+ ASSERT_EQ(0u, requests_.size());
+}
+
+namespace {
+
+class MockStreamListenSocket : public StreamListenSocket {
+ public:
+ MockStreamListenSocket(StreamListenSocket::Delegate* delegate)
+ : StreamListenSocket(kInvalidSocket, delegate) {}
+
+ virtual void Accept() OVERRIDE { NOTREACHED(); }
+
+ private:
+ virtual ~MockStreamListenSocket() {}
+};
+
+} // namespace
+
+TEST_F(HttpServerTest, RequestWithBodySplitAcrossPackets) {
+ scoped_refptr<StreamListenSocket> socket(
+ new MockStreamListenSocket(server_.get()));
+ server_->DidAccept(NULL, socket.get());
+ std::string body("body");
+ std::string request = base::StringPrintf(
+ "GET /test HTTP/1.1\r\n"
+ "SomeHeader: 1\r\n"
+ "Content-Length: %" PRIuS "\r\n\r\n%s",
+ body.length(),
+ body.c_str());
+ server_->DidRead(socket.get(), request.c_str(), request.length() - 2);
+ ASSERT_EQ(0u, requests_.size());
+ server_->DidRead(socket.get(), request.c_str() + request.length() - 2, 2);
+ ASSERT_EQ(1u, requests_.size());
+ ASSERT_EQ(body, requests_[0].data);
+}
+
+TEST_F(HttpServerTest, MultipleRequestsOnSameConnection) {
+ // The idea behind this test is that requests with or without bodies should
+ // not break parsing of the next request.
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ std::string body = "body";
+ client.Send(base::StringPrintf(
+ "GET /test HTTP/1.1\r\n"
+ "Content-Length: %" PRIuS "\r\n\r\n%s",
+ body.length(),
+ body.c_str()));
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ ASSERT_EQ(body, requests_[0].data);
+
+ client.Send("GET /test2 HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(2));
+ ASSERT_EQ("/test2", requests_[1].path);
+
+ client.Send("GET /test3 HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(3));
+ ASSERT_EQ("/test3", requests_[2].path);
+}
+
+} // namespace net
diff --git a/chromium/net/server/web_socket.cc b/chromium/net/server/web_socket.cc
new file mode 100644
index 00000000000..f06b425fee0
--- /dev/null
+++ b/chromium/net/server/web_socket.cc
@@ -0,0 +1,406 @@
+// 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/web_socket.h"
+
+#include <limits>
+
+#include "base/base64.h"
+#include "base/rand_util.h"
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_byteorder.h"
+#include "net/server/http_connection.h"
+#include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
+
+namespace net {
+
+namespace {
+
+static uint32 WebSocketKeyFingerprint(const std::string& str) {
+ std::string result;
+ const char* p_char = str.c_str();
+ int length = str.length();
+ int spaces = 0;
+ for (int i = 0; i < length; ++i) {
+ if (p_char[i] >= '0' && p_char[i] <= '9')
+ result.append(&p_char[i], 1);
+ else if (p_char[i] == ' ')
+ spaces++;
+ }
+ if (spaces == 0)
+ return 0;
+ int64 number = 0;
+ if (!base::StringToInt64(result, &number))
+ return 0;
+ return base::HostToNet32(static_cast<uint32>(number / spaces));
+}
+
+class WebSocketHixie76 : public net::WebSocket {
+ public:
+ static net::WebSocket* Create(HttpConnection* connection,
+ const HttpServerRequestInfo& request,
+ size_t* pos) {
+ if (connection->recv_data().length() < *pos + kWebSocketHandshakeBodyLen)
+ return NULL;
+ return new WebSocketHixie76(connection, request, pos);
+ }
+
+ virtual void Accept(const HttpServerRequestInfo& request) OVERRIDE {
+ std::string key1 = request.GetHeaderValue("sec-websocket-key1");
+ std::string key2 = request.GetHeaderValue("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, &key3_[0], 8);
+
+ base::MD5Digest digest;
+ base::MD5Sum(data, 16, &digest);
+
+ std::string origin = request.GetHeaderValue("origin");
+ std::string host = request.GetHeaderValue("host");
+ std::string location = "ws://" + host + request.path;
+ connection_->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_->Send(reinterpret_cast<char*>(digest.a), 16);
+ }
+
+ virtual ParseResult Read(std::string* message) OVERRIDE {
+ DCHECK(message);
+ const std::string& data = connection_->recv_data();
+ if (data[0])
+ return FRAME_ERROR;
+
+ size_t pos = data.find('\377', 1);
+ if (pos == std::string::npos)
+ return FRAME_INCOMPLETE;
+
+ std::string buffer(data.begin() + 1, data.begin() + pos);
+ message->swap(buffer);
+ connection_->Shift(pos + 1);
+
+ return FRAME_OK;
+ }
+
+ virtual void Send(const std::string& message) OVERRIDE {
+ char message_start = 0;
+ char message_end = -1;
+ connection_->Send(&message_start, 1);
+ connection_->Send(message);
+ connection_->Send(&message_end, 1);
+ }
+
+ private:
+ static const int kWebSocketHandshakeBodyLen;
+
+ WebSocketHixie76(HttpConnection* connection,
+ const HttpServerRequestInfo& request,
+ size_t* pos) : WebSocket(connection) {
+ std::string key1 = request.GetHeaderValue("sec-websocket-key1");
+ std::string key2 = request.GetHeaderValue("sec-websocket-key2");
+
+ if (key1.empty()) {
+ connection->Send(HttpServerResponseInfo::CreateFor500(
+ "Invalid request format. Sec-WebSocket-Key1 is empty or isn't "
+ "specified."));
+ return;
+ }
+
+ if (key2.empty()) {
+ connection->Send(HttpServerResponseInfo::CreateFor500(
+ "Invalid request format. Sec-WebSocket-Key2 is empty or isn't "
+ "specified."));
+ return;
+ }
+
+ key3_ = connection->recv_data().substr(
+ *pos,
+ *pos + kWebSocketHandshakeBodyLen);
+ *pos += kWebSocketHandshakeBodyLen;
+ }
+
+ std::string key3_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHixie76);
+};
+
+const int WebSocketHixie76::kWebSocketHandshakeBodyLen = 8;
+
+
+// Constants for hybi-10 frame format.
+
+typedef int OpCode;
+
+const OpCode kOpCodeContinuation = 0x0;
+const OpCode kOpCodeText = 0x1;
+const OpCode kOpCodeBinary = 0x2;
+const OpCode kOpCodeClose = 0x8;
+const OpCode kOpCodePing = 0x9;
+const OpCode kOpCodePong = 0xA;
+
+const unsigned char kFinalBit = 0x80;
+const unsigned char kReserved1Bit = 0x40;
+const unsigned char kReserved2Bit = 0x20;
+const unsigned char kReserved3Bit = 0x10;
+const unsigned char kOpCodeMask = 0xF;
+const unsigned char kMaskBit = 0x80;
+const unsigned char kPayloadLengthMask = 0x7F;
+
+const size_t kMaxSingleBytePayloadLength = 125;
+const size_t kTwoBytePayloadLengthField = 126;
+const size_t kEightBytePayloadLengthField = 127;
+const size_t kMaskingKeyWidthInBytes = 4;
+
+class WebSocketHybi17 : public WebSocket {
+ public:
+ static WebSocket* Create(HttpConnection* connection,
+ const HttpServerRequestInfo& request,
+ size_t* pos) {
+ std::string version = request.GetHeaderValue("sec-websocket-version");
+ if (version != "8" && version != "13")
+ return NULL;
+
+ std::string key = request.GetHeaderValue("sec-websocket-key");
+ if (key.empty()) {
+ connection->Send(HttpServerResponseInfo::CreateFor500(
+ "Invalid request format. Sec-WebSocket-Key is empty or isn't "
+ "specified."));
+ return NULL;
+ }
+ return new WebSocketHybi17(connection, request, pos);
+ }
+
+ virtual void Accept(const HttpServerRequestInfo& request) OVERRIDE {
+ static const char* const kWebSocketGuid =
+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ std::string key = request.GetHeaderValue("sec-websocket-key");
+ std::string data = base::StringPrintf("%s%s", key.c_str(), kWebSocketGuid);
+ std::string encoded_hash;
+ base::Base64Encode(base::SHA1HashString(data), &encoded_hash);
+
+ std::string response = base::StringPrintf(
+ "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n"
+ "\r\n",
+ encoded_hash.c_str());
+ connection_->Send(response);
+ }
+
+ virtual ParseResult Read(std::string* message) OVERRIDE {
+ const std::string& frame = connection_->recv_data();
+ int bytes_consumed = 0;
+
+ ParseResult result =
+ WebSocket::DecodeFrameHybi17(frame, true, &bytes_consumed, message);
+ if (result == FRAME_OK)
+ connection_->Shift(bytes_consumed);
+ if (result == FRAME_CLOSE)
+ closed_ = true;
+ return result;
+ }
+
+ virtual void Send(const std::string& message) OVERRIDE {
+ if (closed_)
+ return;
+ std::string data = WebSocket::EncodeFrameHybi17(message, 0);
+ connection_->Send(data);
+ }
+
+ private:
+ WebSocketHybi17(HttpConnection* connection,
+ const HttpServerRequestInfo& request,
+ size_t* pos)
+ : WebSocket(connection),
+ op_code_(0),
+ final_(false),
+ reserved1_(false),
+ reserved2_(false),
+ reserved3_(false),
+ masked_(false),
+ payload_(0),
+ payload_length_(0),
+ frame_end_(0),
+ closed_(false) {
+ }
+
+ OpCode op_code_;
+ bool final_;
+ bool reserved1_;
+ bool reserved2_;
+ bool reserved3_;
+ bool masked_;
+ const char* payload_;
+ size_t payload_length_;
+ const char* frame_end_;
+ bool closed_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHybi17);
+};
+
+} // anonymous namespace
+
+WebSocket* WebSocket::CreateWebSocket(HttpConnection* connection,
+ const HttpServerRequestInfo& request,
+ size_t* pos) {
+ WebSocket* socket = WebSocketHybi17::Create(connection, request, pos);
+ if (socket)
+ return socket;
+
+ return WebSocketHixie76::Create(connection, request, pos);
+}
+
+// static
+WebSocket::ParseResult WebSocket::DecodeFrameHybi17(const std::string& frame,
+ bool client_frame,
+ int* bytes_consumed,
+ std::string* output) {
+ size_t data_length = frame.length();
+ if (data_length < 2)
+ return FRAME_INCOMPLETE;
+
+ const char* buffer_begin = const_cast<char*>(frame.data());
+ const char* p = buffer_begin;
+ const char* buffer_end = p + data_length;
+
+ unsigned char first_byte = *p++;
+ unsigned char second_byte = *p++;
+
+ bool final = (first_byte & kFinalBit) != 0;
+ bool reserved1 = (first_byte & kReserved1Bit) != 0;
+ bool reserved2 = (first_byte & kReserved2Bit) != 0;
+ bool reserved3 = (first_byte & kReserved3Bit) != 0;
+ int op_code = first_byte & kOpCodeMask;
+ bool masked = (second_byte & kMaskBit) != 0;
+ if (!final || reserved1 || reserved2 || reserved3)
+ return FRAME_ERROR; // Extensions and not supported.
+
+ bool closed = false;
+ switch (op_code) {
+ case kOpCodeClose:
+ closed = true;
+ break;
+ case kOpCodeText:
+ break;
+ case kOpCodeBinary: // We don't support binary frames yet.
+ case kOpCodeContinuation: // We don't support binary frames yet.
+ case kOpCodePing: // We don't support binary frames yet.
+ case kOpCodePong: // We don't support binary frames yet.
+ default:
+ return FRAME_ERROR;
+ }
+
+ if (client_frame && !masked) // In Hybi-17 spec client MUST mask his frame.
+ return FRAME_ERROR;
+
+ uint64 payload_length64 = second_byte & kPayloadLengthMask;
+ if (payload_length64 > kMaxSingleBytePayloadLength) {
+ int extended_payload_length_size;
+ if (payload_length64 == kTwoBytePayloadLengthField)
+ extended_payload_length_size = 2;
+ else {
+ DCHECK(payload_length64 == kEightBytePayloadLengthField);
+ extended_payload_length_size = 8;
+ }
+ if (buffer_end - p < extended_payload_length_size)
+ return FRAME_INCOMPLETE;
+ payload_length64 = 0;
+ for (int i = 0; i < extended_payload_length_size; ++i) {
+ payload_length64 <<= 8;
+ payload_length64 |= static_cast<unsigned char>(*p++);
+ }
+ }
+
+ size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
+ static const uint64 max_payload_length = 0x7FFFFFFFFFFFFFFFull;
+ static size_t max_length = std::numeric_limits<size_t>::max();
+ if (payload_length64 > max_payload_length ||
+ payload_length64 + actual_masking_key_length > max_length) {
+ // WebSocket frame length too large.
+ return FRAME_ERROR;
+ }
+ size_t payload_length = static_cast<size_t>(payload_length64);
+
+ size_t total_length = actual_masking_key_length + payload_length;
+ if (static_cast<size_t>(buffer_end - p) < total_length)
+ return FRAME_INCOMPLETE;
+
+ if (masked) {
+ output->resize(payload_length);
+ const char* masking_key = p;
+ char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
+ for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
+ (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
+ } else {
+ std::string buffer(p, p + payload_length);
+ output->swap(buffer);
+ }
+
+ size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
+ *bytes_consumed = pos;
+ return closed ? FRAME_CLOSE : FRAME_OK;
+}
+
+// static
+std::string WebSocket::EncodeFrameHybi17(const std::string& message,
+ int masking_key) {
+ std::vector<char> frame;
+ OpCode op_code = kOpCodeText;
+ size_t data_length = message.length();
+
+ frame.push_back(kFinalBit | op_code);
+ char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
+ if (data_length <= kMaxSingleBytePayloadLength)
+ frame.push_back(data_length | mask_key_bit);
+ else if (data_length <= 0xFFFF) {
+ frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
+ frame.push_back((data_length & 0xFF00) >> 8);
+ frame.push_back(data_length & 0xFF);
+ } else {
+ frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
+ char extended_payload_length[8];
+ size_t remaining = data_length;
+ // Fill the length into extended_payload_length in the network byte order.
+ for (int i = 0; i < 8; ++i) {
+ extended_payload_length[7 - i] = remaining & 0xFF;
+ remaining >>= 8;
+ }
+ frame.insert(frame.end(),
+ extended_payload_length,
+ extended_payload_length + 8);
+ DCHECK(!remaining);
+ }
+
+ const char* data = const_cast<char*>(message.data());
+ if (masking_key != 0) {
+ const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
+ frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
+ for (size_t i = 0; i < data_length; ++i) // Mask the payload.
+ frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
+ } else {
+ frame.insert(frame.end(), data, data + data_length);
+ }
+ return std::string(&frame[0], frame.size());
+}
+
+WebSocket::WebSocket(HttpConnection* connection) : connection_(connection) {
+}
+
+} // namespace net
diff --git a/chromium/net/server/web_socket.h b/chromium/net/server/web_socket.h
new file mode 100644
index 00000000000..49ced84ee6d
--- /dev/null
+++ b/chromium/net/server/web_socket.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef NET_SERVER_WEB_SOCKET_H_
+#define NET_SERVER_WEB_SOCKET_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+class HttpConnection;
+class HttpServerRequestInfo;
+
+class WebSocket {
+ public:
+ enum ParseResult {
+ FRAME_OK,
+ FRAME_INCOMPLETE,
+ FRAME_CLOSE,
+ FRAME_ERROR
+ };
+
+ static WebSocket* CreateWebSocket(HttpConnection* connection,
+ const HttpServerRequestInfo& request,
+ size_t* pos);
+
+ static ParseResult DecodeFrameHybi17(const std::string& frame,
+ bool client_frame,
+ int* bytes_consumed,
+ std::string* output);
+
+ static std::string EncodeFrameHybi17(const std::string& data,
+ int masking_key);
+
+ virtual void Accept(const HttpServerRequestInfo& request) = 0;
+ virtual ParseResult Read(std::string* message) = 0;
+ virtual void Send(const std::string& message) = 0;
+ virtual ~WebSocket() {}
+
+ protected:
+ explicit WebSocket(HttpConnection* connection);
+ HttpConnection* connection_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebSocket);
+};
+
+} // namespace net
+
+#endif // NET_SERVER_WEB_SOCKET_H_