summaryrefslogtreecommitdiffstats
path: root/net/websockets
diff options
context:
space:
mode:
authorukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-03 08:58:08 +0000
committerukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-03 08:58:08 +0000
commit817fc50713c37fb0cb41fbc455d83fb40b40da7e (patch)
treedfde9cc335e1e35bf9c19fc140c696c3d00e3281 /net/websockets
parent18141b6e06116e417b40e3bfc16a042e8188fb2b (diff)
downloadchromium_src-817fc50713c37fb0cb41fbc455d83fb40b40da7e.zip
chromium_src-817fc50713c37fb0cb41fbc455d83fb40b40da7e.tar.gz
chromium_src-817fc50713c37fb0cb41fbc455d83fb40b40da7e.tar.bz2
Refactor WebSocket handshake.
BUG=none TEST=none Review URL: http://codereview.chromium.org/2452001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48816 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/websockets')
-rw-r--r--net/websockets/websocket_handshake_handler.cc277
-rw-r--r--net/websockets/websocket_handshake_handler.h110
-rw-r--r--net/websockets/websocket_handshake_handler_unittest.cc152
-rw-r--r--net/websockets/websocket_job.cc240
-rw-r--r--net/websockets/websocket_job.h10
5 files changed, 595 insertions, 194 deletions
diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc
new file mode 100644
index 0000000..8b1e071
--- /dev/null
+++ b/net/websockets/websocket_handshake_handler.cc
@@ -0,0 +1,277 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/websockets/websocket_handshake_handler.h"
+
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_util.h"
+
+namespace {
+
+const size_t kRequestKey3Size = 8U;
+const size_t kResponseKeySize = 16U;
+
+void ParseHandshakeHeader(
+ const char* handshake_message, int len,
+ std::string* status_line,
+ std::string* headers) {
+ size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
+ if (i == base::StringPiece::npos) {
+ *status_line = std::string(handshake_message, len);
+ *headers = "";
+ return;
+ }
+ *status_line = std::string(handshake_message, i + 2);
+ *headers = std::string(handshake_message + i + 2, len - i - 2);
+}
+
+void FetchHeaders(const std::string& headers,
+ const char* const headers_to_get[],
+ size_t headers_to_get_len,
+ std::vector<std::string>* values) {
+ net::HttpUtil::HeadersIterator iter(headers.begin(), headers.end(), "\r\n");
+ while (iter.GetNext()) {
+ for (size_t i = 0; i < headers_to_get_len; i++) {
+ if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+ headers_to_get[i])) {
+ values->push_back(iter.values());
+ }
+ }
+ }
+}
+
+bool GetHeaderName(std::string::const_iterator line_begin,
+ std::string::const_iterator line_end,
+ std::string::const_iterator* name_begin,
+ std::string::const_iterator* name_end) {
+ std::string::const_iterator colon = std::find(line_begin, line_end, ':');
+ if (colon == line_end) {
+ return false;
+ }
+ *name_begin = line_begin;
+ *name_end = colon;
+ if (*name_begin == *name_end || net::HttpUtil::IsLWS(**name_begin))
+ return false;
+ net::HttpUtil::TrimLWS(name_begin, name_end);
+ return true;
+}
+
+// Similar to HttpUtil::StripHeaders, but it preserves malformed headers, that
+// is, lines that are not formatted as "<name>: <value>\r\n".
+std::string FilterHeaders(
+ const std::string& headers,
+ const char* const headers_to_remove[],
+ size_t headers_to_remove_len) {
+ std::string filtered_headers;
+
+ StringTokenizer lines(headers.begin(), headers.end(), "\r\n");
+ while (lines.GetNext()) {
+ std::string::const_iterator line_begin = lines.token_begin();
+ std::string::const_iterator line_end = lines.token_end();
+ std::string::const_iterator name_begin;
+ std::string::const_iterator name_end;
+ bool should_remove = false;
+ if (GetHeaderName(line_begin, line_end, &name_begin, &name_end)) {
+ for (size_t i = 0; i < headers_to_remove_len; ++i) {
+ if (LowerCaseEqualsASCII(name_begin, name_end, headers_to_remove[i])) {
+ should_remove = true;
+ break;
+ }
+ }
+ }
+ if (!should_remove) {
+ filtered_headers.append(line_begin, line_end);
+ filtered_headers.append("\r\n");
+ }
+ }
+ return filtered_headers;
+}
+
+} // anonymous namespace
+
+namespace net {
+
+WebSocketHandshakeRequestHandler::WebSocketHandshakeRequestHandler()
+ : original_length_(0),
+ raw_length_(0) {}
+
+bool WebSocketHandshakeRequestHandler::ParseRequest(
+ const char* data, int length) {
+ DCHECK_GT(length, 0);
+ std::string input(data, length);
+ int input_header_length =
+ HttpUtil::LocateEndOfHeaders(input.data(), input.size(), 0);
+ if (input_header_length <= 0 ||
+ input_header_length + kRequestKey3Size > input.size())
+ return false;
+
+ ParseHandshakeHeader(input.data(),
+ input_header_length,
+ &status_line_,
+ &headers_);
+
+ // draft-hixie-thewebsocketprotocol-76 or later will send /key3/
+ // after handshake request header.
+ // Assumes WebKit doesn't send any data after handshake request message
+ // until handshake is finished.
+ // Thus, |key3_| is part of handshake message, and not in part
+ // of WebSocket frame stream.
+ DCHECK_EQ(kRequestKey3Size,
+ input.size() -
+ input_header_length);
+ key3_ = std::string(input.data() + input_header_length,
+ input.size() - input_header_length);
+ original_length_ = input.size();
+ return true;
+}
+
+size_t WebSocketHandshakeRequestHandler::original_length() const {
+ return original_length_;
+}
+
+void WebSocketHandshakeRequestHandler::AppendHeaderIfMissing(
+ const std::string& name, const std::string& value) {
+ DCHECK(headers_.size() > 0);
+ HttpUtil::AppendHeaderIfMissing(name.c_str(), value, &headers_);
+}
+
+void WebSocketHandshakeRequestHandler::RemoveHeaders(
+ const char* const headers_to_remove[],
+ size_t headers_to_remove_len) {
+ DCHECK(headers_.size() > 0);
+ headers_ = FilterHeaders(
+ 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_;
+}
+
+std::string WebSocketHandshakeRequestHandler::GetRawRequest() {
+ DCHECK(status_line_.size() > 0);
+ DCHECK(headers_.size() > 0);
+ DCHECK_EQ(kRequestKey3Size, key3_.size());
+ std::string raw_request = status_line_ + headers_ + "\r\n" + key3_;
+ raw_length_ = raw_request.size();
+ return raw_request;
+}
+
+size_t WebSocketHandshakeRequestHandler::raw_length() const {
+ DCHECK_GT(raw_length_, 0);
+ return raw_length_;
+}
+
+std::string WebSocketHandshakeRequestHandler::GetChallenge() const {
+ NOTIMPLEMENTED();
+ return "";
+}
+
+WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler()
+ : original_header_length_(0) {
+}
+
+size_t WebSocketHandshakeResponseHandler::ParseRawResponse(
+ const char* data, int length) {
+ DCHECK_GT(length, 0);
+ if (HasResponse()) {
+ DCHECK(status_line_.size() > 0);
+ DCHECK(headers_.size() > 0);
+ DCHECK_EQ(kResponseKeySize, key_.size());
+ return 0;
+ }
+
+ size_t old_original_length = original_.size();
+
+ original_.append(data, length);
+ // TODO(ukai): fail fast when response gives wrong status code.
+ original_header_length_ = HttpUtil::LocateEndOfHeaders(
+ original_.data(), original_.size(), 0);
+ if (!HasResponse())
+ return length;
+
+ ParseHandshakeHeader(original_.data(),
+ original_header_length_,
+ &status_line_,
+ &headers_);
+ key_ = std::string(original_.data() + original_header_length_,
+ kResponseKeySize);
+
+ return original_header_length_ + kResponseKeySize - old_original_length;
+}
+
+bool WebSocketHandshakeResponseHandler::HasResponse() const {
+ return original_header_length_ > 0 &&
+ original_header_length_ + kResponseKeySize <= original_.size();
+}
+
+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())
+ return false;
+
+ std::string response_message;
+ response_message = response_info_.headers->GetStatusLine();
+ response_message += "\r\n";
+ void* iter = NULL;
+ std::string name;
+ std::string 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|.
+ 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,
+ std::vector<std::string>* values) {
+ DCHECK(HasResponse());
+ DCHECK(status_line_.size() > 0);
+ DCHECK(headers_.size() > 0);
+ DCHECK_EQ(kResponseKeySize, key_.size());
+
+ FetchHeaders(headers_, headers_to_get, headers_to_get_len, values);
+}
+
+void WebSocketHandshakeResponseHandler::RemoveHeaders(
+ const char* const headers_to_remove[],
+ size_t headers_to_remove_len) {
+ DCHECK(HasResponse());
+ DCHECK(status_line_.size() > 0);
+ DCHECK(headers_.size() > 0);
+ DCHECK_EQ(kResponseKeySize, key_.size());
+
+ headers_ = FilterHeaders(headers_, headers_to_remove, headers_to_remove_len);
+}
+
+std::string WebSocketHandshakeResponseHandler::GetResponse() {
+ DCHECK(HasResponse());
+ DCHECK(status_line_.size() > 0);
+ DCHECK(headers_.size() > 0);
+ DCHECK_EQ(kResponseKeySize, key_.size());
+
+ return status_line_ + headers_ + "\r\n" + key_;
+}
+
+} // namespace net
diff --git a/net/websockets/websocket_handshake_handler.h b/net/websockets/websocket_handshake_handler.h
new file mode 100644
index 0000000..8f0cb4b
--- /dev/null
+++ b/net/websockets/websocket_handshake_handler.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// WebSocketHandshake*Handler handles WebSocket handshake request message
+// from WebKit renderer process, and WebSocket handshake response message
+// from WebSocket server.
+// It modifies messages for the following reason:
+// - We don't trust WebKit renderer process, so we'll not expose HttpOnly
+// cookies to the renderer process, so handles HttpOnly cookies in
+// browser process.
+
+#ifndef NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+
+namespace net {
+
+class WebSocketHandshakeRequestHandler {
+ public:
+ WebSocketHandshakeRequestHandler();
+ ~WebSocketHandshakeRequestHandler() {}
+
+ // Parses WebSocket handshake request from renderer process.
+ // It assumes a WebSocket handshake request message is given at once, and
+ // no other data is added to the request message.
+ bool ParseRequest(const char* data, int length);
+
+ size_t original_length() const;
+
+ // Appends the header value pair for |name| and |value|, if |name| doesn't
+ // exist.
+ void AppendHeaderIfMissing(const std::string& name,
+ const std::string& value);
+ // Removes the headers that matches (case insensitive).
+ void RemoveHeaders(const char* const headers_to_remove[],
+ size_t headers_to_remove_len);
+
+ // Gets request info to open WebSocket connection.
+ const HttpRequestInfo& GetRequestInfo(const GURL& url);
+ // 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_;
+ int original_length_;
+ int raw_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeRequestHandler);
+};
+
+class WebSocketHandshakeResponseHandler {
+ public:
+ WebSocketHandshakeResponseHandler();
+ ~WebSocketHandshakeResponseHandler() {}
+
+ // Parses WebSocket handshake response from WebSocket server.
+ // Returns number of bytes in |data| used for WebSocket handshake response
+ // message, including response key. If it already got whole WebSocket
+ // handshake response message, returns zero. In other words,
+ // [data + returned value, data + length) will be WebSocket frame data
+ // after handshake response message.
+ // TODO(ukai): fail fast when response gives wrong status code.
+ size_t ParseRawResponse(const char* data, int length);
+ // Returns true if it already parses full handshake response message.
+ bool HasResponse() const;
+ // Parses WebSocket handshake response info given as HttpResponseInfo.
+ bool ParseResponseInfo(const HttpResponseInfo& response_info,
+ WebSocketHandshakeRequestHandler* request_handler);
+
+ // Gets the headers value.
+ void GetHeaders(const char* const headers_to_get[],
+ size_t headers_to_get_len,
+ std::vector<std::string>* values);
+ // Removes the headers that matches (case insensitive).
+ void RemoveHeaders(const char* const headers_to_remove[],
+ size_t headers_to_remove_len);
+
+ // Gets WebSocket handshake response message sent to renderer process.
+ std::string GetResponse();
+
+ private:
+ HttpResponseInfo response_info_;
+ std::string original_;
+ int original_header_length_;
+ std::string status_line_;
+ std::string headers_;
+ std::string key_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeResponseHandler);
+};
+
+} // namespace net
+
+#endif // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
diff --git a/net/websockets/websocket_handshake_handler_unittest.cc b/net/websockets/websocket_handshake_handler_unittest.cc
new file mode 100644
index 0000000..44e1633
--- /dev/null
+++ b/net/websockets/websocket_handshake_handler_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
+#include "net/websockets/websocket_handshake_handler.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+const char* const kCookieHeaders[] = {
+ "cookie", "cookie2"
+};
+
+const char* const kSetCookieHeaders[] = {
+ "set-cookie", "set-cookie2"
+};
+
+}
+
+namespace net {
+
+TEST(WebSocketHandshakeRequestHandlerTest, SimpleRequest) {
+ WebSocketHandshakeRequestHandler 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(handler.ParseRequest(kHandshakeRequestMessage,
+ strlen(kHandshakeRequestMessage)));
+
+ handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
+
+ EXPECT_EQ(kHandshakeRequestMessage, handler.GetRawRequest());
+}
+
+TEST(WebSocketHandshakeRequestHandlerTest, ReplaceRequestCookies) {
+ WebSocketHandshakeRequestHandler 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"
+ "Cookie: WK-websocket-test=1\r\n"
+ "\r\n"
+ "^n:ds[4U";
+
+ EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage,
+ strlen(kHandshakeRequestMessage)));
+
+ handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
+
+ handler.AppendHeaderIfMissing("Cookie",
+ "WK-websocket-test=1; "
+ "WK-websocket-test-httponly=1");
+
+ static const char* kHandshakeRequestExpectedMessage =
+ "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_EQ(kHandshakeRequestExpectedMessage, handler.GetRawRequest());
+}
+
+TEST(WebSocketHandshakeResponseHandlerTest, SimpleResponse) {
+ WebSocketHandshakeResponseHandler handler;
+
+ static const char* kHandshakeResponseMessage =
+ "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_TRUE(handler.ParseRawResponse(kHandshakeResponseMessage,
+ strlen(kHandshakeResponseMessage)));
+
+ handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
+
+ EXPECT_EQ(kHandshakeResponseMessage, handler.GetResponse());
+}
+
+TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) {
+ WebSocketHandshakeResponseHandler handler;
+
+ static const char* kHandshakeResponseMessage =
+ "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"
+ "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_TRUE(handler.ParseRawResponse(kHandshakeResponseMessage,
+ strlen(kHandshakeResponseMessage)));
+ std::vector<std::string> cookies;
+ handler.GetHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders), &cookies);
+ ASSERT_EQ(2U, cookies.size());
+ EXPECT_EQ("WK-websocket-test-1", cookies[0]);
+ EXPECT_EQ("WK-websocket-test-httponly=1; HttpOnly", cookies[1]);
+ handler.RemoveHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders));
+
+ 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, handler.GetResponse());
+}
+
+} // namespace net
diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc
index 063b3ab..0998c98 100644
--- a/net/websockets/websocket_job.cc
+++ b/net/websockets/websocket_job.cc
@@ -15,13 +15,11 @@
#include "net/http/http_util.h"
#include "net/url_request/url_request_context.h"
#include "net/websockets/websocket_frame_handler.h"
+#include "net/websockets/websocket_handshake_handler.h"
#include "net/websockets/websocket_throttle.h"
namespace {
-const size_t kRequestKey3Size = 8U;
-const size_t kResponseKeySize = 16U;
-
// lower-case header names.
const char* const kCookieHeaders[] = {
"cookie", "cookie2"
@@ -46,83 +44,6 @@ class WebSocketJobInitSingleton {
}
};
-void ParseHandshakeMessage(
- const char* handshake_message, int len,
- std::string* status_line,
- std::string* header) {
- size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
- if (i == base::StringPiece::npos) {
- *status_line = std::string(handshake_message, len);
- *header = "";
- return;
- }
- *status_line = std::string(handshake_message, i + 2);
- *header = std::string(handshake_message + i + 2, len - i - 2);
-}
-
-void FetchResponseCookies(
- const char* handshake_message, int len,
- std::vector<std::string>* response_cookies) {
- std::string handshake_response(handshake_message, len);
- net::HttpUtil::HeadersIterator iter(handshake_response.begin(),
- handshake_response.end(), "\r\n");
- while (iter.GetNext()) {
- for (size_t i = 0; i < arraysize(kSetCookieHeaders); i++) {
- if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
- kSetCookieHeaders[i])) {
- response_cookies->push_back(iter.values());
- }
- }
- }
-}
-
-bool GetHeaderName(std::string::const_iterator line_begin,
- std::string::const_iterator line_end,
- std::string::const_iterator* name_begin,
- std::string::const_iterator* name_end) {
- std::string::const_iterator colon = std::find(line_begin, line_end, ':');
- if (colon == line_end) {
- return false;
- }
- *name_begin = line_begin;
- *name_end = colon;
- if (*name_begin == *name_end || net::HttpUtil::IsLWS(**name_begin))
- return false;
- net::HttpUtil::TrimLWS(name_begin, name_end);
- return true;
-}
-
-// Similar to HttpUtil::StripHeaders, but it preserves malformed headers, that
-// is, lines that are not formatted as "<name>: <value>\r\n".
-std::string FilterHeaders(
- const std::string& headers,
- const char* const headers_to_remove[],
- size_t headers_to_remove_len) {
- std::string filtered_headers;
-
- StringTokenizer lines(headers.begin(), headers.end(), "\r\n");
- while (lines.GetNext()) {
- std::string::const_iterator line_begin = lines.token_begin();
- std::string::const_iterator line_end = lines.token_end();
- std::string::const_iterator name_begin;
- std::string::const_iterator name_end;
- bool should_remove = false;
- if (GetHeaderName(line_begin, line_end, &name_begin, &name_end)) {
- for (size_t i = 0; i < headers_to_remove_len; ++i) {
- if (LowerCaseEqualsASCII(name_begin, name_end, headers_to_remove[i])) {
- should_remove = true;
- break;
- }
- }
- }
- if (!should_remove) {
- filtered_headers.append(line_begin, line_end);
- filtered_headers.append("\r\n");
- }
- }
- return filtered_headers;
-}
-
} // anonymous namespace
namespace net {
@@ -137,8 +58,9 @@ WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate)
state_(INITIALIZED),
waiting_(false),
callback_(NULL),
+ handshake_request_(new WebSocketHandshakeRequestHandler),
+ handshake_response_(new WebSocketHandshakeResponseHandler),
handshake_request_sent_(0),
- handshake_response_header_length_(0),
response_cookies_save_index_(0),
ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_(
this, &WebSocketJob::OnCanGetCookiesCompleted)),
@@ -330,22 +252,12 @@ void WebSocketJob::OnError(const SocketStream* socket, int error) {
bool WebSocketJob::SendHandshakeRequest(const char* data, int len) {
DCHECK_EQ(state_, CONNECTING);
- if (!handshake_request_.empty()) {
- // if we're already sending handshake message, don't send any more data
- // until handshake is completed.
+ if (!handshake_request_->ParseRequest(data, len))
return false;
- }
- original_handshake_request_.append(data, len);
- original_handshake_request_header_length_ =
- HttpUtil::LocateEndOfHeaders(original_handshake_request_.data(),
- original_handshake_request_.size(), 0);
- if (original_handshake_request_header_length_ > 0 &&
- original_handshake_request_header_length_ + kRequestKey3Size <=
- original_handshake_request_.size()) {
- // handshake message is completed.
- AddCookieHeaderAndSend();
- }
- // Just buffered in original_handshake_request_.
+
+ // handshake message is completed.
+ AddCookieHeaderAndSend();
+ // Just buffered in |handshake_request_|.
return true;
}
@@ -367,19 +279,8 @@ void WebSocketJob::AddCookieHeaderAndSend() {
void WebSocketJob::OnCanGetCookiesCompleted(int policy) {
if (socket_ && delegate_ && state_ == CONNECTING) {
- std::string handshake_request_status_line;
- std::string handshake_request_header;
- ParseHandshakeMessage(original_handshake_request_.data(),
- original_handshake_request_header_length_,
- &handshake_request_status_line,
- &handshake_request_header);
-
- // Remove cookie headers. We should not send out malformed headers, so
- // use HttpUtil::StripHeaders() instead of FilterHeaders().
- handshake_request_header = HttpUtil::StripHeaders(
- handshake_request_header,
+ handshake_request_->RemoveHeaders(
kCookieHeaders, arraysize(kCookieHeaders));
-
if (policy == OK) {
// Add cookies, including HttpOnly cookies.
if (socket_->context()->cookie_store()) {
@@ -388,34 +289,15 @@ void WebSocketJob::OnCanGetCookiesCompleted(int policy) {
std::string cookie =
socket_->context()->cookie_store()->GetCookiesWithOptions(
GetURLForCookies(), cookie_options);
- if (!cookie.empty()) {
- HttpUtil::AppendHeaderIfMissing("Cookie", cookie,
- &handshake_request_header);
- }
+ if (!cookie.empty())
+ handshake_request_->AppendHeaderIfMissing("Cookie", cookie);
}
}
- // draft-hixie-thewebsocketprotocol-76 or later will send /key3/
- // after handshake request header.
- // Assumes WebKit doesn't send any data after handshake request message
- // until handshake is finished.
- // Thus, additional_data is part of handshake message, and not in part
- // of websocket frame stream.
- DCHECK_EQ(kRequestKey3Size,
- original_handshake_request_.size() -
- original_handshake_request_header_length_);
- std::string additional_data =
- std::string(original_handshake_request_.data() +
- original_handshake_request_header_length_,
- original_handshake_request_.size() -
- original_handshake_request_header_length_);
- handshake_request_ =
- handshake_request_status_line + handshake_request_header + "\r\n" +
- additional_data;
-
+ const std::string& handshake_request = handshake_request_->GetRawRequest();
handshake_request_sent_ = 0;
- socket_->SendData(handshake_request_.data(),
- handshake_request_.size());
+ socket_->SendData(handshake_request.data(),
+ handshake_request.size());
}
Release(); // Balance AddRef taken in AddCookieHeaderAndSend
}
@@ -424,50 +306,51 @@ void WebSocketJob::OnSentHandshakeRequest(
SocketStream* socket, int amount_sent) {
DCHECK_EQ(state_, CONNECTING);
handshake_request_sent_ += amount_sent;
- DCHECK_LE(handshake_request_sent_, handshake_request_.size());
- if (handshake_request_sent_ >= handshake_request_.size()) {
+ DCHECK_LE(handshake_request_sent_, handshake_request_->raw_length());
+ if (handshake_request_sent_ >= handshake_request_->raw_length()) {
// handshake request has been sent.
// notify original size of handshake request to delegate.
if (delegate_)
- delegate_->OnSentData(socket, original_handshake_request_.size());
+ delegate_->OnSentData(
+ socket,
+ handshake_request_->original_length());
+ handshake_request_.reset();
}
}
void WebSocketJob::OnReceivedHandshakeResponse(
SocketStream* socket, const char* data, int len) {
DCHECK_EQ(state_, CONNECTING);
- // Check if response is already full received before appending new data
- // to |handshake_response_|
- if (handshake_response_header_length_ > 0 &&
- handshake_response_header_length_ + kResponseKeySize
- <= handshake_response_.size()) {
- // already started cookies processing.
- handshake_response_.append(data, len);
+ if (handshake_response_->HasResponse()) {
+ // If we already has handshake response, received data should be frame
+ // data, not handshake message.
+ receive_frame_handler_->AppendData(data, len);
return;
}
- handshake_response_.append(data, len);
- handshake_response_header_length_ = HttpUtil::LocateEndOfHeaders(
- handshake_response_.data(),
- handshake_response_.size(), 0);
- if (handshake_response_header_length_ > 0 &&
- handshake_response_header_length_ + kResponseKeySize
- <= handshake_response_.size()) {
- // handshake message is completed.
- SaveCookiesAndNotifyHeaderComplete();
+
+ size_t response_length = handshake_response_->ParseRawResponse(data, len);
+ if (!handshake_response_->HasResponse()) {
+ // not yet. we need more data.
+ return;
}
+ // handshake message is completed.
+ if (len - response_length > 0) {
+ // If we received extra data, it should be frame data.
+ receive_frame_handler_->AppendData(data + response_length,
+ len - response_length);
+ }
+ SaveCookiesAndNotifyHeaderComplete();
}
void WebSocketJob::SaveCookiesAndNotifyHeaderComplete() {
// handshake message is completed.
- DCHECK(handshake_response_.data());
- DCHECK_GT(handshake_response_header_length_, 0);
+ DCHECK(handshake_response_->HasResponse());
response_cookies_.clear();
response_cookies_save_index_ = 0;
- FetchResponseCookies(handshake_response_.data(),
- handshake_response_header_length_,
- &response_cookies_);
+ handshake_response_->GetHeaders(
+ kSetCookieHeaders, arraysize(kSetCookieHeaders), &response_cookies_);
// Now, loop over the response cookies, and attempt to persist each.
SaveNextCookie();
@@ -478,41 +361,18 @@ void WebSocketJob::SaveNextCookie() {
response_cookies_.clear();
response_cookies_save_index_ = 0;
- std::string handshake_response_status_line;
- std::string handshake_response_header;
- ParseHandshakeMessage(handshake_response_.data(),
- handshake_response_header_length_,
- &handshake_response_status_line,
- &handshake_response_header);
// Remove cookie headers, with malformed headers preserved.
// Actual handshake should be done in WebKit.
- std::string filtered_handshake_response_header =
- FilterHeaders(handshake_response_header,
- kSetCookieHeaders, arraysize(kSetCookieHeaders));
- std::string response_key =
- std::string(handshake_response_.data() +
- handshake_response_header_length_,
- kResponseKeySize);
- std::string received_data =
- handshake_response_status_line +
- filtered_handshake_response_header +
- "\r\n" +
- response_key;
- if (handshake_response_header_length_ + kResponseKeySize
- < handshake_response_.size()) {
- receive_frame_handler_->AppendData(
- handshake_response_.data() + handshake_response_header_length_ +
- kResponseKeySize,
- handshake_response_.size() - handshake_response_header_length_ -
- kResponseKeySize);
- // Don't buffer receiving data for now.
- // TODO(ukai): fix performance of WebSocketFrameHandler.
- while (receive_frame_handler_->UpdateCurrentBuffer(false) > 0) {
- received_data +=
- std::string(receive_frame_handler_->GetCurrentBuffer()->data(),
- receive_frame_handler_->GetCurrentBufferSize());
- receive_frame_handler_->ReleaseCurrentBuffer();
- }
+ handshake_response_->RemoveHeaders(
+ kSetCookieHeaders, arraysize(kSetCookieHeaders));
+ std::string received_data = handshake_response_->GetResponse();
+ // Don't buffer receiving data for now.
+ // TODO(ukai): fix performance of WebSocketFrameHandler.
+ while (receive_frame_handler_->UpdateCurrentBuffer(false) > 0) {
+ received_data +=
+ std::string(receive_frame_handler_->GetCurrentBuffer()->data(),
+ receive_frame_handler_->GetCurrentBufferSize());
+ receive_frame_handler_->ReleaseCurrentBuffer();
}
state_ = OPEN;
@@ -520,6 +380,8 @@ void WebSocketJob::SaveNextCookie() {
delegate_->OnReceivedData(
socket_, received_data.data(), received_data.size());
+ handshake_response_.reset();
+
Singleton<WebSocketThrottle>::get()->RemoveFromQueue(this);
Singleton<WebSocketThrottle>::get()->WakeupSocketIfNecessary();
return;
diff --git a/net/websockets/websocket_job.h b/net/websockets/websocket_job.h
index db436be..833726b 100644
--- a/net/websockets/websocket_job.h
+++ b/net/websockets/websocket_job.h
@@ -18,6 +18,8 @@ namespace net {
class DrainableIOBuffer;
class WebSocketFrameHandler;
+class WebSocketHandshakeRequestHandler;
+class WebSocketHandshakeResponseHandler;
// WebSocket protocol specific job on SocketStream.
// It captures WebSocket handshake message and handles cookie operations.
@@ -95,13 +97,11 @@ class WebSocketJob : public SocketStreamJob, public SocketStream::Delegate {
AddressList addresses_;
CompletionCallback* callback_; // for throttling.
- std::string original_handshake_request_;
- int original_handshake_request_header_length_;
- std::string handshake_request_;
+ scoped_ptr<WebSocketHandshakeRequestHandler> handshake_request_;
+ scoped_ptr<WebSocketHandshakeResponseHandler> handshake_response_;
+
size_t handshake_request_sent_;
- std::string handshake_response_;
- int handshake_response_header_length_;
std::vector<std::string> response_cookies_;
size_t response_cookies_save_index_;