diff options
author | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-08 02:11:56 +0000 |
---|---|---|
committer | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-08 02:11:56 +0000 |
commit | 32bf3c4f2ab152883dfa86ca3597f557af84d289 (patch) | |
tree | a1248d4be2c8001853c35f975194543c96045d2a /net/websockets | |
parent | 95def6d076e66115ede482dc5926ed90a391d3d2 (diff) | |
download | chromium_src-32bf3c4f2ab152883dfa86ca3597f557af84d289.zip chromium_src-32bf3c4f2ab152883dfa86ca3597f557af84d289.tar.gz chromium_src-32bf3c4f2ab152883dfa86ca3597f557af84d289.tar.bz2 |
Implement WebSocketHandshake*Handler to support Http{Request,Response}Info.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/2579002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49137 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/websockets')
-rw-r--r-- | net/websockets/websocket_handshake_handler.cc | 103 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_handler.h | 10 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_handler_unittest.cc | 84 |
3 files changed, 161 insertions, 36 deletions
diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc index 8b1e071..7e5ec6f 100644 --- a/net/websockets/websocket_handshake_handler.cc +++ b/net/websockets/websocket_handshake_handler.cc @@ -4,6 +4,8 @@ #include "net/websockets/websocket_handshake_handler.h" +#include "base/md5.h" +#include "base/string_piece.h" #include "base/string_util.h" #include "googleurl/src/gurl.h" #include "net/http/http_util.h" @@ -23,8 +25,11 @@ void ParseHandshakeHeader( *headers = ""; return; } + // |status_line| includes \r\n. *status_line = std::string(handshake_message, i + 2); - *headers = std::string(handshake_message + i + 2, len - i - 2); + // |handshake_message| includes tailing \r\n\r\n. + // |headers| doesn't include 2nd \r\n. + *headers = std::string(handshake_message + i + 2, len - (i + 2) - 2); } void FetchHeaders(const std::string& headers, @@ -89,6 +94,38 @@ std::string FilterHeaders( return filtered_headers; } +// Gets a key number for |key_name| in |headers| and appends the number to +// |challenge|. +// The key number (/part_N/) is extracted as step 4.-8. in +// 5.2. Sending the server's opening handshake of +// http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt +void GetKeyNumber(net::HttpRequestHeaders* headers, const char* key_name, + std::string* challenge) { + std::string key; + headers->GetHeader(key_name, &key); + headers->RemoveHeader(key_name); + + uint32 key_number = 0; + uint32 spaces = 0; + for (size_t i = 0; i < key.size(); ++i) { + if (isdigit(key[i])) + key_number = key_number * 10 + key[i] - '0'; + else if (key[i] == ' ') + ++spaces; + } + // spaces should not be zero in valid handshake request. + if (spaces == 0) + return; + key_number /= spaces; + + char part[4]; + for (int i = 0; i < 4; i++) { + part[3 - i] = key_number & 0xFF; + key_number >>= 8; + } + challenge->append(part, 4); +} + } // anonymous namespace namespace net { @@ -145,19 +182,28 @@ void WebSocketHandshakeRequestHandler::RemoveHeaders( headers_, headers_to_remove, headers_to_remove_len); } -const HttpRequestInfo& WebSocketHandshakeRequestHandler::GetRequestInfo( - const GURL& url) { - NOTIMPLEMENTED(); - // TODO(ukai): implement for Spdy support. Rest is incomplete. - request_info_.url = url; - // TODO(ukai): method should be built from |request_status_line_|. - request_info_.method = "GET"; - - request_info_.extra_headers.Clear(); - request_info_.extra_headers.AddHeadersFromString(headers_); - // TODO(ukai): eliminate unnecessary headers, such as Sec-WebSocket-Key1 - // and Sec-WebSocket-Key2. - return request_info_; +HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo( + const GURL& url, std::string* challenge) { + HttpRequestInfo request_info; + request_info.url = url; + base::StringPiece method = status_line_.data(); + size_t method_end = base::StringPiece( + status_line_.data(), status_line_.size()).find_first_of(" "); + if (method_end != base::StringPiece::npos) + request_info.method = std::string(status_line_.data(), method_end); + + request_info.extra_headers.Clear(); + request_info.extra_headers.AddHeadersFromString(headers_); + + request_info.extra_headers.RemoveHeader("Upgrade"); + request_info.extra_headers.RemoveHeader("Connection"); + + challenge->clear(); + GetKeyNumber(&request_info.extra_headers, "Sec-WebSocket-Key1", challenge); + GetKeyNumber(&request_info.extra_headers, "Sec-WebSocket-Key2", challenge); + challenge->append(key3_); + + return request_info; } std::string WebSocketHandshakeRequestHandler::GetRawRequest() { @@ -174,11 +220,6 @@ size_t WebSocketHandshakeRequestHandler::raw_length() const { return raw_length_; } -std::string WebSocketHandshakeRequestHandler::GetChallenge() const { - NOTIMPLEMENTED(); - return ""; -} - WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler() : original_header_length_(0) { } @@ -219,25 +260,29 @@ bool WebSocketHandshakeResponseHandler::HasResponse() const { bool WebSocketHandshakeResponseHandler::ParseResponseInfo( const HttpResponseInfo& response_info, - WebSocketHandshakeRequestHandler* request_handler) { - NOTIMPLEMENTED(); - // TODO(ukai): implement for Spdy support. Rest is incomplete. - response_info_ = response_info; - - if (!response_info_.headers.get()) + const std::string& challenge) { + if (!response_info.headers.get()) return false; std::string response_message; - response_message = response_info_.headers->GetStatusLine(); + response_message = response_info.headers->GetStatusLine(); response_message += "\r\n"; + response_message += "Upgrade: WebSocket\r\n"; + response_message += "Connection: Upgrade\r\n"; void* iter = NULL; std::string name; std::string value; - while (response_info_.headers->EnumerateHeaderLines(&iter, &name, &value)) { + while (response_info.headers->EnumerateHeaderLines(&iter, &name, &value)) { response_message += name + ": " + value + "\r\n"; } - // TODO(ukai): generate response key from request_handler->GetChallenge() - // and add it to |response_message|. + response_message += "\r\n"; + + MD5Digest digest; + MD5Sum(challenge.data(), challenge.size(), &digest); + + const char* digest_data = reinterpret_cast<char*>(digest.a); + response_message.append(digest_data, sizeof(digest.a)); + return ParseRawResponse(response_message.data(), response_message.size()) == response_message.size(); } diff --git a/net/websockets/websocket_handshake_handler.h b/net/websockets/websocket_handshake_handler.h index 8f0cb4b..d661f94 100644 --- a/net/websockets/websocket_handshake_handler.h +++ b/net/websockets/websocket_handshake_handler.h @@ -43,18 +43,15 @@ class WebSocketHandshakeRequestHandler { size_t headers_to_remove_len); // Gets request info to open WebSocket connection. - const HttpRequestInfo& GetRequestInfo(const GURL& url); + // Also, full challange data in |challenge|. + HttpRequestInfo GetRequestInfo(const GURL& url, std::string* challenge); // Gets WebSocket handshake raw request message to open WebSocket // connection. std::string GetRawRequest(); // Calling raw_length is valid only after GetRawRquest() call. size_t raw_length() const; - std::string GetChallenge() const; - private: - HttpRequestInfo request_info_; - std::string status_line_; std::string headers_; std::string key3_; @@ -81,7 +78,7 @@ class WebSocketHandshakeResponseHandler { bool HasResponse() const; // Parses WebSocket handshake response info given as HttpResponseInfo. bool ParseResponseInfo(const HttpResponseInfo& response_info, - WebSocketHandshakeRequestHandler* request_handler); + const std::string& challenge); // Gets the headers value. void GetHeaders(const char* const headers_to_get[], @@ -95,7 +92,6 @@ class WebSocketHandshakeResponseHandler { std::string GetResponse(); private: - HttpResponseInfo response_info_; std::string original_; int original_header_length_; std::string status_line_; diff --git a/net/websockets/websocket_handshake_handler_unittest.cc b/net/websockets/websocket_handshake_handler_unittest.cc index 44e1633..8bac084 100644 --- a/net/websockets/websocket_handshake_handler_unittest.cc +++ b/net/websockets/websocket_handshake_handler_unittest.cc @@ -6,7 +6,9 @@ #include <vector> #include "base/basictypes.h" +#include "base/string_util.h" #include "googleurl/src/gurl.h" +#include "net/http/http_util.h" #include "net/websockets/websocket_handshake_handler.h" #include "testing/gmock/include/gmock/gmock.h" @@ -149,4 +151,86 @@ TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) { EXPECT_EQ(kHandshakeResponseExpectedMessage, handler.GetResponse()); } +TEST(WebSocketHandshakeHandlerTest, RequestResponse) { + WebSocketHandshakeRequestHandler request_handler; + + static const char* kHandshakeRequestMessage = + "GET /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "Upgrade: WebSocket\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "\r\n" + "^n:ds[4U"; + + EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage, + strlen(kHandshakeRequestMessage))); + + GURL url("ws://example.com/demo"); + std::string challenge; + const HttpRequestInfo& request_info = + request_handler.GetRequestInfo(url, &challenge); + + EXPECT_EQ(url, request_info.url); + EXPECT_EQ("GET", request_info.method); + EXPECT_FALSE(request_info.extra_headers.HasHeader("Upgrade")); + EXPECT_FALSE(request_info.extra_headers.HasHeader("Connection")); + EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key1")); + EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key2")); + std::string value; + EXPECT_TRUE(request_info.extra_headers.GetHeader("Host", &value)); + EXPECT_EQ("example.com", value); + EXPECT_TRUE(request_info.extra_headers.GetHeader("Origin", &value)); + EXPECT_EQ("http://example.com", value); + EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Protocol", + &value)); + EXPECT_EQ("sample", value); + + const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U"; + + EXPECT_EQ(expected_challenge, challenge); + + static const char* kHandshakeResponseHeader = + "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + "Sec-WebSocket-Origin: http://example.com\r\n" + "Sec-WebSocket-Location: ws://example.com/demo\r\n" + "Sec-WebSocket-Protocol: sample\r\n"; + + std::string raw_headers = + HttpUtil::AssembleRawHeaders(kHandshakeResponseHeader, + strlen(kHandshakeResponseHeader)); + HttpResponseInfo response_info; + response_info.headers = new HttpResponseHeaders(raw_headers); + + EXPECT_TRUE(StartsWithASCII(response_info.headers->GetStatusLine(), + "HTTP/1.1 101 ", false)); + EXPECT_FALSE(response_info.headers->HasHeader("Upgrade")); + EXPECT_FALSE(response_info.headers->HasHeader("Connection")); + EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Origin", + "http://example.com")); + EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Location", + "ws://example.com/demo")); + EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Protocol", + "sample")); + + WebSocketHandshakeResponseHandler response_handler; + EXPECT_TRUE(response_handler.ParseResponseInfo(response_info, challenge)); + EXPECT_TRUE(response_handler.HasResponse()); + + static const char* kHandshakeResponseExpectedMessage = + "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Origin: http://example.com\r\n" + "Sec-WebSocket-Location: ws://example.com/demo\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "\r\n" + "8jKS'y:G*Co,Wxa-"; + + EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); +} + } // namespace net |