diff options
author | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-29 09:10:06 +0000 |
---|---|---|
committer | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-29 09:10:06 +0000 |
commit | f61c7856fcb153574f8aa67b0ad0546427d3fe74 (patch) | |
tree | 65e11ff71766a46b666868929a224e9d3e71a13f /net/websockets | |
parent | 909d274de04eecee37c295b2f88d094bac7a4987 (diff) | |
download | chromium_src-f61c7856fcb153574f8aa67b0ad0546427d3fe74.zip chromium_src-f61c7856fcb153574f8aa67b0ad0546427d3fe74.tar.gz chromium_src-f61c7856fcb153574f8aa67b0ad0546427d3fe74.tar.bz2 |
Add Spdy support in WebSocketHandshake*Handler
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/2743003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51109 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/websockets')
-rw-r--r-- | net/websockets/websocket_handshake_handler.cc | 116 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_handler.h | 11 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_handler_unittest.cc | 146 |
3 files changed, 259 insertions, 14 deletions
diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc index 7e5ec6f..647241c 100644 --- a/net/websockets/websocket_handshake_handler.cc +++ b/net/websockets/websocket_handshake_handler.cc @@ -94,24 +94,21 @@ std::string FilterHeaders( return filtered_headers; } -// Gets a key number for |key_name| in |headers| and appends the number to -// |challenge|. +// Gets a key number from |key| 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); - +void GetKeyNumber(const std::string& key, std::string* challenge) { uint32 key_number = 0; uint32 spaces = 0; for (size_t i = 0; i < key.size(); ++i) { - if (isdigit(key[i])) + if (isdigit(key[i])) { + // key_number should not overflow. (it comes from + // WebCore/websockets/WebSocketHandshake.cpp). key_number = key_number * 10 + key[i] - '0'; - else if (key[i] == ' ') + } else if (key[i] == ' ') { ++spaces; + } } // spaces should not be zero in valid handshake request. if (spaces == 0) @@ -199,13 +196,69 @@ HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo( 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); + std::string key; + request_info.extra_headers.GetHeader("Sec-WebSocket-Key1", &key); + request_info.extra_headers.RemoveHeader("Sec-WebSocket-Key1"); + GetKeyNumber(key, challenge); + + request_info.extra_headers.GetHeader("Sec-WebSocket-Key2", &key); + request_info.extra_headers.RemoveHeader("Sec-WebSocket-Key2"); + GetKeyNumber(key, challenge); + challenge->append(key3_); return request_info; } +bool WebSocketHandshakeRequestHandler::GetRequestHeaderBlock( + const GURL& url, spdy::SpdyHeaderBlock* headers, std::string* challenge) { + // We don't set "method" and "version". These are fixed value in WebSocket + // protocol. + (*headers)["url"] = url.spec(); + + std::string key1; + std::string key2; + HttpUtil::HeadersIterator iter(headers_.begin(), headers_.end(), "\r\n"); + while (iter.GetNext()) { + if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(), + "connection")) { + // Ignore "Connection" header. + continue; + } else if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(), + "upgrade")) { + // Ignore "Upgrade" header. + continue; + } else if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(), + "sec-websocket-key1")) { + // Use only for generating challenge. + key1 = iter.values(); + continue; + } else if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(), + "sec-websocket-key2")) { + // Use only for generating challenge. + key2 = iter.values(); + continue; + } + // Others should be sent out to |headers|. + std::string name = StringToLowerASCII(iter.name()); + spdy::SpdyHeaderBlock::iterator found = headers->find(name); + if (found == headers->end()) { + (*headers)[name] = iter.values(); + } else { + // For now, websocket doesn't use multiple headers, but follows to http. + found->second.append(1, '\0'); // +=() doesn't append 0's + found->second.append(iter.values()); + } + } + + challenge->clear(); + GetKeyNumber(key1, challenge); + GetKeyNumber(key2, challenge); + challenge->append(key3_); + + return true; +} + std::string WebSocketHandshakeRequestHandler::GetRawRequest() { DCHECK(status_line_.size() > 0); DCHECK(headers_.size() > 0); @@ -287,6 +340,45 @@ bool WebSocketHandshakeResponseHandler::ParseResponseInfo( response_message.size()) == response_message.size(); } +bool WebSocketHandshakeResponseHandler::ParseResponseHeaderBlock( + const spdy::SpdyHeaderBlock& headers, + const std::string& challenge) { + std::string response_message; + response_message = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"; + response_message += "Upgrade: WebSocket\r\n"; + response_message += "Connection: Upgrade\r\n"; + for (spdy::SpdyHeaderBlock::const_iterator iter = headers.begin(); + iter != headers.end(); + ++iter) { + // For each value, if the server sends a NUL-separated list of values, + // we separate that back out into individual headers for each value + // in the list. + const std::string& value = iter->second; + size_t start = 0; + size_t end = 0; + do { + end = value.find('\0', start); + std::string tval; + if (end != std::string::npos) + tval = value.substr(start, (end - start)); + else + tval = value.substr(start); + response_message += iter->first + ": " + tval + "\r\n"; + start = end + 1; + } while (end != std::string::npos); + } + 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(); +} + void WebSocketHandshakeResponseHandler::GetHeaders( const char* const headers_to_get[], size_t headers_to_get_len, diff --git a/net/websockets/websocket_handshake_handler.h b/net/websockets/websocket_handshake_handler.h index d661f94..3c11e00 100644 --- a/net/websockets/websocket_handshake_handler.h +++ b/net/websockets/websocket_handshake_handler.h @@ -19,6 +19,7 @@ #include "base/ref_counted.h" #include "net/http/http_request_info.h" #include "net/http/http_response_info.h" +#include "net/spdy/spdy_framer.h" namespace net { @@ -43,8 +44,13 @@ class WebSocketHandshakeRequestHandler { size_t headers_to_remove_len); // Gets request info to open WebSocket connection. - // Also, full challange data in |challenge|. + // Also, fill challange data in |challenge|. HttpRequestInfo GetRequestInfo(const GURL& url, std::string* challenge); + // Gets request as SpdyHeaderBlock. + // Also, fill challenge data in |challenge|. + bool GetRequestHeaderBlock(const GURL& url, + spdy::SpdyHeaderBlock* headers, + std::string* challenge); // Gets WebSocket handshake raw request message to open WebSocket // connection. std::string GetRawRequest(); @@ -79,6 +85,9 @@ class WebSocketHandshakeResponseHandler { // Parses WebSocket handshake response info given as HttpResponseInfo. bool ParseResponseInfo(const HttpResponseInfo& response_info, const std::string& challenge); + // Parses WebSocket handshake response as SpdyHeaderBlock. + bool ParseResponseHeaderBlock(const spdy::SpdyHeaderBlock& headers, + const std::string& challenge); // Gets the headers value. void GetHeaders(const char* const headers_to_get[], diff --git a/net/websockets/websocket_handshake_handler_unittest.cc b/net/websockets/websocket_handshake_handler_unittest.cc index 8bac084..4204d6a 100644 --- a/net/websockets/websocket_handshake_handler_unittest.cc +++ b/net/websockets/websocket_handshake_handler_unittest.cc @@ -151,7 +151,7 @@ TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) { EXPECT_EQ(kHandshakeResponseExpectedMessage, handler.GetResponse()); } -TEST(WebSocketHandshakeHandlerTest, RequestResponse) { +TEST(WebSocketHandshakeHandlerTest, HttpRequestResponse) { WebSocketHandshakeRequestHandler request_handler; static const char* kHandshakeRequestMessage = @@ -233,4 +233,148 @@ TEST(WebSocketHandshakeHandlerTest, RequestResponse) { EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); } +TEST(WebSocketHandshakeHandlerTest, SpdyRequestResponse) { + 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" + "X-bogus-header: X\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "X-bogus-header: Y\r\n" + "\r\n" + "^n:ds[4U"; + + EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage, + strlen(kHandshakeRequestMessage))); + + GURL url("ws://example.com/demo"); + std::string challenge; + spdy::SpdyHeaderBlock headers; + ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, &headers, &challenge)); + + EXPECT_EQ(url.spec(), headers["url"]); + EXPECT_TRUE(headers.find("upgrade") == headers.end()); + EXPECT_TRUE(headers.find("Upgrade") == headers.end()); + EXPECT_TRUE(headers.find("connection") == headers.end()); + EXPECT_TRUE(headers.find("Connection") == headers.end()); + EXPECT_TRUE(headers.find("Sec-WebSocket-Key1") == headers.end()); + EXPECT_TRUE(headers.find("sec-websocket-key1") == headers.end()); + EXPECT_TRUE(headers.find("Sec-WebSocket-Key2") == headers.end()); + EXPECT_TRUE(headers.find("sec-websocket-key2") == headers.end()); + EXPECT_EQ("example.com", headers["host"]); + EXPECT_EQ("http://example.com", headers["origin"]); + EXPECT_EQ("sample", headers["sec-websocket-protocol"]); + const char bogus_header[] = "X\0Y"; + std::string bogus_header_str(bogus_header, sizeof(bogus_header) - 1); + EXPECT_EQ(bogus_header_str, headers["x-bogus-header"]); + + const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U"; + + EXPECT_EQ(expected_challenge, challenge); + + headers.clear(); + + headers["sec-websocket-origin"] = "http://example.com"; + headers["sec-websocket-location"] = "ws://example.com/demo"; + headers["sec-websocket-protocol"] = "sample"; + + WebSocketHandshakeResponseHandler response_handler; + EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(headers, challenge)); + EXPECT_TRUE(response_handler.HasResponse()); + + // Note that order of sec-websocket-* is sensitive with hash_map order. + static const char* kHandshakeResponseExpectedMessage = + "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "sec-websocket-location: ws://example.com/demo\r\n" + "sec-websocket-origin: http://example.com\r\n" + "sec-websocket-protocol: sample\r\n" + "\r\n" + "8jKS'y:G*Co,Wxa-"; + + EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); +} + + +TEST(WebSocketHandshakeHandlerTest, SpdyRequestResponseWithCookies) { + WebSocketHandshakeRequestHandler request_handler; + + // Note that websocket won't use multiple headers in request now. + 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" + "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n" + "\r\n" + "^n:ds[4U"; + + EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage, + strlen(kHandshakeRequestMessage))); + + GURL url("ws://example.com/demo"); + std::string challenge; + spdy::SpdyHeaderBlock headers; + ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, &headers, &challenge)); + + EXPECT_EQ(url.spec(), headers["url"]); + EXPECT_TRUE(headers.find("upgrade") == headers.end()); + EXPECT_TRUE(headers.find("Upgrade") == headers.end()); + EXPECT_TRUE(headers.find("connection") == headers.end()); + EXPECT_TRUE(headers.find("Connection") == headers.end()); + EXPECT_TRUE(headers.find("Sec-WebSocket-Key1") == headers.end()); + EXPECT_TRUE(headers.find("sec-websocket-key1") == headers.end()); + EXPECT_TRUE(headers.find("Sec-WebSocket-Key2") == headers.end()); + EXPECT_TRUE(headers.find("sec-websocket-key2") == headers.end()); + EXPECT_EQ("example.com", headers["host"]); + EXPECT_EQ("http://example.com", headers["origin"]); + EXPECT_EQ("sample", headers["sec-websocket-protocol"]); + EXPECT_EQ("WK-websocket-test=1; WK-websocket-test-httponly=1", + headers["cookie"]); + + const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U"; + + EXPECT_EQ(expected_challenge, challenge); + + headers.clear(); + + headers["sec-websocket-origin"] = "http://example.com"; + headers["sec-websocket-location"] = "ws://example.com/demo"; + headers["sec-websocket-protocol"] = "sample"; + std::string cookie = "WK-websocket-test=1"; + cookie.append(1, '\0'); + cookie += "WK-websocket-test-httponly=1; HttpOnly"; + headers["set-cookie"] = cookie; + + WebSocketHandshakeResponseHandler response_handler; + EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(headers, challenge)); + EXPECT_TRUE(response_handler.HasResponse()); + + // Note that order of sec-websocket-* is sensitive with hash_map order. + static const char* kHandshakeResponseExpectedMessage = + "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "sec-websocket-location: ws://example.com/demo\r\n" + "sec-websocket-origin: http://example.com\r\n" + "sec-websocket-protocol: sample\r\n" + "set-cookie: WK-websocket-test=1\r\n" + "set-cookie: WK-websocket-test-httponly=1; HttpOnly\r\n" + "\r\n" + "8jKS'y:G*Co,Wxa-"; + + EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); +} + } // namespace net |