summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/websockets/websocket_handshake_handler.cc103
-rw-r--r--net/websockets/websocket_handshake_handler.h10
-rw-r--r--net/websockets/websocket_handshake_handler_unittest.cc84
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