summaryrefslogtreecommitdiffstats
path: root/net/websockets
diff options
context:
space:
mode:
authorricea <ricea@chromium.org>2014-11-08 06:21:56 -0800
committerCommit bot <commit-bot@chromium.org>2014-11-08 14:22:14 +0000
commit5563c9df40ff8738c49cb1b86633c0ca91ac07c2 (patch)
treeef37780813f290e1904c70a4e689a06782b8a6c3 /net/websockets
parent626256ab20378c1aba947414a84ab3868d2efad9 (diff)
downloadchromium_src-5563c9df40ff8738c49cb1b86633c0ca91ac07c2.zip
chromium_src-5563c9df40ff8738c49cb1b86633c0ca91ac07c2.tar.gz
chromium_src-5563c9df40ff8738c49cb1b86633c0ca91ac07c2.tar.bz2
Delete the old WebSocket implementation from net/
The removal of NetworkDelegate::OnBeforeSocketStreamConnect() is in http://crrev.com/652363005 so not too many reviewers will need to review this more-complex CL. BUG=423201 TEST=net_unittests, browser_tests Review URL: https://codereview.chromium.org/679273005 Cr-Commit-Position: refs/heads/master@{#303387}
Diffstat (limited to 'net/websockets')
-rw-r--r--net/websockets/PRESUBMIT.py57
-rw-r--r--net/websockets/README91
-rw-r--r--net/websockets/websocket_handshake_handler.cc477
-rw-r--r--net/websockets/websocket_handshake_handler.h111
-rw-r--r--net/websockets/websocket_handshake_handler_spdy_test.cc187
-rw-r--r--net/websockets/websocket_handshake_handler_test.cc230
-rw-r--r--net/websockets/websocket_job.cc693
-rw-r--r--net/websockets/websocket_job.h154
-rw-r--r--net/websockets/websocket_job_test.cc1287
-rw-r--r--net/websockets/websocket_net_log_params.cc49
-rw-r--r--net/websockets/websocket_net_log_params.h21
-rw-r--r--net/websockets/websocket_net_log_params_test.cc50
-rw-r--r--net/websockets/websocket_throttle.cc144
-rw-r--r--net/websockets/websocket_throttle.h75
-rw-r--r--net/websockets/websocket_throttle_test.cc359
15 files changed, 7 insertions, 3978 deletions
diff --git a/net/websockets/PRESUBMIT.py b/net/websockets/PRESUBMIT.py
deleted file mode 100644
index 1da441c..0000000
--- a/net/websockets/PRESUBMIT.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2013 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.
-
-"""Chromium presubmit script for src/net/websockets.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details on the presubmit API built into gcl.
-"""
-
-
-# TODO(ricea): Remove this once the old implementation has been removed and the
-# list of files in the README file is no longer needed.
-def _CheckReadMeComplete(input_api, output_api):
- """Verifies that any new files have been added to the README file.
-
- Checks that if any source files were added in this CL, that they were
- also added to the README file. We do not warn about pre-existing
- errors, as that would be annoying.
-
- Args:
- input_api: The InputApi object provided by the presubmit framework.
- output_api: The OutputApi object provided by the framework.
-
- Returns:
- A list of zero or more PresubmitPromptWarning objects.
- """
- # None passed to AffectedSourceFiles means "use the default filter", which
- # does what we want, ie. returns files in the CL with filenames that look like
- # source code.
- added_source_filenames = set(input_api.basename(af.LocalPath())
- for af in input_api.AffectedSourceFiles(None)
- if af.Action().startswith('A'))
- if not added_source_filenames:
- return []
- readme = input_api.AffectedSourceFiles(
- lambda af: af.LocalPath().endswith('/README'))
- if not readme:
- return [output_api.PresubmitPromptWarning(
- 'One or more files were added to net/websockets without being added\n'
- 'to net/websockets/README.\n', added_source_filenames)]
- readme_added_filenames = set(line.strip() for line in readme[0].NewContents()
- if line.strip() in added_source_filenames)
- if readme_added_filenames < added_source_filenames:
- return [output_api.PresubmitPromptWarning(
- 'One or more files added to net/websockets but not found in the README '
- 'file.\n', added_source_filenames - readme_added_filenames)]
- else:
- return []
-
-
-def CheckChangeOnUpload(input_api, output_api):
- return _CheckReadMeComplete(input_api, output_api)
-
-
-def CheckChangeOnCommit(input_api, output_api):
- return _CheckReadMeComplete(input_api, output_api)
diff --git a/net/websockets/README b/net/websockets/README
deleted file mode 100644
index efe7a59..0000000
--- a/net/websockets/README
+++ /dev/null
@@ -1,91 +0,0 @@
-This directory contains files related to Chromium's WebSocket
-implementation. See http://www.websocket.org/ for an explanation of WebSockets.
-
-As of April 2013, the contents of this directory are in a transitional state,
-and contain parts of two different WebSocket implementations.
-
-The following files are part of the legacy implementation. The legacy
-implementation performs WebSocket framing within Blink and presents a
-low-level socket-like interface to the renderer process. It is described in the
-design doc at
-https://docs.google.com/a/google.com/document/d/1_R6YjCIrm4kikJ3YeapcOU2Keqr3lVUPd-OeaIJ93qQ/preview
-
-websocket_handshake_handler_test.cc
-websocket_handshake_handler_spdy_test.cc
-websocket_job.cc
-websocket_job.h
-websocket_job_test.cc
-websocket_net_log_params.cc
-websocket_net_log_params.h
-websocket_net_log_params_test.cc
-websocket_throttle.cc
-websocket_throttle.h
-websocket_throttle_test.cc
-
-The following files are part of the new implementation. The new implementation
-performs framing and implements protocol semantics in the browser process, and
-presents a high-level interface to the renderer process similar to a
-multiplexing proxy. This is the default implementation from M38.
-
-websocket_basic_handshake_stream.cc
-websocket_basic_handshake_stream.h
-websocket_basic_stream.cc
-websocket_basic_stream.h
-websocket_basic_stream_test.cc
-websocket_channel.cc
-websocket_channel.h
-websocket_channel_test.cc
-websocket_deflate_predictor.h
-websocket_deflate_predictor_impl.cc
-websocket_deflate_predictor_impl.h
-websocket_deflate_predictor_impl_test.cc
-websocket_deflate_stream.cc
-websocket_deflate_stream.h
-websocket_deflate_stream_test.cc
-websocket_deflater.cc
-websocket_deflater.h
-websocket_deflater_test.cc
-websocket_errors.cc
-websocket_errors.h
-websocket_errors_test.cc
-websocket_event_interface.h
-websocket_extension.cc
-websocket_extension.h
-websocket_extension_parser.cc
-websocket_extension_parser.h
-websocket_extension_parser_test.cc
-websocket_frame.cc
-websocket_frame.h
-websocket_frame_parser.cc
-websocket_frame_parser.h
-websocket_frame_parser_test.cc
-websocket_frame_test.cc
-websocket_frame_perftest.cc
-websocket_handshake_stream_base.h
-websocket_handshake_stream_create_helper.cc
-websocket_handshake_stream_create_helper.h
-websocket_handshake_stream_create_helper_test.cc
-websocket_handshake_request_info.cc
-websocket_handshake_request_info.h
-websocket_handshake_response_info.cc
-websocket_handshake_response_info.h
-websocket_inflater.cc
-websocket_inflater.h
-websocket_inflater_test.cc
-websocket_mux.h
-websocket_stream.cc
-websocket_stream.h
-websocket_stream_test.cc
-websocket_test_util.cc
-websocket_test_util.h
-
-These files are shared between the old and new implementations.
-
-websocket_handshake_constants.cc
-websocket_handshake_constants.h
-websocket_handshake_handler.cc
-websocket_handshake_handler.h
-
-A pre-submit check helps us keep this README file up-to-date:
-
-PRESUBMIT.py
diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc
index 6324710..6bcc230 100644
--- a/net/websockets/websocket_handshake_handler.cc
+++ b/net/websockets/websocket_handshake_handler.cc
@@ -4,346 +4,12 @@
#include "net/websockets/websocket_handshake_handler.h"
-#include <limits>
-
#include "base/base64.h"
+#include "base/logging.h"
#include "base/sha1.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_tokenizer.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
#include "net/websockets/websocket_handshake_constants.h"
-#include "url/gurl.h"
namespace net {
-namespace {
-
-const int kVersionHeaderValueForRFC6455 = 13;
-
-// Splits |handshake_message| into Status-Line or Request-Line (including CRLF)
-// and headers (excluding 2nd CRLF of double CRLFs at the end of a handshake
-// response).
-void ParseHandshakeHeader(
- const char* handshake_message, int len,
- std::string* request_line,
- std::string* headers) {
- size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
- if (i == base::StringPiece::npos) {
- *request_line = std::string(handshake_message, len);
- *headers = "";
- return;
- }
- // |request_line| includes \r\n.
- *request_line = std::string(handshake_message, i + 2);
-
- int header_len = len - (i + 2) - 2;
- if (header_len > 0) {
- // |handshake_message| includes trailing \r\n\r\n.
- // |headers| doesn't include 2nd \r\n.
- *headers = std::string(handshake_message + i + 2, header_len);
- } else {
- *headers = "";
- }
-}
-
-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;
-
- base::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;
-}
-
-bool CheckVersionInRequest(const std::string& request_headers) {
- std::vector<std::string> values;
- const char* const headers_to_get[1] = {
- websockets::kSecWebSocketVersionLowercase};
- FetchHeaders(request_headers, headers_to_get, 1, &values);
- DCHECK_LE(values.size(), 1U);
- if (values.empty())
- return false;
-
- int version;
- bool conversion_success = base::StringToInt(values[0], &version);
- if (!conversion_success)
- return false;
-
- return version == kVersionHeaderValueForRFC6455;
-}
-
-// Append a header to a string. Equivalent to
-// response_message += header + ": " + value + "\r\n"
-// but avoids unnecessary allocations and copies.
-void AppendHeader(const base::StringPiece& header,
- const base::StringPiece& value,
- std::string* response_message) {
- static const char kColonSpace[] = ": ";
- const size_t kColonSpaceSize = sizeof(kColonSpace) - 1;
- static const char kCrNl[] = "\r\n";
- const size_t kCrNlSize = sizeof(kCrNl) - 1;
-
- size_t extra_size =
- header.size() + kColonSpaceSize + value.size() + kCrNlSize;
- response_message->reserve(response_message->size() + extra_size);
- response_message->append(header.begin(), header.end());
- response_message->append(kColonSpace, kColonSpace + kColonSpaceSize);
- response_message->append(value.begin(), value.end());
- response_message->append(kCrNl, kCrNl + kCrNlSize);
-}
-
-} // namespace
-
-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)
- return false;
-
- ParseHandshakeHeader(input.data(),
- input_header_length,
- &request_line_,
- &headers_);
-
- if (!CheckVersionInRequest(headers_)) {
- NOTREACHED();
- return false;
- }
-
- original_length_ = input_header_length;
- return true;
-}
-
-size_t WebSocketHandshakeRequestHandler::original_length() const {
- return original_length_;
-}
-
-void WebSocketHandshakeRequestHandler::AppendHeaderIfMissing(
- const std::string& name, const std::string& value) {
- DCHECK(!headers_.empty());
- HttpUtil::AppendHeaderIfMissing(name.c_str(), value, &headers_);
-}
-
-void WebSocketHandshakeRequestHandler::RemoveHeaders(
- const char* const headers_to_remove[],
- size_t headers_to_remove_len) {
- DCHECK(!headers_.empty());
- headers_ = FilterHeaders(
- headers_, headers_to_remove, headers_to_remove_len);
-}
-
-HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo(
- const GURL& url, std::string* challenge) {
- HttpRequestInfo request_info;
- request_info.url = url;
- size_t method_end = base::StringPiece(request_line_).find_first_of(" ");
- if (method_end != base::StringPiece::npos)
- request_info.method = std::string(request_line_.data(), method_end);
-
- request_info.extra_headers.Clear();
- request_info.extra_headers.AddHeadersFromString(headers_);
-
- request_info.extra_headers.RemoveHeader(websockets::kUpgrade);
- request_info.extra_headers.RemoveHeader(HttpRequestHeaders::kConnection);
-
- std::string key;
- bool header_present = request_info.extra_headers.GetHeader(
- websockets::kSecWebSocketKey, &key);
- DCHECK(header_present);
- request_info.extra_headers.RemoveHeader(websockets::kSecWebSocketKey);
- *challenge = key;
- return request_info;
-}
-
-bool WebSocketHandshakeRequestHandler::GetRequestHeaderBlock(
- const GURL& url,
- SpdyHeaderBlock* headers,
- std::string* challenge,
- int spdy_protocol_version) {
- // Construct opening handshake request headers as a SPDY header block.
- // For details, see WebSocket Layering over SPDY/3 Draft 8.
- if (spdy_protocol_version <= 2) {
- (*headers)["path"] = url.path();
- (*headers)["version"] = "WebSocket/13";
- (*headers)["scheme"] = url.scheme();
- } else {
- (*headers)[":path"] = url.path();
- (*headers)[":version"] = "WebSocket/13";
- (*headers)[":scheme"] = url.scheme();
- }
-
- HttpUtil::HeadersIterator iter(headers_.begin(), headers_.end(), "\r\n");
- while (iter.GetNext()) {
- if (LowerCaseEqualsASCII(iter.name_begin(),
- iter.name_end(),
- websockets::kUpgradeLowercase) ||
- LowerCaseEqualsASCII(
- iter.name_begin(), iter.name_end(), "connection") ||
- LowerCaseEqualsASCII(iter.name_begin(),
- iter.name_end(),
- websockets::kSecWebSocketVersionLowercase)) {
- // These headers must be ignored.
- continue;
- } else if (LowerCaseEqualsASCII(iter.name_begin(),
- iter.name_end(),
- websockets::kSecWebSocketKeyLowercase)) {
- *challenge = iter.values();
- // Sec-WebSocket-Key is not sent to the server.
- continue;
- } else if (LowerCaseEqualsASCII(
- iter.name_begin(), iter.name_end(), "host") ||
- LowerCaseEqualsASCII(
- iter.name_begin(), iter.name_end(), "origin") ||
- LowerCaseEqualsASCII(
- iter.name_begin(),
- iter.name_end(),
- websockets::kSecWebSocketProtocolLowercase) ||
- LowerCaseEqualsASCII(
- iter.name_begin(),
- iter.name_end(),
- websockets::kSecWebSocketExtensionsLowercase)) {
- // TODO(toyoshim): Some WebSocket extensions may not be compatible with
- // SPDY. We should omit them from a Sec-WebSocket-Extension header.
- std::string name;
- if (spdy_protocol_version <= 2)
- name = base::StringToLowerASCII(iter.name());
- else
- name = ":" + base::StringToLowerASCII(iter.name());
- (*headers)[name] = iter.values();
- continue;
- }
- // Others should be sent out to |headers|.
- std::string name = base::StringToLowerASCII(iter.name());
- 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());
- }
- }
-
- return true;
-}
-
-std::string WebSocketHandshakeRequestHandler::GetRawRequest() {
- DCHECK(!request_line_.empty());
- DCHECK(!headers_.empty());
-
- std::string raw_request = request_line_ + headers_ + "\r\n";
- raw_length_ = raw_request.size();
- return raw_request;
-}
-
-size_t WebSocketHandshakeRequestHandler::raw_length() const {
- DCHECK_GT(raw_length_, 0);
- return raw_length_;
-}
-
-WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler()
- : original_header_length_(0) {}
-
-WebSocketHandshakeResponseHandler::~WebSocketHandshakeResponseHandler() {}
-
-size_t WebSocketHandshakeResponseHandler::ParseRawResponse(
- const char* data, int length) {
- DCHECK_GT(length, 0);
- if (HasResponse()) {
- DCHECK(!status_line_.empty());
- // headers_ might be empty for wrong response from server.
-
- 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_);
- int header_size = status_line_.size() + headers_.size();
- DCHECK_GE(original_header_length_, header_size);
- header_separator_ = std::string(original_.data() + header_size,
- original_header_length_ - header_size);
- return original_header_length_ - old_original_length;
-}
-
-bool WebSocketHandshakeResponseHandler::HasResponse() const {
- return original_header_length_ > 0 &&
- static_cast<size_t>(original_header_length_) <= original_.size();
-}
void ComputeSecWebSocketAccept(const std::string& key,
std::string* accept) {
@@ -354,145 +20,4 @@ void ComputeSecWebSocketAccept(const std::string& key,
base::Base64Encode(hash, accept);
}
-bool WebSocketHandshakeResponseHandler::ParseResponseInfo(
- const HttpResponseInfo& response_info,
- const std::string& challenge) {
- if (!response_info.headers.get())
- return false;
-
- // TODO(ricea): Eliminate all the reallocations and string copies.
- std::string response_message;
- response_message = response_info.headers->GetStatusLine();
- response_message += "\r\n";
-
- AppendHeader(websockets::kUpgrade,
- websockets::kWebSocketLowercase,
- &response_message);
-
- AppendHeader(
- HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message);
-
- std::string websocket_accept;
- ComputeSecWebSocketAccept(challenge, &websocket_accept);
- AppendHeader(
- websockets::kSecWebSocketAccept, websocket_accept, &response_message);
-
- void* iter = NULL;
- std::string name;
- std::string value;
- while (response_info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
- AppendHeader(name, value, &response_message);
- }
- response_message += "\r\n";
-
- return ParseRawResponse(response_message.data(),
- response_message.size()) == response_message.size();
-}
-
-bool WebSocketHandshakeResponseHandler::ParseResponseHeaderBlock(
- const SpdyHeaderBlock& headers,
- const std::string& challenge,
- int spdy_protocol_version) {
- SpdyHeaderBlock::const_iterator status;
- if (spdy_protocol_version <= 2)
- status = headers.find("status");
- else
- status = headers.find(":status");
- if (status == headers.end())
- return false;
-
- std::string hash =
- base::SHA1HashString(challenge + websockets::kWebSocketGuid);
- std::string websocket_accept;
- base::Base64Encode(hash, &websocket_accept);
-
- std::string response_message = base::StringPrintf(
- "%s %s\r\n", websockets::kHttpProtocolVersion, status->second.c_str());
-
- AppendHeader(
- websockets::kUpgrade, websockets::kWebSocketLowercase, &response_message);
- AppendHeader(
- HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message);
- AppendHeader(
- websockets::kSecWebSocketAccept, websocket_accept, &response_message);
-
- for (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.
- if ((spdy_protocol_version <= 2 &&
- LowerCaseEqualsASCII(iter->first, "status")) ||
- (spdy_protocol_version >= 3 &&
- LowerCaseEqualsASCII(iter->first, ":status"))) {
- // The status value is already handled as the first line of
- // |response_message|. Just skip here.
- continue;
- }
- 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);
- if (spdy_protocol_version >= 3 &&
- (LowerCaseEqualsASCII(iter->first,
- websockets::kSecWebSocketProtocolSpdy3) ||
- LowerCaseEqualsASCII(iter->first,
- websockets::kSecWebSocketExtensionsSpdy3)))
- AppendHeader(iter->first.substr(1), tval, &response_message);
- else
- AppendHeader(iter->first, tval, &response_message);
- start = end + 1;
- } while (end != std::string::npos);
- }
- response_message += "\r\n";
-
- 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_.empty());
- // headers_ might be empty for wrong response from server.
- if (headers_.empty())
- return;
-
- 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_.empty());
- // headers_ might be empty for wrong response from server.
- if (headers_.empty())
- return;
-
- headers_ = FilterHeaders(headers_, headers_to_remove, headers_to_remove_len);
-}
-
-std::string WebSocketHandshakeResponseHandler::GetRawResponse() const {
- DCHECK(HasResponse());
- return original_.substr(0, original_header_length_);
-}
-
-std::string WebSocketHandshakeResponseHandler::GetResponse() {
- DCHECK(HasResponse());
- DCHECK(!status_line_.empty());
- // headers_ might be empty for wrong response from server.
-
- return status_line_ + headers_ + header_separator_;
-}
-
} // namespace net
diff --git a/net/websockets/websocket_handshake_handler.h b/net/websockets/websocket_handshake_handler.h
index 73af66d..c65e6d9 100644
--- a/net/websockets/websocket_handshake_handler.h
+++ b/net/websockets/websocket_handshake_handler.h
@@ -1,124 +1,19 @@
// Copyright (c) 2012 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 "net/base/net_export.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_response_info.h"
-#include "net/spdy/spdy_header_block.h"
namespace net {
+// Given a WebSocket handshake challenge, compute the correct response.
+// TODO(ricea): There should probably be a test for this.
void ComputeSecWebSocketAccept(const std::string& key,
std::string* accept);
-class NET_EXPORT_PRIVATE 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 and fills challenge data in
- // |challenge|.
- HttpRequestInfo GetRequestInfo(const GURL& url, std::string* challenge);
- // Gets request as SpdyHeaderBlock.
- // Also, fills challenge data in |challenge|.
- bool GetRequestHeaderBlock(const GURL& url,
- SpdyHeaderBlock* headers,
- std::string* challenge,
- int spdy_protocol_version);
- // Gets WebSocket handshake raw request message to open WebSocket
- // connection.
- std::string GetRawRequest();
- // Calling raw_length is valid only after GetRawRequest() call.
- size_t raw_length() const;
-
- private:
- std::string request_line_;
- std::string headers_;
- int original_length_;
- int raw_length_;
-
- DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeRequestHandler);
-};
-
-class NET_EXPORT_PRIVATE WebSocketHandshakeResponseHandler {
- public:
- WebSocketHandshakeResponseHandler();
- ~WebSocketHandshakeResponseHandler();
-
- // Parses WebSocket handshake response from WebSocket server.
- // Returns number of bytes in |data| used for WebSocket handshake response
- // message. 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,
- const std::string& challenge);
- // Parses WebSocket handshake response as SpdyHeaderBlock.
- bool ParseResponseHeaderBlock(const SpdyHeaderBlock& headers,
- const std::string& challenge,
- int spdy_protocol_version);
-
- // 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 raw WebSocket handshake response received from WebSocket server.
- std::string GetRawResponse() const;
-
- // Gets WebSocket handshake response message sent to renderer process.
- std::string GetResponse();
-
- private:
- // Original bytes input by using ParseRawResponse().
- std::string original_;
- // Number of bytes actually used for the handshake response in |original_|.
- int original_header_length_;
-
- std::string status_line_;
- std::string headers_;
- std::string header_separator_;
-
- DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeResponseHandler);
-};
-
} // namespace net
#endif // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
diff --git a/net/websockets/websocket_handshake_handler_spdy_test.cc b/net/websockets/websocket_handshake_handler_spdy_test.cc
deleted file mode 100644
index 064bdcf..0000000
--- a/net/websockets/websocket_handshake_handler_spdy_test.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2013 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 <string>
-
-#include "net/socket/next_proto.h"
-#include "net/spdy/spdy_header_block.h"
-#include "net/spdy/spdy_websocket_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace net {
-
-namespace {
-
-class WebSocketHandshakeHandlerSpdyTest
- : public ::testing::Test,
- public ::testing::WithParamInterface<NextProto> {
- protected:
- WebSocketHandshakeHandlerSpdyTest() : spdy_util_(GetParam()) {}
-
- SpdyWebSocketTestUtil spdy_util_;
-};
-
-INSTANTIATE_TEST_CASE_P(
- NextProto,
- WebSocketHandshakeHandlerSpdyTest,
- testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
-
-TEST_P(WebSocketHandshakeHandlerSpdyTest, RequestResponse) {
- WebSocketHandshakeRequestHandler request_handler;
-
- static const char kHandshakeRequestMessage[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Extensions: foo\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "\r\n";
-
- EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
- strlen(kHandshakeRequestMessage)));
-
- GURL url("ws://example.com/demo");
- std::string challenge;
- SpdyHeaderBlock headers;
- ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url,
- &headers,
- &challenge,
- spdy_util_.spdy_version()));
-
- EXPECT_EQ(url.path(), spdy_util_.GetHeader(headers, "path"));
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "upgrade").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Upgrade").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "connection").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Connection").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Key").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-websocket-key").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Version").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-webSocket-version").empty());
- EXPECT_EQ("example.com", spdy_util_.GetHeader(headers, "host"));
- EXPECT_EQ("http://example.com", spdy_util_.GetHeader(headers, "origin"));
- EXPECT_EQ("sample", spdy_util_.GetHeader(headers, "sec-websocket-protocol"));
- EXPECT_EQ("foo", spdy_util_.GetHeader(headers, "sec-websocket-extensions"));
- EXPECT_EQ("ws", spdy_util_.GetHeader(headers, "scheme"));
- EXPECT_EQ("WebSocket/13", spdy_util_.GetHeader(headers, "version"));
-
- static const char expected_challenge[] = "dGhlIHNhbXBsZSBub25jZQ==";
-
- EXPECT_EQ(expected_challenge, challenge);
-
- headers.clear();
-
- spdy_util_.SetHeader("status", "101 Switching Protocols", &headers);
- spdy_util_.SetHeader("sec-websocket-protocol", "sample", &headers);
- spdy_util_.SetHeader("sec-websocket-extensions", "foo", &headers);
-
- WebSocketHandshakeResponseHandler response_handler;
- EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(
- headers, challenge, spdy_util_.spdy_version()));
- 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 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "sec-websocket-extensions: foo\r\n"
- "sec-websocket-protocol: sample\r\n"
- "\r\n";
-
- EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
-}
-
-TEST_P(WebSocketHandshakeHandlerSpdyTest, RequestResponseWithCookies) {
- 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"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Extensions: foo\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n"
- "\r\n";
-
- EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
- strlen(kHandshakeRequestMessage)));
-
- GURL url("ws://example.com/demo");
- std::string challenge;
- SpdyHeaderBlock headers;
- ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url,
- &headers,
- &challenge,
- spdy_util_.spdy_version()));
-
- EXPECT_EQ(url.path(), spdy_util_.GetHeader(headers, "path"));
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "upgrade").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Upgrade").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "connection").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Connection").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Key").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-websocket-key").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Version").empty());
- EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-webSocket-version").empty());
- EXPECT_EQ("example.com", spdy_util_.GetHeader(headers, "host"));
- EXPECT_EQ("http://example.com", spdy_util_.GetHeader(headers, "origin"));
- EXPECT_EQ("sample", spdy_util_.GetHeader(headers, "sec-websocket-protocol"));
- EXPECT_EQ("foo", spdy_util_.GetHeader(headers, "sec-websocket-extensions"));
- EXPECT_EQ("ws", spdy_util_.GetHeader(headers, "scheme"));
- EXPECT_EQ("WebSocket/13", spdy_util_.GetHeader(headers, "version"));
- EXPECT_EQ("WK-websocket-test=1; WK-websocket-test-httponly=1",
- headers["cookie"]);
-
- const char expected_challenge[] = "dGhlIHNhbXBsZSBub25jZQ==";
-
- EXPECT_EQ(expected_challenge, challenge);
-
- headers.clear();
-
- spdy_util_.SetHeader("status", "101 Switching Protocols", &headers);
- spdy_util_.SetHeader("sec-websocket-protocol", "sample", &headers);
- spdy_util_.SetHeader("sec-websocket-extensions", "foo", &headers);
- 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, spdy_util_.spdy_version()));
- 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 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "sec-websocket-extensions: foo\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";
-
- EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
-}
-
-} // namespace
-
-} // namespace net
diff --git a/net/websockets/websocket_handshake_handler_test.cc b/net/websockets/websocket_handshake_handler_test.cc
index e59a982..8eff571 100644
--- a/net/websockets/websocket_handshake_handler_test.cc
+++ b/net/websockets/websocket_handshake_handler_test.cc
@@ -4,238 +4,14 @@
#include "net/websockets/websocket_handshake_handler.h"
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
-#include "url/gurl.h"
-
#include "testing/gtest/include/gtest/gtest.h"
-namespace {
-
-const char* const kCookieHeaders[] = {
- "cookie", "cookie2"
-};
-
-const char* const kSetCookieHeaders[] = {
- "set-cookie", "set-cookie2"
-};
-
-} // namespace
-
namespace net {
-TEST(WebSocketHandshakeRequestHandlerTest, SimpleRequest) {
- WebSocketHandshakeRequestHandler handler;
-
- static const char kHandshakeRequestMessage[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "\r\n";
-
- 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"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Cookie: WK-websocket-test=1\r\n"
- "\r\n";
-
- 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"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n"
- "\r\n";
-
- EXPECT_EQ(kHandshakeRequestExpectedMessage, handler.GetRawRequest());
-}
-
-TEST(WebSocketHandshakeResponseHandlerTest, SimpleResponse) {
- WebSocketHandshakeResponseHandler handler;
-
- static const char kHandshakeResponseMessage[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "\r\n";
-
- EXPECT_EQ(strlen(kHandshakeResponseMessage),
- handler.ParseRawResponse(kHandshakeResponseMessage,
- strlen(kHandshakeResponseMessage)));
- EXPECT_TRUE(handler.HasResponse());
-
- handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
-
- EXPECT_EQ(kHandshakeResponseMessage, handler.GetResponse());
-}
-
-TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) {
- WebSocketHandshakeResponseHandler handler;
-
- static const char kHandshakeResponseMessage[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\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";
-
- EXPECT_EQ(strlen(kHandshakeResponseMessage),
- handler.ParseRawResponse(kHandshakeResponseMessage,
- strlen(kHandshakeResponseMessage)));
- EXPECT_TRUE(handler.HasResponse());
- 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 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "\r\n";
-
- EXPECT_EQ(kHandshakeResponseExpectedMessage, handler.GetResponse());
-}
-
-TEST(WebSocketHandshakeResponseHandlerTest, BadResponse) {
- WebSocketHandshakeResponseHandler handler;
-
- static const char kBadMessage[] = "\n\n\r\net-Location: w";
- EXPECT_EQ(2U, handler.ParseRawResponse(kBadMessage, strlen(kBadMessage)));
- EXPECT_TRUE(handler.HasResponse());
- EXPECT_EQ("\n\n", handler.GetResponse());
-}
-
-TEST(WebSocketHandshakeResponseHandlerTest, BadResponse2) {
- WebSocketHandshakeResponseHandler handler;
-
- static const char kBadMessage[] = "\n\r\n\r\net-Location: w";
- EXPECT_EQ(3U, handler.ParseRawResponse(kBadMessage, strlen(kBadMessage)));
- EXPECT_TRUE(handler.HasResponse());
- EXPECT_EQ("\n\r\n", handler.GetResponse());
-}
-
-TEST(WebSocketHandshakeHandlerTest, HttpRequestResponse) {
- WebSocketHandshakeRequestHandler request_handler;
-
- static const char kHandshakeRequestMessage[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "\r\n";
-
- 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-Key"));
- std::string value;
- EXPECT_TRUE(request_info.extra_headers.GetHeader("Host", &value));
- EXPECT_EQ("example.com", value);
- EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Origin",
- &value));
- EXPECT_EQ("http://example.com", value);
- EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Protocol",
- &value));
- EXPECT_EQ("sample", value);
-
- EXPECT_EQ("dGhlIHNhbXBsZSBub25jZQ==", challenge);
-
- static const char kHandshakeResponseHeader[] =
- "HTTP/1.1 101 Switching Protocols\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_FALSE(response_info.headers->HasHeader("Sec-WebSocket-Accept"));
- 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());
+namespace {
- static const char kHandshakeResponseExpectedMessage[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "\r\n";
+// TODO(ricea): Put a test for ComputeSecWebSocketAccept() here.
- EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
-}
+} // namespace
} // namespace net
diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc
deleted file mode 100644
index eb653fa..0000000
--- a/net/websockets/websocket_job.cc
+++ /dev/null
@@ -1,693 +0,0 @@
-// Copyright (c) 2012 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_job.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-#include "net/cookies/cookie_store.h"
-#include "net/http/http_network_session.h"
-#include "net/http/http_transaction_factory.h"
-#include "net/http/http_util.h"
-#include "net/spdy/spdy_session.h"
-#include "net/spdy/spdy_session_pool.h"
-#include "net/url_request/url_request_context.h"
-#include "net/websockets/websocket_handshake_handler.h"
-#include "net/websockets/websocket_net_log_params.h"
-#include "net/websockets/websocket_throttle.h"
-#include "url/gurl.h"
-
-static const int kMaxPendingSendAllowed = 32768; // 32 kilobytes.
-
-namespace {
-
-// lower-case header names.
-const char* const kCookieHeaders[] = {
- "cookie", "cookie2"
-};
-const char* const kSetCookieHeaders[] = {
- "set-cookie", "set-cookie2"
-};
-
-net::SocketStreamJob* WebSocketJobFactory(
- const GURL& url, net::SocketStream::Delegate* delegate,
- net::URLRequestContext* context, net::CookieStore* cookie_store) {
- net::WebSocketJob* job = new net::WebSocketJob(delegate);
- job->InitSocketStream(new net::SocketStream(url, job, context, cookie_store));
- return job;
-}
-
-class WebSocketJobInitSingleton {
- private:
- friend struct base::DefaultLazyInstanceTraits<WebSocketJobInitSingleton>;
- WebSocketJobInitSingleton() {
- net::SocketStreamJob::RegisterProtocolFactory("ws", WebSocketJobFactory);
- net::SocketStreamJob::RegisterProtocolFactory("wss", WebSocketJobFactory);
- }
-};
-
-static base::LazyInstance<WebSocketJobInitSingleton> g_websocket_job_init =
- LAZY_INSTANCE_INITIALIZER;
-
-} // anonymous namespace
-
-namespace net {
-
-// static
-void WebSocketJob::EnsureInit() {
- g_websocket_job_init.Get();
-}
-
-WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate)
- : delegate_(delegate),
- state_(INITIALIZED),
- waiting_(false),
- handshake_request_(new WebSocketHandshakeRequestHandler),
- handshake_response_(new WebSocketHandshakeResponseHandler),
- started_to_send_handshake_request_(false),
- handshake_request_sent_(0),
- response_cookies_save_index_(0),
- spdy_protocol_version_(0),
- save_next_cookie_running_(false),
- callback_pending_(false),
- weak_ptr_factory_(this),
- weak_ptr_factory_for_send_pending_(this) {
-}
-
-WebSocketJob::~WebSocketJob() {
- DCHECK_EQ(CLOSED, state_);
- DCHECK(!delegate_);
- DCHECK(!socket_.get());
-}
-
-void WebSocketJob::Connect() {
- DCHECK(socket_.get());
- DCHECK_EQ(state_, INITIALIZED);
- state_ = CONNECTING;
- socket_->Connect();
-}
-
-bool WebSocketJob::SendData(const char* data, int len) {
- switch (state_) {
- case INITIALIZED:
- return false;
-
- case CONNECTING:
- return SendHandshakeRequest(data, len);
-
- case OPEN:
- {
- scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(len);
- memcpy(buffer->data(), data, len);
- if (current_send_buffer_.get() || !send_buffer_queue_.empty()) {
- send_buffer_queue_.push_back(buffer);
- return true;
- }
- current_send_buffer_ = new DrainableIOBuffer(buffer.get(), len);
- return SendDataInternal(current_send_buffer_->data(),
- current_send_buffer_->BytesRemaining());
- }
-
- case CLOSING:
- case CLOSED:
- return false;
- }
- return false;
-}
-
-void WebSocketJob::Close() {
- if (state_ == CLOSED)
- return;
-
- state_ = CLOSING;
- if (current_send_buffer_.get()) {
- // Will close in SendPending.
- return;
- }
- state_ = CLOSED;
- CloseInternal();
-}
-
-void WebSocketJob::RestartWithAuth(const AuthCredentials& credentials) {
- state_ = CONNECTING;
- socket_->RestartWithAuth(credentials);
-}
-
-void WebSocketJob::DetachDelegate() {
- state_ = CLOSED;
- WebSocketThrottle::GetInstance()->RemoveFromQueue(this);
-
- scoped_refptr<WebSocketJob> protect(this);
- weak_ptr_factory_.InvalidateWeakPtrs();
- weak_ptr_factory_for_send_pending_.InvalidateWeakPtrs();
-
- delegate_ = NULL;
- if (socket_.get())
- socket_->DetachDelegate();
- socket_ = NULL;
- if (!callback_.is_null()) {
- waiting_ = false;
- callback_.Reset();
- Release(); // Balanced with OnStartOpenConnection().
- }
-}
-
-int WebSocketJob::OnStartOpenConnection(
- SocketStream* socket, const CompletionCallback& callback) {
- DCHECK(callback_.is_null());
- state_ = CONNECTING;
-
- addresses_ = socket->address_list();
- if (!WebSocketThrottle::GetInstance()->PutInQueue(this)) {
- return ERR_WS_THROTTLE_QUEUE_TOO_LARGE;
- }
-
- if (delegate_) {
- int result = delegate_->OnStartOpenConnection(socket, callback);
- DCHECK_EQ(OK, result);
- }
- if (waiting_) {
- // PutInQueue() may set |waiting_| true for throttling. In this case,
- // Wakeup() will be called later.
- callback_ = callback;
- AddRef(); // Balanced when callback_ is cleared.
- return ERR_IO_PENDING;
- }
- return TrySpdyStream();
-}
-
-void WebSocketJob::OnConnected(
- SocketStream* socket, int max_pending_send_allowed) {
- if (state_ == CLOSED)
- return;
- DCHECK_EQ(CONNECTING, state_);
- if (delegate_)
- delegate_->OnConnected(socket, max_pending_send_allowed);
-}
-
-void WebSocketJob::OnSentData(SocketStream* socket, int amount_sent) {
- DCHECK_NE(INITIALIZED, state_);
- DCHECK_GT(amount_sent, 0);
- if (state_ == CLOSED)
- return;
- if (state_ == CONNECTING) {
- OnSentHandshakeRequest(socket, amount_sent);
- return;
- }
- if (delegate_) {
- DCHECK(state_ == OPEN || state_ == CLOSING);
- if (!current_send_buffer_.get()) {
- VLOG(1)
- << "OnSentData current_send_buffer=NULL amount_sent=" << amount_sent;
- return;
- }
- current_send_buffer_->DidConsume(amount_sent);
- if (current_send_buffer_->BytesRemaining() > 0)
- return;
-
- // We need to report amount_sent of original buffer size, instead of
- // amount sent to |socket|.
- amount_sent = current_send_buffer_->size();
- DCHECK_GT(amount_sent, 0);
- current_send_buffer_ = NULL;
- if (!weak_ptr_factory_for_send_pending_.HasWeakPtrs()) {
- base::MessageLoopForIO::current()->PostTask(
- FROM_HERE,
- base::Bind(&WebSocketJob::SendPending,
- weak_ptr_factory_for_send_pending_.GetWeakPtr()));
- }
- delegate_->OnSentData(socket, amount_sent);
- }
-}
-
-void WebSocketJob::OnReceivedData(
- SocketStream* socket, const char* data, int len) {
- DCHECK_NE(INITIALIZED, state_);
- if (state_ == CLOSED)
- return;
- if (state_ == CONNECTING) {
- OnReceivedHandshakeResponse(socket, data, len);
- return;
- }
- DCHECK(state_ == OPEN || state_ == CLOSING);
- if (delegate_ && len > 0)
- delegate_->OnReceivedData(socket, data, len);
-}
-
-void WebSocketJob::OnClose(SocketStream* socket) {
- state_ = CLOSED;
- WebSocketThrottle::GetInstance()->RemoveFromQueue(this);
-
- scoped_refptr<WebSocketJob> protect(this);
- weak_ptr_factory_.InvalidateWeakPtrs();
-
- SocketStream::Delegate* delegate = delegate_;
- delegate_ = NULL;
- socket_ = NULL;
- if (!callback_.is_null()) {
- waiting_ = false;
- callback_.Reset();
- Release(); // Balanced with OnStartOpenConnection().
- }
- if (delegate)
- delegate->OnClose(socket);
-}
-
-void WebSocketJob::OnAuthRequired(
- SocketStream* socket, AuthChallengeInfo* auth_info) {
- if (delegate_)
- delegate_->OnAuthRequired(socket, auth_info);
-}
-
-void WebSocketJob::OnSSLCertificateError(
- SocketStream* socket, const SSLInfo& ssl_info, bool fatal) {
- if (delegate_)
- delegate_->OnSSLCertificateError(socket, ssl_info, fatal);
-}
-
-void WebSocketJob::OnError(const SocketStream* socket, int error) {
- if (delegate_ && error != ERR_PROTOCOL_SWITCHED)
- delegate_->OnError(socket, error);
-}
-
-void WebSocketJob::OnCreatedSpdyStream(int result) {
- DCHECK(spdy_websocket_stream_.get());
- DCHECK(socket_.get());
- DCHECK_NE(ERR_IO_PENDING, result);
-
- if (state_ == CLOSED) {
- result = ERR_ABORTED;
- } else if (result == OK) {
- state_ = CONNECTING;
- result = ERR_PROTOCOL_SWITCHED;
- } else {
- spdy_websocket_stream_.reset();
- }
-
- CompleteIO(result);
-}
-
-void WebSocketJob::OnSentSpdyHeaders() {
- DCHECK_NE(INITIALIZED, state_);
- if (state_ != CONNECTING)
- return;
- size_t original_length = handshake_request_->original_length();
- handshake_request_.reset();
- if (delegate_)
- delegate_->OnSentData(socket_.get(), original_length);
-}
-
-void WebSocketJob::OnSpdyResponseHeadersUpdated(
- const SpdyHeaderBlock& response_headers) {
- DCHECK_NE(INITIALIZED, state_);
- if (state_ != CONNECTING)
- return;
- // TODO(toyoshim): Fallback to non-spdy connection?
- handshake_response_->ParseResponseHeaderBlock(response_headers,
- challenge_,
- spdy_protocol_version_);
-
- SaveCookiesAndNotifyHeadersComplete();
-}
-
-void WebSocketJob::OnSentSpdyData(size_t bytes_sent) {
- DCHECK_NE(INITIALIZED, state_);
- DCHECK_NE(CONNECTING, state_);
- if (state_ == CLOSED)
- return;
- if (!spdy_websocket_stream_.get())
- return;
- OnSentData(socket_.get(), static_cast<int>(bytes_sent));
-}
-
-void WebSocketJob::OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) {
- DCHECK_NE(INITIALIZED, state_);
- DCHECK_NE(CONNECTING, state_);
- if (state_ == CLOSED)
- return;
- if (!spdy_websocket_stream_.get())
- return;
- if (buffer) {
- OnReceivedData(
- socket_.get(), buffer->GetRemainingData(), buffer->GetRemainingSize());
- } else {
- OnReceivedData(socket_.get(), NULL, 0);
- }
-}
-
-void WebSocketJob::OnCloseSpdyStream() {
- spdy_websocket_stream_.reset();
- OnClose(socket_.get());
-}
-
-bool WebSocketJob::SendHandshakeRequest(const char* data, int len) {
- DCHECK_EQ(state_, CONNECTING);
- if (started_to_send_handshake_request_)
- return false;
- if (!handshake_request_->ParseRequest(data, len))
- return false;
-
- AddCookieHeaderAndSend();
- return true;
-}
-
-void WebSocketJob::AddCookieHeaderAndSend() {
- bool allow = true;
- if (delegate_ && !delegate_->CanGetCookies(socket_.get(), GetURLForCookies()))
- allow = false;
-
- if (socket_.get() && delegate_ && state_ == CONNECTING) {
- handshake_request_->RemoveHeaders(kCookieHeaders,
- arraysize(kCookieHeaders));
- if (allow && socket_->cookie_store()) {
- // Add cookies, including HttpOnly cookies.
- CookieOptions cookie_options;
- cookie_options.set_include_httponly();
- socket_->cookie_store()->GetCookiesWithOptionsAsync(
- GetURLForCookies(), cookie_options,
- base::Bind(&WebSocketJob::LoadCookieCallback,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- DoSendData();
- }
- }
-}
-
-void WebSocketJob::LoadCookieCallback(const std::string& cookie) {
- if (!cookie.empty())
- // TODO(tyoshino): Sending cookie means that connection doesn't need
- // PRIVACY_MODE_ENABLED as cookies may be server-bound and channel id
- // wouldn't negatively affect privacy anyway. Need to restart connection
- // or refactor to determine cookie status prior to connecting.
- handshake_request_->AppendHeaderIfMissing("Cookie", cookie);
- DoSendData();
-}
-
-void WebSocketJob::DoSendData() {
- if (spdy_websocket_stream_.get()) {
- scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
- handshake_request_->GetRequestHeaderBlock(
- socket_->url(), headers.get(), &challenge_, spdy_protocol_version_);
- spdy_websocket_stream_->SendRequest(headers.Pass());
- } else {
- const std::string& handshake_request =
- handshake_request_->GetRawRequest();
- handshake_request_sent_ = 0;
- socket_->net_log()->AddEvent(
- NetLog::TYPE_WEB_SOCKET_SEND_REQUEST_HEADERS,
- base::Bind(&NetLogWebSocketHandshakeCallback, &handshake_request));
- socket_->SendData(handshake_request.data(),
- handshake_request.size());
- }
- // Just buffered in |handshake_request_|.
- started_to_send_handshake_request_ = true;
-}
-
-void WebSocketJob::OnSentHandshakeRequest(
- SocketStream* socket, int amount_sent) {
- DCHECK_EQ(state_, CONNECTING);
- handshake_request_sent_ += amount_sent;
- 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.
- // Reset the handshake_request_ first in case this object is deleted by the
- // delegate.
- size_t original_length = handshake_request_->original_length();
- handshake_request_.reset();
- if (delegate_)
- delegate_->OnSentData(socket, original_length);
- }
-}
-
-void WebSocketJob::OnReceivedHandshakeResponse(
- SocketStream* socket, const char* data, int len) {
- DCHECK_EQ(state_, CONNECTING);
- if (handshake_response_->HasResponse()) {
- // If we already has handshake response, received data should be frame
- // data, not handshake message.
- received_data_after_handshake_.insert(
- received_data_after_handshake_.end(), data, data + len);
- return;
- }
-
- size_t response_length = handshake_response_->ParseRawResponse(data, len);
- if (!handshake_response_->HasResponse()) {
- // not yet. we need more data.
- return;
- }
- // handshake message is completed.
- std::string raw_response = handshake_response_->GetRawResponse();
- socket_->net_log()->AddEvent(
- NetLog::TYPE_WEB_SOCKET_READ_RESPONSE_HEADERS,
- base::Bind(&NetLogWebSocketHandshakeCallback, &raw_response));
- if (len - response_length > 0) {
- // If we received extra data, it should be frame data.
- DCHECK(received_data_after_handshake_.empty());
- received_data_after_handshake_.assign(data + response_length, data + len);
- }
- SaveCookiesAndNotifyHeadersComplete();
-}
-
-void WebSocketJob::SaveCookiesAndNotifyHeadersComplete() {
- // handshake message is completed.
- DCHECK(handshake_response_->HasResponse());
-
- // Extract cookies from the handshake response into a temporary vector.
- response_cookies_.clear();
- response_cookies_save_index_ = 0;
-
- handshake_response_->GetHeaders(
- kSetCookieHeaders, arraysize(kSetCookieHeaders), &response_cookies_);
-
- // Now, loop over the response cookies, and attempt to persist each.
- SaveNextCookie();
-}
-
-void WebSocketJob::NotifyHeadersComplete() {
- // Remove cookie headers, with malformed headers preserved.
- // Actual handshake should be done in Blink.
- handshake_response_->RemoveHeaders(
- kSetCookieHeaders, arraysize(kSetCookieHeaders));
- std::string handshake_response = handshake_response_->GetResponse();
- handshake_response_.reset();
- std::vector<char> received_data(handshake_response.begin(),
- handshake_response.end());
- received_data.insert(received_data.end(),
- received_data_after_handshake_.begin(),
- received_data_after_handshake_.end());
- received_data_after_handshake_.clear();
-
- state_ = OPEN;
-
- DCHECK(!received_data.empty());
- if (delegate_)
- delegate_->OnReceivedData(
- socket_.get(), &received_data.front(), received_data.size());
-
- WebSocketThrottle::GetInstance()->RemoveFromQueue(this);
-}
-
-void WebSocketJob::SaveNextCookie() {
- if (!socket_.get() || !delegate_ || state_ != CONNECTING)
- return;
-
- callback_pending_ = false;
- save_next_cookie_running_ = true;
-
- if (socket_->cookie_store()) {
- GURL url_for_cookies = GetURLForCookies();
-
- CookieOptions options;
- options.set_include_httponly();
-
- // Loop as long as SetCookieWithOptionsAsync completes synchronously. Since
- // CookieMonster's asynchronous operation APIs queue the callback to run it
- // on the thread where the API was called, there won't be race. I.e. unless
- // the callback is run synchronously, it won't be run in parallel with this
- // method.
- while (!callback_pending_ &&
- response_cookies_save_index_ < response_cookies_.size()) {
- std::string cookie = response_cookies_[response_cookies_save_index_];
- response_cookies_save_index_++;
-
- if (!delegate_->CanSetCookie(
- socket_.get(), url_for_cookies, cookie, &options))
- continue;
-
- callback_pending_ = true;
- socket_->cookie_store()->SetCookieWithOptionsAsync(
- url_for_cookies, cookie, options,
- base::Bind(&WebSocketJob::OnCookieSaved,
- weak_ptr_factory_.GetWeakPtr()));
- }
- }
-
- save_next_cookie_running_ = false;
-
- if (callback_pending_)
- return;
-
- response_cookies_.clear();
- response_cookies_save_index_ = 0;
-
- NotifyHeadersComplete();
-}
-
-void WebSocketJob::OnCookieSaved(bool cookie_status) {
- // Tell the caller of SetCookieWithOptionsAsync() that this completion
- // callback is invoked.
- // - If the caller checks callback_pending earlier than this callback, the
- // caller exits to let this method continue iteration.
- // - Otherwise, the caller continues iteration.
- callback_pending_ = false;
-
- // Resume SaveNextCookie if the caller of SetCookieWithOptionsAsync() exited
- // the loop. Otherwise, return.
- if (save_next_cookie_running_)
- return;
-
- SaveNextCookie();
-}
-
-GURL WebSocketJob::GetURLForCookies() const {
- GURL url = socket_->url();
- std::string scheme = socket_->is_secure() ? "https" : "http";
- url::Replacements<char> replacements;
- replacements.SetScheme(scheme.c_str(), url::Component(0, scheme.length()));
- return url.ReplaceComponents(replacements);
-}
-
-const AddressList& WebSocketJob::address_list() const {
- return addresses_;
-}
-
-int WebSocketJob::TrySpdyStream() {
- if (!socket_.get())
- return ERR_FAILED;
-
- // Check if we have a SPDY session available.
- HttpTransactionFactory* factory =
- socket_->context()->http_transaction_factory();
- if (!factory)
- return OK;
- scoped_refptr<HttpNetworkSession> session = factory->GetSession();
- if (!session.get() || !session->params().enable_websocket_over_spdy)
- return OK;
- SpdySessionPool* spdy_pool = session->spdy_session_pool();
- PrivacyMode privacy_mode = socket_->privacy_mode();
- const SpdySessionKey key(HostPortPair::FromURL(socket_->url()),
- socket_->proxy_server(), privacy_mode);
- // Forbid wss downgrade to SPDY without SSL.
- // TODO(toyoshim): Does it realize the same policy with HTTP?
- base::WeakPtr<SpdySession> spdy_session =
- spdy_pool->FindAvailableSession(key, *socket_->net_log());
- if (!spdy_session)
- return OK;
-
- SSLInfo ssl_info;
- bool was_npn_negotiated;
- NextProto protocol_negotiated = kProtoUnknown;
- bool use_ssl = spdy_session->GetSSLInfo(
- &ssl_info, &was_npn_negotiated, &protocol_negotiated);
- if (socket_->is_secure() && !use_ssl)
- return OK;
-
- // Create SpdyWebSocketStream.
- spdy_protocol_version_ = spdy_session->GetProtocolVersion();
- spdy_websocket_stream_.reset(new SpdyWebSocketStream(spdy_session, this));
-
- int result = spdy_websocket_stream_->InitializeStream(
- socket_->url(), MEDIUM, *socket_->net_log());
- if (result == OK) {
- OnConnected(socket_.get(), kMaxPendingSendAllowed);
- return ERR_PROTOCOL_SWITCHED;
- }
- if (result != ERR_IO_PENDING) {
- spdy_websocket_stream_.reset();
- return OK;
- }
-
- return ERR_IO_PENDING;
-}
-
-void WebSocketJob::SetWaiting() {
- waiting_ = true;
-}
-
-bool WebSocketJob::IsWaiting() const {
- return waiting_;
-}
-
-void WebSocketJob::Wakeup() {
- if (!waiting_)
- return;
- waiting_ = false;
- DCHECK(!callback_.is_null());
- base::MessageLoopForIO::current()->PostTask(
- FROM_HERE,
- base::Bind(&WebSocketJob::RetryPendingIO,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void WebSocketJob::RetryPendingIO() {
- int result = TrySpdyStream();
-
- // In the case of ERR_IO_PENDING, CompleteIO() will be called from
- // OnCreatedSpdyStream().
- if (result != ERR_IO_PENDING)
- CompleteIO(result);
-}
-
-void WebSocketJob::CompleteIO(int result) {
- // |callback_| may be null if OnClose() or DetachDelegate() was called.
- if (!callback_.is_null()) {
- CompletionCallback callback = callback_;
- callback_.Reset();
- callback.Run(result);
- Release(); // Balanced with OnStartOpenConnection().
- }
-}
-
-bool WebSocketJob::SendDataInternal(const char* data, int length) {
- if (spdy_websocket_stream_.get())
- return ERR_IO_PENDING == spdy_websocket_stream_->SendData(data, length);
- if (socket_.get())
- return socket_->SendData(data, length);
- return false;
-}
-
-void WebSocketJob::CloseInternal() {
- if (spdy_websocket_stream_.get())
- spdy_websocket_stream_->Close();
- if (socket_.get())
- socket_->Close();
-}
-
-void WebSocketJob::SendPending() {
- if (current_send_buffer_.get())
- return;
-
- // Current buffer has been sent. Try next if any.
- if (send_buffer_queue_.empty()) {
- // No more data to send.
- if (state_ == CLOSING)
- CloseInternal();
- return;
- }
-
- scoped_refptr<IOBufferWithSize> next_buffer = send_buffer_queue_.front();
- send_buffer_queue_.pop_front();
- current_send_buffer_ =
- new DrainableIOBuffer(next_buffer.get(), next_buffer->size());
- SendDataInternal(current_send_buffer_->data(),
- current_send_buffer_->BytesRemaining());
-}
-
-} // namespace net
diff --git a/net/websockets/websocket_job.h b/net/websockets/websocket_job.h
deleted file mode 100644
index bad06cf..0000000
--- a/net/websockets/websocket_job.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef NET_WEBSOCKETS_WEBSOCKET_JOB_H_
-#define NET_WEBSOCKETS_WEBSOCKET_JOB_H_
-
-#include <deque>
-#include <string>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "net/base/address_list.h"
-#include "net/base/completion_callback.h"
-#include "net/socket_stream/socket_stream_job.h"
-#include "net/spdy/spdy_header_block.h"
-#include "net/spdy/spdy_websocket_stream.h"
-
-class GURL;
-
-namespace net {
-
-class DrainableIOBuffer;
-class SSLInfo;
-class WebSocketHandshakeRequestHandler;
-class WebSocketHandshakeResponseHandler;
-
-// WebSocket protocol specific job on SocketStream.
-// It captures WebSocket handshake message and handles cookie operations.
-// Chrome security policy doesn't allow renderer process (except dev tools)
-// see HttpOnly cookies, so it injects cookie header in handshake request and
-// strips set-cookie headers in handshake response.
-// TODO(ukai): refactor websocket.cc to use this.
-class NET_EXPORT WebSocketJob
- : public SocketStreamJob,
- public SocketStream::Delegate,
- public SpdyWebSocketStream::Delegate {
- public:
- // This is state of WebSocket, not SocketStream.
- enum State {
- INITIALIZED = -1,
- CONNECTING = 0,
- OPEN = 1,
- CLOSING = 2,
- CLOSED = 3,
- };
-
- explicit WebSocketJob(SocketStream::Delegate* delegate);
-
- static void EnsureInit();
-
- State state() const { return state_; }
- void Connect() override;
- bool SendData(const char* data, int len) override;
- void Close() override;
- void RestartWithAuth(const AuthCredentials& credentials) override;
- void DetachDelegate() override;
-
- // SocketStream::Delegate methods.
- int OnStartOpenConnection(SocketStream* socket,
- const CompletionCallback& callback) override;
- void OnConnected(SocketStream* socket, int max_pending_send_allowed) override;
- void OnSentData(SocketStream* socket, int amount_sent) override;
- void OnReceivedData(SocketStream* socket, const char* data, int len) override;
- void OnClose(SocketStream* socket) override;
- void OnAuthRequired(SocketStream* socket,
- AuthChallengeInfo* auth_info) override;
- void OnSSLCertificateError(SocketStream* socket,
- const SSLInfo& ssl_info,
- bool fatal) override;
- void OnError(const SocketStream* socket, int error) override;
-
- // SpdyWebSocketStream::Delegate methods.
- void OnCreatedSpdyStream(int status) override;
- void OnSentSpdyHeaders() override;
- void OnSpdyResponseHeadersUpdated(
- const SpdyHeaderBlock& response_headers) override;
- void OnSentSpdyData(size_t bytes_sent) override;
- void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) override;
- void OnCloseSpdyStream() override;
-
- private:
- friend class WebSocketThrottle;
- friend class WebSocketJobTest;
- ~WebSocketJob() override;
-
- bool SendHandshakeRequest(const char* data, int len);
- void AddCookieHeaderAndSend();
- void LoadCookieCallback(const std::string& cookie);
-
- void OnSentHandshakeRequest(SocketStream* socket, int amount_sent);
- // Parses received data into handshake_response_. When finished receiving the
- // response, calls SaveCookiesAndNotifyHeadersComplete().
- void OnReceivedHandshakeResponse(
- SocketStream* socket, const char* data, int len);
- // Saves received cookies to the cookie store, and then notifies the
- // delegate_ of completion of handshake.
- void SaveCookiesAndNotifyHeadersComplete();
- void SaveNextCookie();
- void OnCookieSaved(bool cookie_status);
- // Clears variables for handling cookies, rebuilds handshake string excluding
- // cookies, and then pass the handshake string to delegate_.
- void NotifyHeadersComplete();
- void DoSendData();
-
- GURL GetURLForCookies() const;
-
- const AddressList& address_list() const;
- int TrySpdyStream();
- void SetWaiting();
- bool IsWaiting() const;
- void Wakeup();
- void RetryPendingIO();
- void CompleteIO(int result);
-
- bool SendDataInternal(const char* data, int length);
- void CloseInternal();
- void SendPending();
-
- SocketStream::Delegate* delegate_;
- State state_;
- bool waiting_;
- AddressList addresses_;
- CompletionCallback callback_; // for throttling.
-
- scoped_ptr<WebSocketHandshakeRequestHandler> handshake_request_;
- scoped_ptr<WebSocketHandshakeResponseHandler> handshake_response_;
-
- bool started_to_send_handshake_request_;
- size_t handshake_request_sent_;
-
- std::vector<std::string> response_cookies_;
- size_t response_cookies_save_index_;
-
- std::deque<scoped_refptr<IOBufferWithSize> > send_buffer_queue_;
- scoped_refptr<DrainableIOBuffer> current_send_buffer_;
- std::vector<char> received_data_after_handshake_;
-
- int spdy_protocol_version_;
- scoped_ptr<SpdyWebSocketStream> spdy_websocket_stream_;
- std::string challenge_;
-
- bool save_next_cookie_running_;
- bool callback_pending_;
-
- base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_;
- base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_for_send_pending_;
-
- DISALLOW_COPY_AND_ASSIGN(WebSocketJob);
-};
-
-} // namespace
-
-#endif // NET_WEBSOCKETS_WEBSOCKET_JOB_H_
diff --git a/net/websockets/websocket_job_test.cc b/net/websockets/websocket_job_test.cc
deleted file mode 100644
index c84af8f..0000000
--- a/net/websockets/websocket_job_test.cc
+++ /dev/null
@@ -1,1287 +0,0 @@
-// Copyright 2013 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_job.h"
-
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_errors.h"
-#include "net/base/test_completion_callback.h"
-#include "net/cookies/cookie_store.h"
-#include "net/cookies/cookie_store_test_helpers.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/http/http_transaction_factory.h"
-#include "net/http/transport_security_state.h"
-#include "net/proxy/proxy_service.h"
-#include "net/socket/next_proto.h"
-#include "net/socket/socket_test_util.h"
-#include "net/socket_stream/socket_stream.h"
-#include "net/spdy/spdy_session.h"
-#include "net/spdy/spdy_websocket_test_util.h"
-#include "net/ssl/ssl_config_service.h"
-#include "net/url_request/url_request_context.h"
-#include "net/websockets/websocket_throttle.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-#include "url/gurl.h"
-
-namespace net {
-
-namespace {
-
-class MockSocketStream : public SocketStream {
- public:
- MockSocketStream(const GURL& url, SocketStream::Delegate* delegate,
- URLRequestContext* context, CookieStore* cookie_store)
- : SocketStream(url, delegate, context, cookie_store) {}
-
- void Connect() override {}
- bool SendData(const char* data, int len) override {
- sent_data_ += std::string(data, len);
- return true;
- }
-
- void Close() override {}
- void RestartWithAuth(const AuthCredentials& credentials) override {}
-
- void DetachDelegate() override { delegate_ = NULL; }
-
- const std::string& sent_data() const {
- return sent_data_;
- }
-
- protected:
- ~MockSocketStream() override {}
-
- private:
- std::string sent_data_;
-};
-
-class MockSocketStreamDelegate : public SocketStream::Delegate {
- public:
- MockSocketStreamDelegate()
- : amount_sent_(0), allow_all_cookies_(true) {}
- void set_allow_all_cookies(bool allow_all_cookies) {
- allow_all_cookies_ = allow_all_cookies;
- }
- ~MockSocketStreamDelegate() override {}
-
- void SetOnStartOpenConnection(const base::Closure& callback) {
- on_start_open_connection_ = callback;
- }
- void SetOnConnected(const base::Closure& callback) {
- on_connected_ = callback;
- }
- void SetOnSentData(const base::Closure& callback) {
- on_sent_data_ = callback;
- }
- void SetOnReceivedData(const base::Closure& callback) {
- on_received_data_ = callback;
- }
- void SetOnClose(const base::Closure& callback) {
- on_close_ = callback;
- }
-
- int OnStartOpenConnection(SocketStream* socket,
- const CompletionCallback& callback) override {
- if (!on_start_open_connection_.is_null())
- on_start_open_connection_.Run();
- return OK;
- }
- void OnConnected(SocketStream* socket,
- int max_pending_send_allowed) override {
- if (!on_connected_.is_null())
- on_connected_.Run();
- }
- void OnSentData(SocketStream* socket, int amount_sent) override {
- amount_sent_ += amount_sent;
- if (!on_sent_data_.is_null())
- on_sent_data_.Run();
- }
- void OnReceivedData(SocketStream* socket,
- const char* data,
- int len) override {
- received_data_ += std::string(data, len);
- if (!on_received_data_.is_null())
- on_received_data_.Run();
- }
- void OnClose(SocketStream* socket) override {
- if (!on_close_.is_null())
- on_close_.Run();
- }
- bool CanGetCookies(SocketStream* socket, const GURL& url) override {
- return allow_all_cookies_;
- }
- bool CanSetCookie(SocketStream* request,
- const GURL& url,
- const std::string& cookie_line,
- CookieOptions* options) override {
- return allow_all_cookies_;
- }
-
- size_t amount_sent() const { return amount_sent_; }
- const std::string& received_data() const { return received_data_; }
-
- private:
- int amount_sent_;
- bool allow_all_cookies_;
- std::string received_data_;
- base::Closure on_start_open_connection_;
- base::Closure on_connected_;
- base::Closure on_sent_data_;
- base::Closure on_received_data_;
- base::Closure on_close_;
-};
-
-class MockCookieStore : public CookieStore {
- public:
- struct Entry {
- GURL url;
- std::string cookie_line;
- CookieOptions options;
- };
-
- MockCookieStore() {}
-
- bool SetCookieWithOptions(const GURL& url,
- const std::string& cookie_line,
- const CookieOptions& options) {
- Entry entry;
- entry.url = url;
- entry.cookie_line = cookie_line;
- entry.options = options;
- entries_.push_back(entry);
- return true;
- }
-
- std::string GetCookiesWithOptions(const GURL& url,
- const CookieOptions& options) {
- std::string result;
- for (size_t i = 0; i < entries_.size(); i++) {
- Entry& entry = entries_[i];
- if (url == entry.url) {
- if (!result.empty()) {
- result += "; ";
- }
- result += entry.cookie_line;
- }
- }
- return result;
- }
-
- // CookieStore:
- void SetCookieWithOptionsAsync(const GURL& url,
- const std::string& cookie_line,
- const CookieOptions& options,
- const SetCookiesCallback& callback) override {
- bool result = SetCookieWithOptions(url, cookie_line, options);
- if (!callback.is_null())
- callback.Run(result);
- }
-
- void GetCookiesWithOptionsAsync(const GURL& url,
- const CookieOptions& options,
- const GetCookiesCallback& callback) override {
- if (!callback.is_null())
- callback.Run(GetCookiesWithOptions(url, options));
- }
-
- void GetAllCookiesForURLAsync(
- const GURL& url,
- const GetCookieListCallback& callback) override {
- ADD_FAILURE();
- }
-
- void DeleteCookieAsync(const GURL& url,
- const std::string& cookie_name,
- const base::Closure& callback) override {
- ADD_FAILURE();
- }
-
- void DeleteAllCreatedBetweenAsync(const base::Time& delete_begin,
- const base::Time& delete_end,
- const DeleteCallback& callback) override {
- ADD_FAILURE();
- }
-
- void DeleteAllCreatedBetweenForHostAsync(
- const base::Time delete_begin,
- const base::Time delete_end,
- const GURL& url,
- const DeleteCallback& callback) override {
- ADD_FAILURE();
- }
-
- void DeleteSessionCookiesAsync(const DeleteCallback&) override {
- ADD_FAILURE();
- }
-
- CookieMonster* GetCookieMonster() override { return NULL; }
-
- scoped_ptr<CookieStore::CookieChangedSubscription>
- AddCallbackForCookie(const GURL& url, const std::string& name,
- const CookieChangedCallback& callback) override {
- ADD_FAILURE();
- return scoped_ptr<CookieChangedSubscription>();
- }
-
- const std::vector<Entry>& entries() const { return entries_; }
-
- private:
- friend class base::RefCountedThreadSafe<MockCookieStore>;
- ~MockCookieStore() override {}
-
- std::vector<Entry> entries_;
-};
-
-class MockSSLConfigService : public SSLConfigService {
- public:
- void GetSSLConfig(SSLConfig* config) override {}
-
- protected:
- ~MockSSLConfigService() override {}
-};
-
-class MockURLRequestContext : public URLRequestContext {
- public:
- explicit MockURLRequestContext(CookieStore* cookie_store)
- : transport_security_state_() {
- set_cookie_store(cookie_store);
- set_transport_security_state(&transport_security_state_);
- base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
- bool include_subdomains = false;
- transport_security_state_.AddHSTS("upgrademe.com", expiry,
- include_subdomains);
- }
-
- ~MockURLRequestContext() override { AssertNoURLRequests(); }
-
- private:
- TransportSecurityState transport_security_state_;
-};
-
-class MockHttpTransactionFactory : public HttpTransactionFactory {
- public:
- MockHttpTransactionFactory(NextProto next_proto,
- OrderedSocketData* data,
- bool enable_websocket_over_spdy) {
- data_ = data;
- MockConnect connect_data(SYNCHRONOUS, OK);
- data_->set_connect_data(connect_data);
- session_deps_.reset(new SpdySessionDependencies(next_proto));
- session_deps_->enable_websocket_over_spdy = enable_websocket_over_spdy;
- session_deps_->socket_factory->AddSocketDataProvider(data_);
- http_session_ =
- SpdySessionDependencies::SpdyCreateSession(session_deps_.get());
- host_port_pair_.set_host("example.com");
- host_port_pair_.set_port(80);
- spdy_session_key_ = SpdySessionKey(host_port_pair_,
- ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED);
- session_ = CreateInsecureSpdySession(
- http_session_, spdy_session_key_, BoundNetLog());
- }
-
- int CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans) override {
- NOTREACHED();
- return ERR_UNEXPECTED;
- }
-
- HttpCache* GetCache() override {
- NOTREACHED();
- return NULL;
- }
-
- HttpNetworkSession* GetSession() override { return http_session_.get(); }
-
- private:
- OrderedSocketData* data_;
- scoped_ptr<SpdySessionDependencies> session_deps_;
- scoped_refptr<HttpNetworkSession> http_session_;
- base::WeakPtr<SpdySession> session_;
- HostPortPair host_port_pair_;
- SpdySessionKey spdy_session_key_;
-};
-
-class DeletingSocketStreamDelegate : public SocketStream::Delegate {
- public:
- DeletingSocketStreamDelegate()
- : delete_next_(false) {}
-
- // Since this class needs to be able to delete |job_|, it must be the only
- // reference holder (except for temporary references). Provide access to the
- // pointer for tests to use.
- WebSocketJob* job() { return job_.get(); }
-
- void set_job(WebSocketJob* job) { job_ = job; }
-
- // After calling this, the next call to a method on this delegate will delete
- // the WebSocketJob object.
- void set_delete_next(bool delete_next) { delete_next_ = delete_next; }
-
- void DeleteJobMaybe() {
- if (delete_next_) {
- job_->DetachContext();
- job_->DetachDelegate();
- job_ = NULL;
- }
- }
-
- // SocketStream::Delegate implementation
-
- // OnStartOpenConnection() is not implemented by SocketStreamDispatcherHost
-
- void OnConnected(SocketStream* socket,
- int max_pending_send_allowed) override {
- DeleteJobMaybe();
- }
-
- void OnSentData(SocketStream* socket, int amount_sent) override {
- DeleteJobMaybe();
- }
-
- void OnReceivedData(SocketStream* socket,
- const char* data,
- int len) override {
- DeleteJobMaybe();
- }
-
- void OnClose(SocketStream* socket) override { DeleteJobMaybe(); }
-
- void OnAuthRequired(SocketStream* socket,
- AuthChallengeInfo* auth_info) override {
- DeleteJobMaybe();
- }
-
- void OnSSLCertificateError(SocketStream* socket,
- const SSLInfo& ssl_info,
- bool fatal) override {
- DeleteJobMaybe();
- }
-
- void OnError(const SocketStream* socket, int error) override {
- DeleteJobMaybe();
- }
-
- // CanGetCookies() and CanSetCookies() do not appear to be able to delete the
- // WebSocketJob object.
-
- private:
- scoped_refptr<WebSocketJob> job_;
- bool delete_next_;
-};
-
-} // namespace
-
-class WebSocketJobTest : public PlatformTest,
- public ::testing::WithParamInterface<NextProto> {
- public:
- WebSocketJobTest()
- : spdy_util_(GetParam()),
- enable_websocket_over_spdy_(false) {}
-
- void SetUp() override {
- stream_type_ = STREAM_INVALID;
- cookie_store_ = new MockCookieStore;
- context_.reset(new MockURLRequestContext(cookie_store_.get()));
- }
- void TearDown() override {
- cookie_store_ = NULL;
- context_.reset();
- websocket_ = NULL;
- socket_ = NULL;
- }
- void DoSendRequest() {
- EXPECT_TRUE(websocket_->SendData(kHandshakeRequestWithoutCookie,
- kHandshakeRequestWithoutCookieLength));
- }
- void DoSendData() {
- if (received_data().size() == kHandshakeResponseWithoutCookieLength)
- websocket_->SendData(kDataHello, kDataHelloLength);
- }
- void DoSync() {
- sync_test_callback_.callback().Run(OK);
- }
- int WaitForResult() {
- return sync_test_callback_.WaitForResult();
- }
-
- protected:
- enum StreamType {
- STREAM_INVALID,
- STREAM_MOCK_SOCKET,
- STREAM_SOCKET,
- STREAM_SPDY_WEBSOCKET,
- };
- enum ThrottlingOption {
- THROTTLING_OFF,
- THROTTLING_ON,
- };
- enum SpdyOption {
- SPDY_OFF,
- SPDY_ON,
- };
- void InitWebSocketJob(const GURL& url,
- MockSocketStreamDelegate* delegate,
- StreamType stream_type) {
- DCHECK_NE(STREAM_INVALID, stream_type);
- stream_type_ = stream_type;
- websocket_ = new WebSocketJob(delegate);
-
- if (stream_type == STREAM_MOCK_SOCKET)
- socket_ = new MockSocketStream(url, websocket_.get(), context_.get(),
- NULL);
-
- if (stream_type == STREAM_SOCKET || stream_type == STREAM_SPDY_WEBSOCKET) {
- if (stream_type == STREAM_SPDY_WEBSOCKET) {
- http_factory_.reset(new MockHttpTransactionFactory(
- GetParam(), data_.get(), enable_websocket_over_spdy_));
- context_->set_http_transaction_factory(http_factory_.get());
- }
-
- ssl_config_service_ = new MockSSLConfigService();
- context_->set_ssl_config_service(ssl_config_service_.get());
- proxy_service_.reset(ProxyService::CreateDirect());
- context_->set_proxy_service(proxy_service_.get());
- host_resolver_.reset(new MockHostResolver);
- context_->set_host_resolver(host_resolver_.get());
-
- socket_ = new SocketStream(url, websocket_.get(), context_.get(), NULL);
- socket_factory_.reset(new MockClientSocketFactory);
- DCHECK(data_.get());
- socket_factory_->AddSocketDataProvider(data_.get());
- socket_->SetClientSocketFactory(socket_factory_.get());
- }
-
- websocket_->InitSocketStream(socket_.get());
- // MockHostResolver resolves all hosts to 127.0.0.1; however, when we create
- // a WebSocketJob purely to block another one in a throttling test, we don't
- // perform a real connect. In that case, the following address is used
- // instead.
- IPAddressNumber ip;
- ParseIPLiteralToNumber("127.0.0.1", &ip);
- websocket_->addresses_ = AddressList::CreateFromIPAddress(ip, 80);
- }
- void SkipToConnecting() {
- websocket_->state_ = WebSocketJob::CONNECTING;
- ASSERT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(websocket_.get()));
- }
- WebSocketJob::State GetWebSocketJobState() {
- return websocket_->state_;
- }
- void CloseWebSocketJob() {
- if (websocket_->socket_.get()) {
- websocket_->socket_->DetachDelegate();
- WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_.get());
- }
- websocket_->state_ = WebSocketJob::CLOSED;
- websocket_->delegate_ = NULL;
- websocket_->socket_ = NULL;
- }
- SocketStream* GetSocket(SocketStreamJob* job) {
- return job->socket_.get();
- }
- const std::string& sent_data() const {
- DCHECK_EQ(STREAM_MOCK_SOCKET, stream_type_);
- MockSocketStream* socket =
- static_cast<MockSocketStream*>(socket_.get());
- DCHECK(socket);
- return socket->sent_data();
- }
- const std::string& received_data() const {
- DCHECK_NE(STREAM_INVALID, stream_type_);
- MockSocketStreamDelegate* delegate =
- static_cast<MockSocketStreamDelegate*>(websocket_->delegate_);
- DCHECK(delegate);
- return delegate->received_data();
- }
-
- void TestSimpleHandshake();
- void TestSlowHandshake();
- void TestHandshakeWithCookie();
- void TestHandshakeWithCookieButNotAllowed();
- void TestHSTSUpgrade();
- void TestInvalidSendData();
- void TestConnectByWebSocket(ThrottlingOption throttling);
- void TestConnectBySpdy(SpdyOption spdy, ThrottlingOption throttling);
- void TestThrottlingLimit();
-
- SpdyWebSocketTestUtil spdy_util_;
- StreamType stream_type_;
- scoped_refptr<MockCookieStore> cookie_store_;
- scoped_ptr<MockURLRequestContext> context_;
- scoped_refptr<WebSocketJob> websocket_;
- scoped_refptr<SocketStream> socket_;
- scoped_ptr<MockClientSocketFactory> socket_factory_;
- scoped_ptr<OrderedSocketData> data_;
- TestCompletionCallback sync_test_callback_;
- scoped_refptr<MockSSLConfigService> ssl_config_service_;
- scoped_ptr<ProxyService> proxy_service_;
- scoped_ptr<MockHostResolver> host_resolver_;
- scoped_ptr<MockHttpTransactionFactory> http_factory_;
-
- // Must be set before call to enable_websocket_over_spdy, defaults to false.
- bool enable_websocket_over_spdy_;
-
- static const char kHandshakeRequestWithoutCookie[];
- static const char kHandshakeRequestWithCookie[];
- static const char kHandshakeRequestWithFilteredCookie[];
- static const char kHandshakeResponseWithoutCookie[];
- static const char kHandshakeResponseWithCookie[];
- static const char kDataHello[];
- static const char kDataWorld[];
- static const char* const kHandshakeRequestForSpdy[];
- static const char* const kHandshakeResponseForSpdy[];
- static const size_t kHandshakeRequestWithoutCookieLength;
- static const size_t kHandshakeRequestWithCookieLength;
- static const size_t kHandshakeRequestWithFilteredCookieLength;
- static const size_t kHandshakeResponseWithoutCookieLength;
- static const size_t kHandshakeResponseWithCookieLength;
- static const size_t kDataHelloLength;
- static const size_t kDataWorldLength;
-};
-
-// Tests using this fixture verify that the WebSocketJob can handle being
-// deleted while calling back to the delegate correctly. These tests need to be
-// run under AddressSanitizer or other systems for detecting use-after-free
-// errors in order to find problems.
-class WebSocketJobDeleteTest : public ::testing::Test {
- protected:
- WebSocketJobDeleteTest()
- : delegate_(new DeletingSocketStreamDelegate),
- cookie_store_(new MockCookieStore),
- context_(new MockURLRequestContext(cookie_store_.get())) {
- WebSocketJob* websocket = new WebSocketJob(delegate_.get());
- delegate_->set_job(websocket);
-
- socket_ = new MockSocketStream(
- GURL("ws://127.0.0.1/"), websocket, context_.get(), NULL);
-
- websocket->InitSocketStream(socket_.get());
- }
-
- void SetDeleteNext() { return delegate_->set_delete_next(true); }
- WebSocketJob* job() { return delegate_->job(); }
-
- scoped_ptr<DeletingSocketStreamDelegate> delegate_;
- scoped_refptr<MockCookieStore> cookie_store_;
- scoped_ptr<MockURLRequestContext> context_;
- scoped_refptr<SocketStream> socket_;
-};
-
-const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: WebSocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "\r\n";
-
-const char WebSocketJobTest::kHandshakeRequestWithCookie[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: WebSocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Cookie: WK-test=1\r\n"
- "\r\n";
-
-const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: WebSocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Origin: http://example.com\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Cookie: CR-test=1; CR-test-httponly=1\r\n"
- "\r\n";
-
-const char WebSocketJobTest::kHandshakeResponseWithoutCookie[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "\r\n";
-
-const char WebSocketJobTest::kHandshakeResponseWithCookie[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "Sec-WebSocket-Protocol: sample\r\n"
- "Set-Cookie: CR-set-test=1\r\n"
- "\r\n";
-
-const char WebSocketJobTest::kDataHello[] = "Hello, ";
-
-const char WebSocketJobTest::kDataWorld[] = "World!\n";
-
-const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength =
- arraysize(kHandshakeRequestWithoutCookie) - 1;
-const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength =
- arraysize(kHandshakeRequestWithCookie) - 1;
-const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength =
- arraysize(kHandshakeRequestWithFilteredCookie) - 1;
-const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength =
- arraysize(kHandshakeResponseWithoutCookie) - 1;
-const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength =
- arraysize(kHandshakeResponseWithCookie) - 1;
-const size_t WebSocketJobTest::kDataHelloLength =
- arraysize(kDataHello) - 1;
-const size_t WebSocketJobTest::kDataWorldLength =
- arraysize(kDataWorld) - 1;
-
-void WebSocketJobTest::TestSimpleHandshake() {
- GURL url("ws://example.com/demo");
- MockSocketStreamDelegate delegate;
- InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
- SkipToConnecting();
-
- DoSendRequest();
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- websocket_->OnSentData(socket_.get(),
- kHandshakeRequestWithoutCookieLength);
- EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
-
- websocket_->OnReceivedData(socket_.get(),
- kHandshakeResponseWithoutCookie,
- kHandshakeResponseWithoutCookieLength);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
- EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
- CloseWebSocketJob();
-}
-
-void WebSocketJobTest::TestSlowHandshake() {
- GURL url("ws://example.com/demo");
- MockSocketStreamDelegate delegate;
- InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
- SkipToConnecting();
-
- DoSendRequest();
- // We assume request is sent in one data chunk (from WebKit)
- // We don't support streaming request.
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- websocket_->OnSentData(socket_.get(),
- kHandshakeRequestWithoutCookieLength);
- EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
-
- std::vector<std::string> lines;
- base::SplitString(kHandshakeResponseWithoutCookie, '\n', &lines);
- for (size_t i = 0; i < lines.size() - 2; i++) {
- std::string line = lines[i] + "\r\n";
- SCOPED_TRACE("Line: " + line);
- websocket_->OnReceivedData(socket_.get(), line.c_str(), line.size());
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_TRUE(delegate.received_data().empty());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- }
- websocket_->OnReceivedData(socket_.get(), "\r\n", 2);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_FALSE(delegate.received_data().empty());
- EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
- EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
- CloseWebSocketJob();
-}
-
-INSTANTIATE_TEST_CASE_P(
- NextProto,
- WebSocketJobTest,
- testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
-
-TEST_P(WebSocketJobTest, DelayedCookies) {
- enable_websocket_over_spdy_ = true;
- GURL url("ws://example.com/demo");
- GURL cookieUrl("http://example.com/demo");
- CookieOptions cookie_options;
- scoped_refptr<DelayedCookieMonster> cookie_store = new DelayedCookieMonster();
- context_->set_cookie_store(cookie_store.get());
- cookie_store->SetCookieWithOptionsAsync(cookieUrl,
- "CR-test=1",
- cookie_options,
- CookieMonster::SetCookiesCallback());
- cookie_options.set_include_httponly();
- cookie_store->SetCookieWithOptionsAsync(
- cookieUrl, "CR-test-httponly=1", cookie_options,
- CookieMonster::SetCookiesCallback());
-
- MockSocketStreamDelegate delegate;
- InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
- SkipToConnecting();
-
- bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
- kHandshakeRequestWithCookieLength);
- EXPECT_TRUE(sent);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- websocket_->OnSentData(socket_.get(),
- kHandshakeRequestWithFilteredCookieLength);
- EXPECT_EQ(kHandshakeRequestWithCookieLength,
- delegate.amount_sent());
-
- websocket_->OnReceivedData(socket_.get(),
- kHandshakeResponseWithCookie,
- kHandshakeResponseWithCookieLength);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
- EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
-
- CloseWebSocketJob();
-}
-
-void WebSocketJobTest::TestHandshakeWithCookie() {
- GURL url("ws://example.com/demo");
- GURL cookieUrl("http://example.com/demo");
- CookieOptions cookie_options;
- cookie_store_->SetCookieWithOptions(
- cookieUrl, "CR-test=1", cookie_options);
- cookie_options.set_include_httponly();
- cookie_store_->SetCookieWithOptions(
- cookieUrl, "CR-test-httponly=1", cookie_options);
-
- MockSocketStreamDelegate delegate;
- InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
- SkipToConnecting();
-
- bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
- kHandshakeRequestWithCookieLength);
- EXPECT_TRUE(sent);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- websocket_->OnSentData(socket_.get(),
- kHandshakeRequestWithFilteredCookieLength);
- EXPECT_EQ(kHandshakeRequestWithCookieLength,
- delegate.amount_sent());
-
- websocket_->OnReceivedData(socket_.get(),
- kHandshakeResponseWithCookie,
- kHandshakeResponseWithCookieLength);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
- EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
-
- EXPECT_EQ(3U, cookie_store_->entries().size());
- EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
- EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
- EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
- EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
- EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url);
- EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line);
-
- CloseWebSocketJob();
-}
-
-void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() {
- GURL url("ws://example.com/demo");
- GURL cookieUrl("http://example.com/demo");
- CookieOptions cookie_options;
- cookie_store_->SetCookieWithOptions(
- cookieUrl, "CR-test=1", cookie_options);
- cookie_options.set_include_httponly();
- cookie_store_->SetCookieWithOptions(
- cookieUrl, "CR-test-httponly=1", cookie_options);
-
- MockSocketStreamDelegate delegate;
- delegate.set_allow_all_cookies(false);
- InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
- SkipToConnecting();
-
- bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
- kHandshakeRequestWithCookieLength);
- EXPECT_TRUE(sent);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- websocket_->OnSentData(socket_.get(), kHandshakeRequestWithoutCookieLength);
- EXPECT_EQ(kHandshakeRequestWithCookieLength, delegate.amount_sent());
-
- websocket_->OnReceivedData(socket_.get(),
- kHandshakeResponseWithCookie,
- kHandshakeResponseWithCookieLength);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
- EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
-
- EXPECT_EQ(2U, cookie_store_->entries().size());
- EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
- EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
- EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
- EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
-
- CloseWebSocketJob();
-}
-
-void WebSocketJobTest::TestHSTSUpgrade() {
- GURL url("ws://upgrademe.com/");
- MockSocketStreamDelegate delegate;
- scoped_refptr<SocketStreamJob> job =
- SocketStreamJob::CreateSocketStreamJob(
- url, &delegate, context_->transport_security_state(),
- context_->ssl_config_service(), NULL, NULL);
- EXPECT_TRUE(GetSocket(job.get())->is_secure());
- job->DetachDelegate();
-
- url = GURL("ws://donotupgrademe.com/");
- job = SocketStreamJob::CreateSocketStreamJob(
- url, &delegate, context_->transport_security_state(),
- context_->ssl_config_service(), NULL, NULL);
- EXPECT_FALSE(GetSocket(job.get())->is_secure());
- job->DetachDelegate();
-}
-
-void WebSocketJobTest::TestInvalidSendData() {
- GURL url("ws://example.com/demo");
- MockSocketStreamDelegate delegate;
- InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
- SkipToConnecting();
-
- DoSendRequest();
- // We assume request is sent in one data chunk (from WebKit)
- // We don't support streaming request.
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- websocket_->OnSentData(socket_.get(),
- kHandshakeRequestWithoutCookieLength);
- EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
-
- // We could not send any data until connection is established.
- bool sent = websocket_->SendData(kHandshakeRequestWithoutCookie,
- kHandshakeRequestWithoutCookieLength);
- EXPECT_FALSE(sent);
- EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
- CloseWebSocketJob();
-}
-
-// Following tests verify cooperation between WebSocketJob and SocketStream.
-// Other former tests use MockSocketStream as SocketStream, so we could not
-// check SocketStream behavior.
-// OrderedSocketData provide socket level verifiation by checking out-going
-// packets in comparison with the MockWrite array and emulating in-coming
-// packets with MockRead array.
-
-void WebSocketJobTest::TestConnectByWebSocket(
- ThrottlingOption throttling) {
- // This is a test for verifying cooperation between WebSocketJob and
- // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic
- // situation. If |throttling| was |THROTTLING_ON|, throttling limits the
- // latter connection.
- MockWrite writes[] = {
- MockWrite(ASYNC,
- kHandshakeRequestWithoutCookie,
- kHandshakeRequestWithoutCookieLength,
- 1),
- MockWrite(ASYNC,
- kDataHello,
- kDataHelloLength,
- 3)
- };
- MockRead reads[] = {
- MockRead(ASYNC,
- kHandshakeResponseWithoutCookie,
- kHandshakeResponseWithoutCookieLength,
- 2),
- MockRead(ASYNC,
- kDataWorld,
- kDataWorldLength,
- 4),
- MockRead(SYNCHRONOUS, 0, 5) // EOF
- };
- data_.reset(new OrderedSocketData(
- reads, arraysize(reads), writes, arraysize(writes)));
-
- GURL url("ws://example.com/demo");
- MockSocketStreamDelegate delegate;
- WebSocketJobTest* test = this;
- if (throttling == THROTTLING_ON)
- delegate.SetOnStartOpenConnection(
- base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
- delegate.SetOnConnected(
- base::Bind(&WebSocketJobTest::DoSendRequest,
- base::Unretained(test)));
- delegate.SetOnReceivedData(
- base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test)));
- delegate.SetOnClose(
- base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
- InitWebSocketJob(url, &delegate, STREAM_SOCKET);
-
- scoped_refptr<WebSocketJob> block_websocket;
- if (throttling == THROTTLING_ON) {
- // Create former WebSocket object which obstructs the latter one.
- block_websocket = new WebSocketJob(NULL);
- block_websocket->addresses_ = AddressList(websocket_->address_list());
- ASSERT_TRUE(
- WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get()));
- }
-
- websocket_->Connect();
-
- if (throttling == THROTTLING_ON) {
- EXPECT_EQ(OK, WaitForResult());
- EXPECT_TRUE(websocket_->IsWaiting());
-
- // Remove the former WebSocket object from throttling queue to unblock the
- // latter.
- block_websocket->state_ = WebSocketJob::CLOSED;
- WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get());
- block_websocket = NULL;
- }
-
- EXPECT_EQ(OK, WaitForResult());
- EXPECT_TRUE(data_->at_read_eof());
- EXPECT_TRUE(data_->at_write_eof());
- EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState());
-}
-
-void WebSocketJobTest::TestConnectBySpdy(
- SpdyOption spdy, ThrottlingOption throttling) {
- // This is a test for verifying cooperation between WebSocketJob and
- // SocketStream in the situation we have SPDY session to the server. If
- // |throttling| was |THROTTLING_ON|, throttling limits the latter connection.
- // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected
- // results depend on its configuration.
- MockWrite writes_websocket[] = {
- MockWrite(ASYNC,
- kHandshakeRequestWithoutCookie,
- kHandshakeRequestWithoutCookieLength,
- 1),
- MockWrite(ASYNC,
- kDataHello,
- kDataHelloLength,
- 3)
- };
- MockRead reads_websocket[] = {
- MockRead(ASYNC,
- kHandshakeResponseWithoutCookie,
- kHandshakeResponseWithoutCookieLength,
- 2),
- MockRead(ASYNC,
- kDataWorld,
- kDataWorldLength,
- 4),
- MockRead(SYNCHRONOUS, 0, 5) // EOF
- };
-
- scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock());
- spdy_util_.SetHeader("path", "/demo", request_headers.get());
- spdy_util_.SetHeader("version", "WebSocket/13", request_headers.get());
- spdy_util_.SetHeader("scheme", "ws", request_headers.get());
- spdy_util_.SetHeader("host", "example.com", request_headers.get());
- spdy_util_.SetHeader("origin", "http://example.com", request_headers.get());
- spdy_util_.SetHeader("sec-websocket-protocol", "sample",
- request_headers.get());
-
- scoped_ptr<SpdyHeaderBlock> response_headers(new SpdyHeaderBlock());
- spdy_util_.SetHeader("status", "101 Switching Protocols",
- response_headers.get());
- spdy_util_.SetHeader("sec-websocket-protocol", "sample",
- response_headers.get());
-
- const SpdyStreamId kStreamId = 1;
- scoped_ptr<SpdyFrame> request_frame(
- spdy_util_.ConstructSpdyWebSocketHandshakeRequestFrame(
- request_headers.Pass(),
- kStreamId,
- MEDIUM));
- scoped_ptr<SpdyFrame> response_frame(
- spdy_util_.ConstructSpdyWebSocketHandshakeResponseFrame(
- response_headers.Pass(),
- kStreamId,
- MEDIUM));
- scoped_ptr<SpdyFrame> data_hello_frame(
- spdy_util_.ConstructSpdyWebSocketDataFrame(
- kDataHello,
- kDataHelloLength,
- kStreamId,
- false));
- scoped_ptr<SpdyFrame> data_world_frame(
- spdy_util_.ConstructSpdyWebSocketDataFrame(
- kDataWorld,
- kDataWorldLength,
- kStreamId,
- false));
- MockWrite writes_spdy[] = {
- CreateMockWrite(*request_frame.get(), 1),
- CreateMockWrite(*data_hello_frame.get(), 3),
- };
- MockRead reads_spdy[] = {
- CreateMockRead(*response_frame.get(), 2),
- CreateMockRead(*data_world_frame.get(), 4),
- MockRead(SYNCHRONOUS, 0, 5) // EOF
- };
-
- if (spdy == SPDY_ON)
- data_.reset(new OrderedSocketData(
- reads_spdy, arraysize(reads_spdy),
- writes_spdy, arraysize(writes_spdy)));
- else
- data_.reset(new OrderedSocketData(
- reads_websocket, arraysize(reads_websocket),
- writes_websocket, arraysize(writes_websocket)));
-
- GURL url("ws://example.com/demo");
- MockSocketStreamDelegate delegate;
- WebSocketJobTest* test = this;
- if (throttling == THROTTLING_ON)
- delegate.SetOnStartOpenConnection(
- base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
- delegate.SetOnConnected(
- base::Bind(&WebSocketJobTest::DoSendRequest,
- base::Unretained(test)));
- delegate.SetOnReceivedData(
- base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test)));
- delegate.SetOnClose(
- base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
- InitWebSocketJob(url, &delegate, STREAM_SPDY_WEBSOCKET);
-
- scoped_refptr<WebSocketJob> block_websocket;
- if (throttling == THROTTLING_ON) {
- // Create former WebSocket object which obstructs the latter one.
- block_websocket = new WebSocketJob(NULL);
- block_websocket->addresses_ = AddressList(websocket_->address_list());
- ASSERT_TRUE(
- WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get()));
- }
-
- websocket_->Connect();
-
- if (throttling == THROTTLING_ON) {
- EXPECT_EQ(OK, WaitForResult());
- EXPECT_TRUE(websocket_->IsWaiting());
-
- // Remove the former WebSocket object from throttling queue to unblock the
- // latter.
- block_websocket->state_ = WebSocketJob::CLOSED;
- WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get());
- block_websocket = NULL;
- }
-
- EXPECT_EQ(OK, WaitForResult());
- EXPECT_TRUE(data_->at_read_eof());
- EXPECT_TRUE(data_->at_write_eof());
- EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState());
-}
-
-void WebSocketJobTest::TestThrottlingLimit() {
- std::vector<scoped_refptr<WebSocketJob> > jobs;
- const int kMaxWebSocketJobsThrottled = 1024;
- IPAddressNumber ip;
- ParseIPLiteralToNumber("127.0.0.1", &ip);
- for (int i = 0; i < kMaxWebSocketJobsThrottled + 1; ++i) {
- scoped_refptr<WebSocketJob> job = new WebSocketJob(NULL);
- job->addresses_ = AddressList(AddressList::CreateFromIPAddress(ip, 80));
- if (i >= kMaxWebSocketJobsThrottled)
- EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job.get()));
- else
- EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job.get()));
- jobs.push_back(job);
- }
-
- // Close the jobs in reverse order. Otherwise, We need to make them prepared
- // for Wakeup call.
- for (std::vector<scoped_refptr<WebSocketJob> >::reverse_iterator iter =
- jobs.rbegin();
- iter != jobs.rend();
- ++iter) {
- WebSocketJob* job = (*iter).get();
- job->state_ = WebSocketJob::CLOSED;
- WebSocketThrottle::GetInstance()->RemoveFromQueue(job);
- }
-}
-
-// Execute tests in both spdy-disabled mode and spdy-enabled mode.
-TEST_P(WebSocketJobTest, SimpleHandshake) {
- TestSimpleHandshake();
-}
-
-TEST_P(WebSocketJobTest, SlowHandshake) {
- TestSlowHandshake();
-}
-
-TEST_P(WebSocketJobTest, HandshakeWithCookie) {
- TestHandshakeWithCookie();
-}
-
-TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) {
- TestHandshakeWithCookieButNotAllowed();
-}
-
-TEST_P(WebSocketJobTest, HSTSUpgrade) {
- TestHSTSUpgrade();
-}
-
-TEST_P(WebSocketJobTest, InvalidSendData) {
- TestInvalidSendData();
-}
-
-TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestSimpleHandshake();
-}
-
-TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestSlowHandshake();
-}
-
-TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestHandshakeWithCookie();
-}
-
-TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestHandshakeWithCookieButNotAllowed();
-}
-
-TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestHSTSUpgrade();
-}
-
-TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestInvalidSendData();
-}
-
-TEST_P(WebSocketJobTest, ConnectByWebSocket) {
- enable_websocket_over_spdy_ = true;
- TestConnectByWebSocket(THROTTLING_OFF);
-}
-
-TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestConnectByWebSocket(THROTTLING_OFF);
-}
-
-TEST_P(WebSocketJobTest, ConnectBySpdy) {
- TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF);
-}
-
-TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestConnectBySpdy(SPDY_ON, THROTTLING_OFF);
-}
-
-TEST_P(WebSocketJobTest, ThrottlingWebSocket) {
- TestConnectByWebSocket(THROTTLING_ON);
-}
-
-TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) {
- TestThrottlingLimit();
-}
-
-TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestConnectByWebSocket(THROTTLING_ON);
-}
-
-TEST_P(WebSocketJobTest, ThrottlingSpdy) {
- TestConnectBySpdy(SPDY_OFF, THROTTLING_ON);
-}
-
-TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) {
- enable_websocket_over_spdy_ = true;
- TestConnectBySpdy(SPDY_ON, THROTTLING_ON);
-}
-
-TEST_F(WebSocketJobDeleteTest, OnClose) {
- SetDeleteNext();
- job()->OnClose(socket_.get());
- // OnClose() sets WebSocketJob::_socket to NULL before we can detach it, so
- // socket_->delegate is still set at this point. Clear it to avoid hitting
- // DCHECK(!delegate_) in the SocketStream destructor. SocketStream::Finish()
- // is the only caller of this method in real code, and it also sets delegate_
- // to NULL.
- socket_->DetachDelegate();
- EXPECT_FALSE(job());
-}
-
-TEST_F(WebSocketJobDeleteTest, OnAuthRequired) {
- SetDeleteNext();
- job()->OnAuthRequired(socket_.get(), NULL);
- EXPECT_FALSE(job());
-}
-
-TEST_F(WebSocketJobDeleteTest, OnSSLCertificateError) {
- SSLInfo ssl_info;
- SetDeleteNext();
- job()->OnSSLCertificateError(socket_.get(), ssl_info, true);
- EXPECT_FALSE(job());
-}
-
-TEST_F(WebSocketJobDeleteTest, OnError) {
- SetDeleteNext();
- job()->OnError(socket_.get(), ERR_CONNECTION_RESET);
- EXPECT_FALSE(job());
-}
-
-TEST_F(WebSocketJobDeleteTest, OnSentSpdyHeaders) {
- job()->Connect();
- SetDeleteNext();
- job()->OnSentSpdyHeaders();
- EXPECT_FALSE(job());
-}
-
-TEST_F(WebSocketJobDeleteTest, OnSentHandshakeRequest) {
- static const char kMinimalRequest[] =
- "GET /demo HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "Upgrade: WebSocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Origin: http://example.com\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "\r\n";
- const size_t kMinimalRequestSize = arraysize(kMinimalRequest) - 1;
- job()->Connect();
- job()->SendData(kMinimalRequest, kMinimalRequestSize);
- SetDeleteNext();
- job()->OnSentData(socket_.get(), kMinimalRequestSize);
- EXPECT_FALSE(job());
-}
-
-TEST_F(WebSocketJobDeleteTest, NotifyHeadersComplete) {
- static const char kMinimalResponse[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
- "\r\n";
- job()->Connect();
- SetDeleteNext();
- job()->OnReceivedData(
- socket_.get(), kMinimalResponse, arraysize(kMinimalResponse) - 1);
- EXPECT_FALSE(job());
-}
-
-// TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation.
-// TODO(toyoshim,yutak): Add tests to verify closing handshake.
-} // namespace net
diff --git a/net/websockets/websocket_net_log_params.cc b/net/websockets/websocket_net_log_params.cc
deleted file mode 100644
index dd9bdde..0000000
--- a/net/websockets/websocket_net_log_params.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2012 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_net_log_params.h"
-
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-
-namespace net {
-
-base::Value* NetLogWebSocketHandshakeCallback(
- const std::string* headers,
- NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue();
- base::ListValue* header_list = new base::ListValue();
-
- size_t last = 0;
- size_t headers_size = headers->size();
- size_t pos = 0;
- while (pos <= headers_size) {
- if (pos == headers_size ||
- ((*headers)[pos] == '\r' &&
- pos + 1 < headers_size && (*headers)[pos + 1] == '\n')) {
- std::string entry = headers->substr(last, pos - last);
- pos += 2;
- last = pos;
-
- header_list->Append(new base::StringValue(entry));
-
- if (entry.empty()) {
- // Dump WebSocket key3.
- std::string key;
- for (; pos < headers_size; ++pos) {
- key += base::StringPrintf("\\x%02x", (*headers)[pos] & 0xff);
- }
- header_list->Append(new base::StringValue(key));
- break;
- }
- } else {
- ++pos;
- }
- }
-
- dict->Set("headers", header_list);
- return dict;
-}
-
-} // namespace net
diff --git a/net/websockets/websocket_net_log_params.h b/net/websockets/websocket_net_log_params.h
deleted file mode 100644
index 45dabb0..0000000
--- a/net/websockets/websocket_net_log_params.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef NET_WEBSOCKETS_WEBSOCKET_NET_LOG_PARAMS_H_
-#define NET_WEBSOCKETS_WEBSOCKET_NET_LOG_PARAMS_H_
-
-#include <string>
-
-#include "net/base/net_export.h"
-#include "net/base/net_log.h"
-
-namespace net {
-
-NET_EXPORT_PRIVATE base::Value* NetLogWebSocketHandshakeCallback(
- const std::string* headers,
- NetLog::LogLevel /* log_level */);
-
-} // namespace net
-
-#endif // NET_WEBSOCKETS_WEBSOCKET_NET_LOG_PARAMS_H_
diff --git a/net/websockets/websocket_net_log_params_test.cc b/net/websockets/websocket_net_log_params_test.cc
deleted file mode 100644
index d6d2a0d..0000000
--- a/net/websockets/websocket_net_log_params_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2013 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_net_log_params.h"
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(NetLogWebSocketHandshakeParameterTest, ToValue) {
- base::ListValue* list = new base::ListValue();
- list->Append(new base::StringValue("GET /demo HTTP/1.1"));
- list->Append(new base::StringValue("Host: example.com"));
- list->Append(new base::StringValue("Connection: Upgrade"));
- list->Append(new base::StringValue("Sec-WebSocket-Key2: 12998 5 Y3 1 .P00"));
- list->Append(new base::StringValue("Sec-WebSocket-Protocol: sample"));
- list->Append(new base::StringValue("Upgrade: WebSocket"));
- list->Append(new base::StringValue(
- "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5"));
- list->Append(new base::StringValue("Origin: http://example.com"));
- list->Append(new base::StringValue(std::string()));
- list->Append(new base::StringValue(
- "\\x00\\x01\\x0a\\x0d\\xff\\xfe\\x0d\\x0a"));
-
- base::DictionaryValue expected;
- expected.Set("headers", list);
-
- const std::string key("\x00\x01\x0a\x0d\xff\xfe\x0d\x0a", 8);
- const std::string testInput =
- "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" +
- key;
-
- scoped_ptr<base::Value> actual(
- net::NetLogWebSocketHandshakeCallback(&testInput,
- net::NetLog::LOG_ALL));
-
- EXPECT_TRUE(expected.Equals(actual.get()));
-}
diff --git a/net/websockets/websocket_throttle.cc b/net/websockets/websocket_throttle.cc
deleted file mode 100644
index 59e73fd..0000000
--- a/net/websockets/websocket_throttle.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2012 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_throttle.h"
-
-#include <algorithm>
-#include <set>
-#include <string>
-#include <utility>
-
-#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "net/base/io_buffer.h"
-#include "net/socket_stream/socket_stream.h"
-#include "net/websockets/websocket_job.h"
-
-namespace net {
-
-namespace {
-
-const size_t kMaxWebSocketJobsThrottled = 1024;
-
-} // namespace
-
-WebSocketThrottle::WebSocketThrottle() {
-}
-
-WebSocketThrottle::~WebSocketThrottle() {
- DCHECK(queue_.empty());
- DCHECK(addr_map_.empty());
-}
-
-// static
-WebSocketThrottle* WebSocketThrottle::GetInstance() {
- return Singleton<WebSocketThrottle>::get();
-}
-
-bool WebSocketThrottle::PutInQueue(WebSocketJob* job) {
- if (queue_.size() >= kMaxWebSocketJobsThrottled)
- return false;
-
- queue_.push_back(job);
- const AddressList& address_list = job->address_list();
- std::set<IPEndPoint> address_set;
- for (AddressList::const_iterator addr_iter = address_list.begin();
- addr_iter != address_list.end();
- ++addr_iter) {
- const IPEndPoint& address = *addr_iter;
- // If |address| is already processed, don't do it again.
- if (!address_set.insert(address).second)
- continue;
-
- ConnectingAddressMap::iterator iter = addr_map_.find(address);
- if (iter == addr_map_.end()) {
- ConnectingAddressMap::iterator new_queue =
- addr_map_.insert(make_pair(address, ConnectingQueue())).first;
- new_queue->second.push_back(job);
- } else {
- DCHECK(!iter->second.empty());
- iter->second.push_back(job);
- job->SetWaiting();
- DVLOG(1) << "Waiting on " << address.ToString();
- }
- }
-
- return true;
-}
-
-void WebSocketThrottle::RemoveFromQueue(WebSocketJob* job) {
- ConnectingQueue::iterator queue_iter =
- std::find(queue_.begin(), queue_.end(), job);
- if (queue_iter == queue_.end())
- return;
- queue_.erase(queue_iter);
-
- std::set<WebSocketJob*> wakeup_candidates;
-
- const AddressList& resolved_address_list = job->address_list();
- std::set<IPEndPoint> address_set;
- for (AddressList::const_iterator addr_iter = resolved_address_list.begin();
- addr_iter != resolved_address_list.end();
- ++addr_iter) {
- const IPEndPoint& address = *addr_iter;
- // If |address| is already processed, don't do it again.
- if (!address_set.insert(address).second)
- continue;
-
- ConnectingAddressMap::iterator map_iter = addr_map_.find(address);
- DCHECK(map_iter != addr_map_.end());
-
- ConnectingQueue& per_address_queue = map_iter->second;
- DCHECK(!per_address_queue.empty());
- // Job may not be front of the queue if the socket is closed while waiting.
- ConnectingQueue::iterator per_address_queue_iter =
- std::find(per_address_queue.begin(), per_address_queue.end(), job);
- bool was_front = false;
- if (per_address_queue_iter != per_address_queue.end()) {
- was_front = (per_address_queue_iter == per_address_queue.begin());
- per_address_queue.erase(per_address_queue_iter);
- }
- if (per_address_queue.empty()) {
- addr_map_.erase(map_iter);
- } else if (was_front) {
- // The new front is a wake-up candidate.
- wakeup_candidates.insert(per_address_queue.front());
- }
- }
-
- WakeupSocketIfNecessary(wakeup_candidates);
-}
-
-void WebSocketThrottle::WakeupSocketIfNecessary(
- const std::set<WebSocketJob*>& wakeup_candidates) {
- for (std::set<WebSocketJob*>::const_iterator iter = wakeup_candidates.begin();
- iter != wakeup_candidates.end();
- ++iter) {
- WebSocketJob* job = *iter;
- if (!job->IsWaiting())
- continue;
-
- bool should_wakeup = true;
- const AddressList& resolved_address_list = job->address_list();
- for (AddressList::const_iterator addr_iter = resolved_address_list.begin();
- addr_iter != resolved_address_list.end();
- ++addr_iter) {
- const IPEndPoint& address = *addr_iter;
- ConnectingAddressMap::iterator map_iter = addr_map_.find(address);
- DCHECK(map_iter != addr_map_.end());
- const ConnectingQueue& per_address_queue = map_iter->second;
- if (job != per_address_queue.front()) {
- should_wakeup = false;
- break;
- }
- }
- if (should_wakeup)
- job->Wakeup();
- }
-}
-
-} // namespace net
diff --git a/net/websockets/websocket_throttle.h b/net/websockets/websocket_throttle.h
deleted file mode 100644
index 0b7a39f..0000000
--- a/net/websockets/websocket_throttle.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2011 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.
-
-#ifndef NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_
-#define NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_
-
-#include <deque>
-#include <map>
-#include <set>
-#include <string>
-
-#include "net/base/ip_endpoint.h"
-#include "net/base/net_export.h"
-
-template <typename T> struct DefaultSingletonTraits;
-
-namespace net {
-
-class SocketStream;
-class WebSocketJob;
-
-// SocketStreamThrottle for WebSocket protocol.
-// Implements the client-side requirements in the spec.
-// http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
-// 4.1 Handshake
-// 1. If the user agent already has a Web Socket connection to the
-// remote host (IP address) identified by /host/, even if known by
-// another name, wait until that connection has been established or
-// for that connection to have failed.
-class NET_EXPORT_PRIVATE WebSocketThrottle {
- public:
- // Returns the singleton instance.
- static WebSocketThrottle* GetInstance();
-
- // Puts |job| in |queue_| and queues for the destination addresses
- // of |job|.
- // If other job is using the same destination address, set |job| waiting.
- //
- // Returns true if successful. If the number of pending jobs will exceed
- // the limit, does nothing and returns false.
- bool PutInQueue(WebSocketJob* job);
-
- // Removes |job| from |queue_| and queues for the destination addresses
- // of |job|, and then wakes up jobs that can now resume establishing a
- // connection.
- void RemoveFromQueue(WebSocketJob* job);
-
- private:
- typedef std::deque<WebSocketJob*> ConnectingQueue;
- typedef std::map<IPEndPoint, ConnectingQueue> ConnectingAddressMap;
-
- WebSocketThrottle();
- ~WebSocketThrottle();
- friend struct DefaultSingletonTraits<WebSocketThrottle>;
-
- // Examines if any of the given jobs can resume establishing a connection. If
- // for all per-address queues for each resolved addresses
- // (job->address_list()) of a job, the job is at the front of the queues, the
- // job can resume establishing a connection, so wakes up the job.
- void WakeupSocketIfNecessary(
- const std::set<WebSocketJob*>& wakeup_candidates);
-
- // Key: string of host's address. Value: queue of sockets for the address.
- ConnectingAddressMap addr_map_;
-
- // Queue of sockets for websockets in opening state.
- ConnectingQueue queue_;
-
- DISALLOW_COPY_AND_ASSIGN(WebSocketThrottle);
-};
-
-} // namespace net
-
-#endif // NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_
diff --git a/net/websockets/websocket_throttle_test.cc b/net/websockets/websocket_throttle_test.cc
deleted file mode 100644
index 4e27c83..0000000
--- a/net/websockets/websocket_throttle_test.cc
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright 2013 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_throttle.h"
-
-#include <string>
-
-#include "base/message_loop/message_loop.h"
-#include "net/base/address_list.h"
-#include "net/base/test_completion_callback.h"
-#include "net/socket_stream/socket_stream.h"
-#include "net/url_request/url_request_test_util.h"
-#include "net/websockets/websocket_job.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-#include "url/gurl.h"
-
-namespace net {
-
-namespace {
-
-class DummySocketStreamDelegate : public SocketStream::Delegate {
- public:
- DummySocketStreamDelegate() {}
- ~DummySocketStreamDelegate() override {}
- void OnConnected(SocketStream* socket,
- int max_pending_send_allowed) override {}
- void OnSentData(SocketStream* socket, int amount_sent) override {}
- void OnReceivedData(SocketStream* socket,
- const char* data,
- int len) override {}
- void OnClose(SocketStream* socket) override {}
-};
-
-class WebSocketThrottleTestContext : public TestURLRequestContext {
- public:
- explicit WebSocketThrottleTestContext(bool enable_websocket_over_spdy)
- : TestURLRequestContext(true) {
- HttpNetworkSession::Params params;
- params.enable_websocket_over_spdy = enable_websocket_over_spdy;
- Init();
- }
-};
-
-} // namespace
-
-class WebSocketThrottleTest : public PlatformTest {
- protected:
- static IPEndPoint MakeAddr(int a1, int a2, int a3, int a4) {
- IPAddressNumber ip;
- ip.push_back(a1);
- ip.push_back(a2);
- ip.push_back(a3);
- ip.push_back(a4);
- return IPEndPoint(ip, 0);
- }
-
- static void MockSocketStreamConnect(
- SocketStream* socket, const AddressList& list) {
- socket->set_addresses(list);
- // TODO(toyoshim): We should introduce additional tests on cases via proxy.
- socket->proxy_info_.UseDirect();
- // In SocketStream::Connect(), it adds reference to socket, which is
- // balanced with SocketStream::Finish() that is finally called from
- // SocketStream::Close() or SocketStream::DetachDelegate(), when
- // next_state_ is not STATE_NONE.
- // If next_state_ is STATE_NONE, SocketStream::Close() or
- // SocketStream::DetachDelegate() won't call SocketStream::Finish(),
- // so Release() won't be called. Thus, we don't need socket->AddRef()
- // here.
- DCHECK_EQ(socket->next_state_, SocketStream::STATE_NONE);
- }
-};
-
-TEST_F(WebSocketThrottleTest, Throttle) {
- // TODO(toyoshim): We need to consider both spdy-enabled and spdy-disabled
- // configuration.
- WebSocketThrottleTestContext context(true);
- DummySocketStreamDelegate delegate;
-
- // For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6
- AddressList addr;
- addr.push_back(MakeAddr(1, 2, 3, 4));
- addr.push_back(MakeAddr(1, 2, 3, 5));
- addr.push_back(MakeAddr(1, 2, 3, 6));
- scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s1(
- new SocketStream(GURL("ws://host1/"), w1.get(), &context, NULL));
- w1->InitSocketStream(s1.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr);
-
- DVLOG(1) << "socket1";
- TestCompletionCallback callback_s1;
- // Trying to open connection to host1 will start without wait.
- EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback()));
-
- // Now connecting to host1, so waiting queue looks like
- // Address | head -> tail
- // 1.2.3.4 | w1
- // 1.2.3.5 | w1
- // 1.2.3.6 | w1
-
- // For host2: 1.2.3.4
- addr.clear();
- addr.push_back(MakeAddr(1, 2, 3, 4));
- scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s2(
- new SocketStream(GURL("ws://host2/"), w2.get(), &context, NULL));
- w2->InitSocketStream(s2.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s2.get(), addr);
-
- DVLOG(1) << "socket2";
- TestCompletionCallback callback_s2;
- // Trying to open connection to host2 will wait for w1.
- EXPECT_EQ(ERR_IO_PENDING,
- w2->OnStartOpenConnection(s2.get(), callback_s2.callback()));
- // Now waiting queue looks like
- // Address | head -> tail
- // 1.2.3.4 | w1 w2
- // 1.2.3.5 | w1
- // 1.2.3.6 | w1
-
- // For host3: 1.2.3.5
- addr.clear();
- addr.push_back(MakeAddr(1, 2, 3, 5));
- scoped_refptr<WebSocketJob> w3(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s3(
- new SocketStream(GURL("ws://host3/"), w3.get(), &context, NULL));
- w3->InitSocketStream(s3.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s3.get(), addr);
-
- DVLOG(1) << "socket3";
- TestCompletionCallback callback_s3;
- // Trying to open connection to host3 will wait for w1.
- EXPECT_EQ(ERR_IO_PENDING,
- w3->OnStartOpenConnection(s3.get(), callback_s3.callback()));
- // Address | head -> tail
- // 1.2.3.4 | w1 w2
- // 1.2.3.5 | w1 w3
- // 1.2.3.6 | w1
-
- // For host4: 1.2.3.4, 1.2.3.6
- addr.clear();
- addr.push_back(MakeAddr(1, 2, 3, 4));
- addr.push_back(MakeAddr(1, 2, 3, 6));
- scoped_refptr<WebSocketJob> w4(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s4(
- new SocketStream(GURL("ws://host4/"), w4.get(), &context, NULL));
- w4->InitSocketStream(s4.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s4.get(), addr);
-
- DVLOG(1) << "socket4";
- TestCompletionCallback callback_s4;
- // Trying to open connection to host4 will wait for w1, w2.
- EXPECT_EQ(ERR_IO_PENDING,
- w4->OnStartOpenConnection(s4.get(), callback_s4.callback()));
- // Address | head -> tail
- // 1.2.3.4 | w1 w2 w4
- // 1.2.3.5 | w1 w3
- // 1.2.3.6 | w1 w4
-
- // For host5: 1.2.3.6
- addr.clear();
- addr.push_back(MakeAddr(1, 2, 3, 6));
- scoped_refptr<WebSocketJob> w5(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s5(
- new SocketStream(GURL("ws://host5/"), w5.get(), &context, NULL));
- w5->InitSocketStream(s5.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s5.get(), addr);
-
- DVLOG(1) << "socket5";
- TestCompletionCallback callback_s5;
- // Trying to open connection to host5 will wait for w1, w4
- EXPECT_EQ(ERR_IO_PENDING,
- w5->OnStartOpenConnection(s5.get(), callback_s5.callback()));
- // Address | head -> tail
- // 1.2.3.4 | w1 w2 w4
- // 1.2.3.5 | w1 w3
- // 1.2.3.6 | w1 w4 w5
-
- // For host6: 1.2.3.6
- addr.clear();
- addr.push_back(MakeAddr(1, 2, 3, 6));
- scoped_refptr<WebSocketJob> w6(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s6(
- new SocketStream(GURL("ws://host6/"), w6.get(), &context, NULL));
- w6->InitSocketStream(s6.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s6.get(), addr);
-
- DVLOG(1) << "socket6";
- TestCompletionCallback callback_s6;
- // Trying to open connection to host6 will wait for w1, w4, w5
- EXPECT_EQ(ERR_IO_PENDING,
- w6->OnStartOpenConnection(s6.get(), callback_s6.callback()));
- // Address | head -> tail
- // 1.2.3.4 | w1 w2 w4
- // 1.2.3.5 | w1 w3
- // 1.2.3.6 | w1 w4 w5 w6
-
- // Receive partial response on w1, still connecting.
- DVLOG(1) << "socket1 1";
- static const char kHeader[] = "HTTP/1.1 101 WebSocket Protocol\r\n";
- w1->OnReceivedData(s1.get(), kHeader, sizeof(kHeader) - 1);
- EXPECT_FALSE(callback_s2.have_result());
- EXPECT_FALSE(callback_s3.have_result());
- EXPECT_FALSE(callback_s4.have_result());
- EXPECT_FALSE(callback_s5.have_result());
- EXPECT_FALSE(callback_s6.have_result());
-
- // Receive rest of handshake response on w1.
- DVLOG(1) << "socket1 2";
- static const char kHeader2[] =
- "Upgrade: WebSocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Origin: http://www.google.com\r\n"
- "Sec-WebSocket-Location: ws://websocket.chromium.org\r\n"
- "\r\n"
- "8jKS'y:G*Co,Wxa-";
- w1->OnReceivedData(s1.get(), kHeader2, sizeof(kHeader2) - 1);
- base::MessageLoopForIO::current()->RunUntilIdle();
- // Now, w1 is open.
- EXPECT_EQ(WebSocketJob::OPEN, w1->state());
- // So, w2 and w3 can start connecting. w4 needs to wait w2 (1.2.3.4)
- EXPECT_TRUE(callback_s2.have_result());
- EXPECT_TRUE(callback_s3.have_result());
- EXPECT_FALSE(callback_s4.have_result());
- // Address | head -> tail
- // 1.2.3.4 | w2 w4
- // 1.2.3.5 | w3
- // 1.2.3.6 | w4 w5 w6
-
- // Closing s1 doesn't change waiting queue.
- DVLOG(1) << "socket1 close";
- w1->OnClose(s1.get());
- base::MessageLoopForIO::current()->RunUntilIdle();
- EXPECT_FALSE(callback_s4.have_result());
- s1->DetachDelegate();
- // Address | head -> tail
- // 1.2.3.4 | w2 w4
- // 1.2.3.5 | w3
- // 1.2.3.6 | w4 w5 w6
-
- // w5 can close while waiting in queue.
- DVLOG(1) << "socket5 close";
- // w5 close() closes SocketStream that change state to STATE_CLOSE, calls
- // DoLoop(), so OnClose() callback will be called.
- w5->OnClose(s5.get());
- base::MessageLoopForIO::current()->RunUntilIdle();
- EXPECT_FALSE(callback_s4.have_result());
- // Address | head -> tail
- // 1.2.3.4 | w2 w4
- // 1.2.3.5 | w3
- // 1.2.3.6 | w4 w6
- s5->DetachDelegate();
-
- // w6 close abnormally (e.g. renderer finishes) while waiting in queue.
- DVLOG(1) << "socket6 close abnormally";
- w6->DetachDelegate();
- base::MessageLoopForIO::current()->RunUntilIdle();
- EXPECT_FALSE(callback_s4.have_result());
- // Address | head -> tail
- // 1.2.3.4 | w2 w4
- // 1.2.3.5 | w3
- // 1.2.3.6 | w4
-
- // Closing s2 kicks w4 to start connecting.
- DVLOG(1) << "socket2 close";
- w2->OnClose(s2.get());
- base::MessageLoopForIO::current()->RunUntilIdle();
- EXPECT_TRUE(callback_s4.have_result());
- // Address | head -> tail
- // 1.2.3.4 | w4
- // 1.2.3.5 | w3
- // 1.2.3.6 | w4
- s2->DetachDelegate();
-
- DVLOG(1) << "socket3 close";
- w3->OnClose(s3.get());
- base::MessageLoopForIO::current()->RunUntilIdle();
- s3->DetachDelegate();
- w4->OnClose(s4.get());
- s4->DetachDelegate();
- DVLOG(1) << "Done";
- base::MessageLoopForIO::current()->RunUntilIdle();
-}
-
-TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
- WebSocketThrottleTestContext context(true);
- DummySocketStreamDelegate delegate;
-
- // For localhost: 127.0.0.1, 127.0.0.1
- AddressList addr;
- addr.push_back(MakeAddr(127, 0, 0, 1));
- addr.push_back(MakeAddr(127, 0, 0, 1));
- scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s1(
- new SocketStream(GURL("ws://localhost/"), w1.get(), &context, NULL));
- w1->InitSocketStream(s1.get());
- WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr);
-
- DVLOG(1) << "socket1";
- TestCompletionCallback callback_s1;
- // Trying to open connection to localhost will start without wait.
- EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback()));
-
- DVLOG(1) << "socket1 close";
- w1->OnClose(s1.get());
- s1->DetachDelegate();
- DVLOG(1) << "Done";
- base::MessageLoopForIO::current()->RunUntilIdle();
-}
-
-// A connection should not be blocked by another connection to the same IP
-// with a different port.
-TEST_F(WebSocketThrottleTest, NoThrottleForDistinctPort) {
- WebSocketThrottleTestContext context(false);
- DummySocketStreamDelegate delegate;
- IPAddressNumber localhost;
- ParseIPLiteralToNumber("127.0.0.1", &localhost);
-
- // socket1: 127.0.0.1:80
- scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s1(
- new SocketStream(GURL("ws://localhost:80/"), w1.get(), &context, NULL));
- w1->InitSocketStream(s1.get());
- MockSocketStreamConnect(s1.get(),
- AddressList::CreateFromIPAddress(localhost, 80));
-
- DVLOG(1) << "connecting socket1";
- TestCompletionCallback callback_s1;
- // Trying to open connection to localhost:80 will start without waiting.
- EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback()));
-
- // socket2: 127.0.0.1:81
- scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
- scoped_refptr<SocketStream> s2(
- new SocketStream(GURL("ws://localhost:81/"), w2.get(), &context, NULL));
- w2->InitSocketStream(s2.get());
- MockSocketStreamConnect(s2.get(),
- AddressList::CreateFromIPAddress(localhost, 81));
-
- DVLOG(1) << "connecting socket2";
- TestCompletionCallback callback_s2;
- // Trying to open connection to localhost:81 will start without waiting.
- EXPECT_EQ(OK, w2->OnStartOpenConnection(s2.get(), callback_s2.callback()));
-
- DVLOG(1) << "closing socket1";
- w1->OnClose(s1.get());
- s1->DetachDelegate();
-
- DVLOG(1) << "closing socket2";
- w2->OnClose(s2.get());
- s2->DetachDelegate();
- DVLOG(1) << "Done";
- base::MessageLoopForIO::current()->RunUntilIdle();
-}
-
-} // namespace net