summaryrefslogtreecommitdiffstats
path: root/net/websockets
diff options
context:
space:
mode:
authorukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-29 09:10:06 +0000
committerukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-29 09:10:06 +0000
commitf61c7856fcb153574f8aa67b0ad0546427d3fe74 (patch)
tree65e11ff71766a46b666868929a224e9d3e71a13f /net/websockets
parent909d274de04eecee37c295b2f88d094bac7a4987 (diff)
downloadchromium_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.cc116
-rw-r--r--net/websockets/websocket_handshake_handler.h11
-rw-r--r--net/websockets/websocket_handshake_handler_unittest.cc146
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