Chromium Code Reviews| Index: net/server/http_server.cc |
| diff --git a/net/server/http_server.cc b/net/server/http_server.cc |
| index 061731a9d2b1b5ae7260d68ed1df49783645eab6..7bdb43b9a125c8284df9693c6b8aed9dad1300d1 100644 |
| --- a/net/server/http_server.cc |
| +++ b/net/server/http_server.cc |
| @@ -4,14 +4,17 @@ |
| #include "net/server/http_server.h" |
| +#include "base/base64.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/md5.h" |
| +#include "base/sha1.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "build/build_config.h" |
| #include "net/server/http_server_request_info.h" |
| +#include "net/server/web_socket_frame.h" |
| #if defined(OS_WIN) |
| #include <winsock2.h> |
| @@ -67,13 +70,9 @@ uint32 WebSocketKeyFingerprint(const std::string& str) { |
| return htonl(static_cast<uint32>(number / spaces)); |
| } |
| -void HttpServer::AcceptWebSocket( |
| - int connection_id, |
| +void HttpServer::AcceptWebSocketHixie76( |
| + HttpServer::Connection* connection, |
| 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"); |
| @@ -104,13 +103,8 @@ void HttpServer::AcceptWebSocket( |
| connection->socket_->Send(reinterpret_cast<char*>(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_); |
| +void HttpServer::SendOverWebSocketHixie76(HttpServer::Connection* connection, |
| + const std::string& data) { |
| char message_start = 0; |
| char message_end = -1; |
| connection->socket_->Send(&message_start, 1); |
| @@ -189,7 +183,8 @@ void HttpServer::Close(int connection_id) |
| HttpServer::Connection::Connection(HttpServer* server, ListenSocket* sock) |
| : server_(server), |
| socket_(sock), |
| - is_web_socket_(false) { |
| + is_web_socket_(false), |
| + use_hixie76_web_socket_(false) { |
| id_ = lastId_++; |
| } |
| @@ -376,24 +371,54 @@ void HttpServer::DidRead(ListenSocket* socket, |
| connection->recv_data_.append(data, len); |
| while (connection->recv_data_.length()) { |
| int pos = 0; |
| - HttpServerRequestInfo request; |
| - if (!ParseHeaders(connection, &request, &pos)) |
| - break; |
| - |
| if (connection->is_web_socket_) { |
| - delegate_->OnWebSocketMessage(connection->id_, request.data); |
| - connection->Shift(pos); |
| + if (connection->use_hixie76_web_socket_) { |
| + HttpServerRequestInfo request; |
| + if (!ParseHeaders(connection, &request, &pos)) |
|
pfeldman
2011/07/28 14:54:18
Please add FIXME suggesting that hixie76 frames ar
|
| + break; |
| + delegate_->OnWebSocketMessage(connection->id_, request.data); |
| + connection->Shift(pos); |
| + } else { |
| + std::string message; |
| + if (!DidReadWebSocketHyBi10( |
| + connection, |
| + (char*)(connection->recv_data_.c_str() + pos), |
| + connection->recv_data_.length() - pos, |
| + &message)) |
| + break; |
| + delegate_->OnWebSocketMessage(connection->id_, message); |
| + } |
| continue; |
| } |
| + HttpServerRequestInfo request; |
| + if (!ParseHeaders(connection, &request, &pos)) |
| + break; |
| + |
| std::string connection_header = GetHeaderValue(request, "Connection"); |
| if (connection_header == "Upgrade") { |
| + std::string version = GetHeaderValue(request, "Sec-WebSocket-Version"); |
| + if (version.length() && version == "8") { |
| + std::string key = GetHeaderValue(request, "Sec-WebSocket-Key"); |
| + if (key.empty()) { |
| + Send500(connection->id_, |
| + "Invalid request format. " |
| + "Sec-WebSocket-Key is empty or isn't specified."); |
| + connection->Shift(pos); |
| + break; |
| + } |
| + |
| + delegate_->OnWebSocketRequest(connection->id_, request); |
| + connection->Shift(pos); |
| + continue; |
| + } |
| + |
| // 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"); |
| - const int websocket_handshake_body_len = 8; |
| - if (pos + websocket_handshake_body_len > |
| + const int web_socket_handshake_body_len = 8; |
| + if (pos + web_socket_handshake_body_len > |
| static_cast<int>(connection->recv_data_.length())) { |
| // We haven't received websocket handshake body yet. Wait. |
| break; |
| @@ -402,8 +427,9 @@ void HttpServer::DidRead(ListenSocket* socket, |
| if (!key1.empty() && !key2.empty()) { |
| request.data = connection->recv_data_.substr( |
| pos, |
| - pos + websocket_handshake_body_len); |
| - pos += websocket_handshake_body_len; |
| + pos + web_socket_handshake_body_len); |
| + pos += web_socket_handshake_body_len; |
| + connection->use_hixie76_web_socket_ = true; |
| delegate_->OnWebSocketRequest(connection->id_, request); |
| connection->Shift(pos); |
| continue; |
| @@ -437,4 +463,84 @@ HttpServer::Connection* HttpServer::FindConnection(ListenSocket* socket) { |
| return it->second; |
| } |
| +void HttpServer::AcceptWebSocket( |
| + int connection_id, |
| + const HttpServerRequestInfo& request) { |
| + Connection* connection = FindConnection(connection_id); |
| + if (connection == NULL) |
| + return; |
| + if (connection->use_hixie76_web_socket_) |
| + AcceptWebSocketHixie76(connection, request); |
| + else |
| + AcceptWebSocketHyBi10(connection, request); |
| +} |
| + |
| +static std::string GenerateWebSocketHyBi10AcceptKey( |
| + const std::string& sec_web_socket_key) |
| +{ |
| + static const char* const web_socket_key_GUID = |
| + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
| + std::string data = base::StringPrintf("%s%s", |
| + sec_web_socket_key.c_str(), |
| + web_socket_key_GUID); |
| + std::string encoded_hash; |
| + base::Base64Encode(base::SHA1HashString(data), &encoded_hash); |
| + return encoded_hash; |
| +} |
| + |
| +void HttpServer::AcceptWebSocketHyBi10( |
| + HttpServer::Connection* connection, |
| + const HttpServerRequestInfo& request) { |
| + std::string key = GetHeaderValue(request, "Sec-WebSocket-Key"); |
| + connection->is_web_socket_ = true; |
| + |
| + std::string response = base::StringPrintf( |
| + "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" |
|
pfeldman
2011/07/28 14:54:18
wrong indent
|
| + "Upgrade: WebSocket\r\n" |
| + "Connection: Upgrade\r\n" |
| + "Sec-WebSocket-Accept: %s\r\n" |
| + "\r\n", |
| + GenerateWebSocketHyBi10AcceptKey(key).c_str()); |
| + connection->socket_->Send(response); |
| +} |
| + |
| +void HttpServer::SendOverWebSocket(int connection_id, |
| + const std::string& data) { |
| + Connection* connection = FindConnection(connection_id); |
| + if (connection == NULL) |
| + return; |
| + DCHECK(connection->is_web_socket_); |
| + if (connection->use_hixie76_web_socket_) |
|
pfeldman
2011/07/28 14:54:18
Having two web socket implementations with the sam
|
| + SendOverWebSocketHixie76(connection, data); |
| + else |
| + SendOverWebSocketHyBi10(connection, data); |
| +} |
| + |
| +void HttpServer::SendOverWebSocketHyBi10(HttpServer::Connection* connection, |
| + const std::string& data) { |
| + WebSocketFrame frame; |
| + std::vector<char> buffer = frame.EncodeFrame(WebSocketFrame::kOpCodeText, |
| + data.c_str(), data.length()); |
| + connection->socket_->Send(&buffer[0], buffer.size()); |
| +} |
| + |
| +bool HttpServer::DidReadWebSocketHyBi10(HttpServer::Connection* connection, |
| + char* data, |
|
pfeldman
2011/07/28 14:54:18
poor indentation.
|
| + size_t length, |
| + std::string* message) { |
| + WebSocketFrame frame; |
| + WebSocketFrame::ParseResult parse_result = frame.DecodeFrame(data, length); |
| + if (parse_result == WebSocketFrame::FrameError) |
| + return false; |
| + |
| + if (parse_result == WebSocketFrame::FrameIncomplete) |
| + return false; |
| + |
| + CHECK(parse_result == WebSocketFrame::FrameOK); |
| + *message = std::string(frame.payload_, frame.payload_length_); |
| + connection->Shift(frame.frame_end_ - data); |
| + |
| + return true; |
| +} |
| + |
| } // namespace net |