| Index: net/websockets/websocket_stream_test.cc
|
| diff --git a/net/websockets/websocket_stream_test.cc b/net/websockets/websocket_stream_test.cc
|
| deleted file mode 100644
|
| index 56f2e648c413c1ea92ea0610c972d2c71019ab06..0000000000000000000000000000000000000000
|
| --- a/net/websockets/websocket_stream_test.cc
|
| +++ /dev/null
|
| @@ -1,1320 +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_stream.h"
|
| -
|
| -#include <algorithm>
|
| -#include <string>
|
| -#include <utility>
|
| -#include <vector>
|
| -
|
| -#include "base/compiler_specific.h"
|
| -#include "base/memory/scoped_vector.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/metrics/histogram_samples.h"
|
| -#include "base/metrics/statistics_recorder.h"
|
| -#include "base/run_loop.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/timer/mock_timer.h"
|
| -#include "base/timer/timer.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/test_data_directory.h"
|
| -#include "net/http/http_request_headers.h"
|
| -#include "net/http/http_response_headers.h"
|
| -#include "net/proxy/proxy_service.h"
|
| -#include "net/socket/client_socket_handle.h"
|
| -#include "net/socket/socket_test_util.h"
|
| -#include "net/test/cert_test_util.h"
|
| -#include "net/url_request/url_request_test_util.h"
|
| -#include "net/websockets/websocket_basic_handshake_stream.h"
|
| -#include "net/websockets/websocket_frame.h"
|
| -#include "net/websockets/websocket_stream_create_test_base.h"
|
| -#include "net/websockets/websocket_test_util.h"
|
| -#include "testing/gtest/include/gtest/gtest.h"
|
| -#include "url/gurl.h"
|
| -#include "url/origin.h"
|
| -
|
| -namespace net {
|
| -namespace {
|
| -
|
| -// Simple builder for a DeterministicSocketData object to save repetitive code.
|
| -// It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
|
| -// be used in tests where the connect fails. In practice, those tests never have
|
| -// any read/write data and so can't benefit from it anyway. The arrays are not
|
| -// copied. It is up to the caller to ensure they stay in scope until the test
|
| -// ends.
|
| -template <size_t reads_count, size_t writes_count>
|
| -scoped_ptr<DeterministicSocketData> BuildSocketData(
|
| - MockRead (&reads)[reads_count],
|
| - MockWrite (&writes)[writes_count]) {
|
| - scoped_ptr<DeterministicSocketData> socket_data(
|
| - new DeterministicSocketData(reads, reads_count, writes, writes_count));
|
| - socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
| - socket_data->SetStop(reads_count + writes_count);
|
| - return socket_data.Pass();
|
| -}
|
| -
|
| -// Builder for a DeterministicSocketData that expects nothing. This does not
|
| -// set the connect data, so the calling code must do that explicitly.
|
| -scoped_ptr<DeterministicSocketData> BuildNullSocketData() {
|
| - return make_scoped_ptr(new DeterministicSocketData(NULL, 0, NULL, 0));
|
| -}
|
| -
|
| -class MockWeakTimer : public base::MockTimer,
|
| - public base::SupportsWeakPtr<MockWeakTimer> {
|
| - public:
|
| - MockWeakTimer(bool retain_user_task, bool is_repeating)
|
| - : MockTimer(retain_user_task, is_repeating) {}
|
| -};
|
| -
|
| -class WebSocketStreamCreateTest : public ::testing::Test,
|
| - public WebSocketStreamCreateTestBase {
|
| - public:
|
| - ~WebSocketStreamCreateTest() override {
|
| - // Permit any endpoint locks to be released.
|
| - stream_request_.reset();
|
| - stream_.reset();
|
| - base::RunLoop().RunUntilIdle();
|
| - }
|
| -
|
| - void CreateAndConnectCustomResponse(
|
| - const std::string& socket_url,
|
| - const std::string& socket_host,
|
| - const std::string& socket_path,
|
| - const std::vector<std::string>& sub_protocols,
|
| - const std::string& origin,
|
| - const std::string& extra_request_headers,
|
| - const std::string& response_body,
|
| - scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
|
| - url_request_context_host_.SetExpectations(
|
| - WebSocketStandardRequest(socket_path, socket_host, origin,
|
| - extra_request_headers),
|
| - response_body);
|
| - CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
|
| - }
|
| -
|
| - // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
|
| - // errors like "Unable to perform synchronous IO while stopped" will occur.
|
| - void CreateAndConnectStandard(
|
| - const std::string& socket_url,
|
| - const std::string& socket_host,
|
| - const std::string& socket_path,
|
| - const std::vector<std::string>& sub_protocols,
|
| - const std::string& origin,
|
| - const std::string& extra_request_headers,
|
| - const std::string& extra_response_headers,
|
| - scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
|
| - CreateAndConnectCustomResponse(
|
| - socket_url, socket_host, socket_path, sub_protocols, origin,
|
| - extra_request_headers,
|
| - WebSocketStandardResponse(extra_response_headers), timer.Pass());
|
| - }
|
| -
|
| - void CreateAndConnectRawExpectations(
|
| - const std::string& socket_url,
|
| - const std::vector<std::string>& sub_protocols,
|
| - const std::string& origin,
|
| - scoped_ptr<DeterministicSocketData> socket_data,
|
| - scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
|
| - AddRawExpectations(socket_data.Pass());
|
| - CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
|
| - }
|
| -
|
| - // Add additional raw expectations for sockets created before the final one.
|
| - void AddRawExpectations(scoped_ptr<DeterministicSocketData> socket_data) {
|
| - url_request_context_host_.AddRawExpectations(socket_data.Pass());
|
| - }
|
| -};
|
| -
|
| -// There are enough tests of the Sec-WebSocket-Extensions header that they
|
| -// deserve their own test fixture.
|
| -class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest {
|
| - public:
|
| - // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
|
| - // header in the response set to |extensions_header_value|. Runs the event
|
| - // loop to allow the connect to complete.
|
| - void CreateAndConnectWithExtensions(
|
| - const std::string& extensions_header_value) {
|
| - CreateAndConnectStandard(
|
| - "ws://localhost/testing_path", "localhost", "/testing_path",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n");
|
| - WaitUntilConnectDone();
|
| - }
|
| -};
|
| -
|
| -// Common code to construct expectations for authentication tests that receive
|
| -// the auth challenge on one connection and then create a second connection to
|
| -// send the authenticated request on.
|
| -class CommonAuthTestHelper {
|
| - public:
|
| - CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
|
| -
|
| - scoped_ptr<DeterministicSocketData> BuildSocketData1(
|
| - const std::string& response) {
|
| - request1_ =
|
| - WebSocketStandardRequest("/", "localhost", "http://localhost", "");
|
| - writes1_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
|
| - response1_ = response;
|
| - reads1_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
|
| - reads1_[1] = MockRead(SYNCHRONOUS, OK, 2); // Close connection
|
| -
|
| - return BuildSocketData(reads1_, writes1_);
|
| - }
|
| -
|
| - scoped_ptr<DeterministicSocketData> BuildSocketData2(
|
| - const std::string& request,
|
| - const std::string& response) {
|
| - request2_ = request;
|
| - response2_ = response;
|
| - writes2_[0] = MockWrite(SYNCHRONOUS, 0, request2_.c_str());
|
| - reads2_[0] = MockRead(SYNCHRONOUS, 1, response2_.c_str());
|
| - return BuildSocketData(reads2_, writes2_);
|
| - }
|
| -
|
| - private:
|
| - // These need to be object-scoped since they have to remain valid until all
|
| - // socket operations in the test are complete.
|
| - std::string request1_;
|
| - std::string request2_;
|
| - std::string response1_;
|
| - std::string response2_;
|
| - MockRead reads1_[2];
|
| - MockWrite writes1_[1];
|
| - MockRead reads2_[1];
|
| - MockWrite writes2_[1];
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper);
|
| -};
|
| -
|
| -// Data and methods for BasicAuth tests.
|
| -class WebSocketStreamCreateBasicAuthTest : public WebSocketStreamCreateTest {
|
| - protected:
|
| - void CreateAndConnectAuthHandshake(const std::string& url,
|
| - const std::string& base64_user_pass,
|
| - const std::string& response2) {
|
| - AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
|
| -
|
| - static const char request2format[] =
|
| - "GET / HTTP/1.1\r\n"
|
| - "Host: localhost\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Pragma: no-cache\r\n"
|
| - "Cache-Control: no-cache\r\n"
|
| - "Authorization: Basic %s\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Origin: http://localhost\r\n"
|
| - "Sec-WebSocket-Version: 13\r\n"
|
| - "User-Agent:\r\n"
|
| - "Accept-Encoding: gzip, deflate\r\n"
|
| - "Accept-Language: en-us,fr\r\n"
|
| - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
| - "Sec-WebSocket-Extensions: permessage-deflate; "
|
| - "client_max_window_bits\r\n"
|
| - "\r\n";
|
| - const std::string request =
|
| - base::StringPrintf(request2format, base64_user_pass.c_str());
|
| - CreateAndConnectRawExpectations(
|
| - url,
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - helper_.BuildSocketData2(request, response2));
|
| - }
|
| -
|
| - static const char kUnauthorizedResponse[];
|
| -
|
| - CommonAuthTestHelper helper_;
|
| -};
|
| -
|
| -class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {
|
| - protected:
|
| - static const char kUnauthorizedResponse[];
|
| - static const char kAuthorizedRequest[];
|
| -
|
| - CommonAuthTestHelper helper_;
|
| -};
|
| -
|
| -const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse[] =
|
| - "HTTP/1.1 401 Unauthorized\r\n"
|
| - "Content-Length: 0\r\n"
|
| - "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
|
| - "\r\n";
|
| -
|
| -// These negotiation values are borrowed from
|
| -// http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
|
| -// you are bored. Only the weakest (no qop) variants of Digest authentication
|
| -// can be tested by this method, because the others involve random input.
|
| -const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse[] =
|
| - "HTTP/1.1 401 Unauthorized\r\n"
|
| - "Content-Length: 0\r\n"
|
| - "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
|
| - "\r\n";
|
| -
|
| -const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest[] =
|
| - "GET / HTTP/1.1\r\n"
|
| - "Host: localhost\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Pragma: no-cache\r\n"
|
| - "Cache-Control: no-cache\r\n"
|
| - "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
|
| - "nonce=\"nonce-value\", uri=\"/\", "
|
| - "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Origin: http://localhost\r\n"
|
| - "Sec-WebSocket-Version: 13\r\n"
|
| - "User-Agent:\r\n"
|
| - "Accept-Encoding: gzip, deflate\r\n"
|
| - "Accept-Language: en-us,fr\r\n"
|
| - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
| - "Sec-WebSocket-Extensions: permessage-deflate; "
|
| - "client_max_window_bits\r\n"
|
| - "\r\n";
|
| -
|
| -class WebSocketStreamCreateUMATest : public ::testing::Test {
|
| - public:
|
| - // This enum should match with the enum in Delegate in websocket_stream.cc.
|
| - enum HandshakeResult {
|
| - INCOMPLETE,
|
| - CONNECTED,
|
| - FAILED,
|
| - NUM_HANDSHAKE_RESULT_TYPES,
|
| - };
|
| -
|
| - class StreamCreation : public WebSocketStreamCreateTest {
|
| - void TestBody() override {}
|
| - };
|
| -
|
| - scoped_ptr<base::HistogramSamples> GetSamples(const std::string& name) {
|
| - base::HistogramBase* histogram =
|
| - base::StatisticsRecorder::FindHistogram(name);
|
| - return histogram ? histogram->SnapshotSamples()
|
| - : scoped_ptr<base::HistogramSamples>();
|
| - }
|
| -};
|
| -
|
| -// Confirm that the basic case works as expected.
|
| -TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
|
| - CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "", "");
|
| - EXPECT_FALSE(request_info_);
|
| - EXPECT_FALSE(response_info_);
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| - EXPECT_TRUE(request_info_);
|
| - EXPECT_TRUE(response_info_);
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateTest, HandshakeInfo) {
|
| - static const char kResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "foo: bar, baz\r\n"
|
| - "hoge: fuga\r\n"
|
| - "hoge: piyo\r\n"
|
| - "\r\n";
|
| -
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kResponse);
|
| - EXPECT_FALSE(request_info_);
|
| - EXPECT_FALSE(response_info_);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(stream_);
|
| - ASSERT_TRUE(request_info_);
|
| - ASSERT_TRUE(response_info_);
|
| - std::vector<HeaderKeyValuePair> request_headers =
|
| - RequestHeadersToVector(request_info_->headers);
|
| - // We examine the contents of request_info_ and response_info_
|
| - // mainly only in this test case.
|
| - EXPECT_EQ(GURL("ws://localhost/"), request_info_->url);
|
| - EXPECT_EQ(GURL("ws://localhost/"), response_info_->url);
|
| - EXPECT_EQ(101, response_info_->status_code);
|
| - EXPECT_EQ("Switching Protocols", response_info_->status_text);
|
| - ASSERT_EQ(12u, request_headers.size());
|
| - EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
|
| - request_headers[3]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
|
| - request_headers[5]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
|
| - request_headers[6]);
|
| - EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
|
| - request_headers[8]);
|
| - EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
|
| - request_headers[9]);
|
| - EXPECT_EQ("Sec-WebSocket-Key", request_headers[10].first);
|
| - EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
|
| - "permessage-deflate; client_max_window_bits"),
|
| - request_headers[11]);
|
| -
|
| - std::vector<HeaderKeyValuePair> response_headers =
|
| - ResponseHeadersToVector(*response_info_->headers.get());
|
| - ASSERT_EQ(6u, response_headers.size());
|
| - // Sort the headers for ease of verification.
|
| - std::sort(response_headers.begin(), response_headers.end());
|
| -
|
| - EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
|
| - EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
|
| - EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
|
| - EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
|
| - EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
|
| - EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
|
| -}
|
| -
|
| -// Confirm that the stream isn't established until the message loop runs.
|
| -TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
|
| - CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "", "");
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_FALSE(stream_);
|
| -}
|
| -
|
| -// Check the path is used.
|
| -TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", NoSubProtocols(),
|
| - "http://localhost", "", "");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| -}
|
| -
|
| -// Check that the origin is used.
|
| -TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", NoSubProtocols(),
|
| - "http://google.com", "", "");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| -}
|
| -
|
| -// Check that sub-protocols are sent and parsed.
|
| -TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
|
| - std::vector<std::string> sub_protocols;
|
| - sub_protocols.push_back("chatv11.chromium.org");
|
| - sub_protocols.push_back("chatv20.chromium.org");
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", sub_protocols, "http://google.com",
|
| - "Sec-WebSocket-Protocol: chatv11.chromium.org, "
|
| - "chatv20.chromium.org\r\n",
|
| - "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(stream_);
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
|
| -}
|
| -
|
| -// Unsolicited sub-protocols are rejected.
|
| -TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", NoSubProtocols(),
|
| - "http://google.com", "",
|
| - "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "Response must not include 'Sec-WebSocket-Protocol' header "
|
| - "if not present in request: chatv20.chromium.org",
|
| - failure_message());
|
| -}
|
| -
|
| -// Missing sub-protocol response is rejected.
|
| -TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
|
| - std::vector<std::string> sub_protocols;
|
| - sub_protocols.push_back("chat.example.com");
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", sub_protocols, "http://localhost",
|
| - "Sec-WebSocket-Protocol: chat.example.com\r\n", "");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "Sent non-empty 'Sec-WebSocket-Protocol' header "
|
| - "but no response was received",
|
| - failure_message());
|
| -}
|
| -
|
| -// Only one sub-protocol can be accepted.
|
| -TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
|
| - std::vector<std::string> sub_protocols;
|
| - sub_protocols.push_back("chatv11.chromium.org");
|
| - sub_protocols.push_back("chatv20.chromium.org");
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", sub_protocols, "http://google.com",
|
| - "Sec-WebSocket-Protocol: chatv11.chromium.org, "
|
| - "chatv20.chromium.org\r\n",
|
| - "Sec-WebSocket-Protocol: chatv11.chromium.org, "
|
| - "chatv20.chromium.org\r\n");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Sec-WebSocket-Protocol' header must not appear "
|
| - "more than once in a response",
|
| - failure_message());
|
| -}
|
| -
|
| -// Unmatched sub-protocol should be rejected.
|
| -TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) {
|
| - std::vector<std::string> sub_protocols;
|
| - sub_protocols.push_back("chatv11.chromium.org");
|
| - sub_protocols.push_back("chatv20.chromium.org");
|
| - CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
|
| - "/testing_path", sub_protocols, "http://google.com",
|
| - "Sec-WebSocket-Protocol: chatv11.chromium.org, "
|
| - "chatv20.chromium.org\r\n",
|
| - "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
|
| - "in response does not match any of sent values",
|
| - failure_message());
|
| -}
|
| -
|
| -// permessage-deflate extension basic success case.
|
| -TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
|
| - CreateAndConnectWithExtensions("permessage-deflate");
|
| - EXPECT_TRUE(stream_);
|
| - EXPECT_FALSE(has_failed());
|
| -}
|
| -
|
| -// permessage-deflate extensions success with all parameters.
|
| -TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; client_no_context_takeover; "
|
| - "server_max_window_bits=11; client_max_window_bits=13; "
|
| - "server_no_context_takeover");
|
| - EXPECT_TRUE(stream_);
|
| - EXPECT_FALSE(has_failed());
|
| -}
|
| -
|
| -// Verify that incoming messages are actually decompressed with
|
| -// permessage-deflate enabled.
|
| -TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
|
| - CreateAndConnectCustomResponse(
|
| - "ws://localhost/testing_path", "localhost", "/testing_path",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - WebSocketStandardResponse(
|
| - "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
|
| - std::string(
|
| - "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
|
| - "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
|
| - 9));
|
| - WaitUntilConnectDone();
|
| -
|
| - ASSERT_TRUE(stream_);
|
| - ScopedVector<WebSocketFrame> frames;
|
| - CompletionCallback callback;
|
| - ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback));
|
| - ASSERT_EQ(1U, frames.size());
|
| - ASSERT_EQ(5U, frames[0]->header.payload_length);
|
| - EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5));
|
| -}
|
| -
|
| -// Unknown extension in the response is rejected
|
| -TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) {
|
| - CreateAndConnectWithExtensions("x-unknown-extension");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "Found an unsupported extension 'x-unknown-extension' "
|
| - "in 'Sec-WebSocket-Extensions' header",
|
| - failure_message());
|
| -}
|
| -
|
| -// Malformed extensions are rejected (this file does not cover all possible
|
| -// parse failures, as the parser is covered thoroughly by its own unit tests).
|
| -TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) {
|
| - CreateAndConnectWithExtensions(";");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
|
| - "value is rejected by the parser: ;",
|
| - failure_message());
|
| -}
|
| -
|
| -// The permessage-deflate extension may only be specified once.
|
| -TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate, permessage-deflate; client_max_window_bits=10");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: "
|
| - "Received duplicate permessage-deflate response",
|
| - failure_message());
|
| -}
|
| -
|
| -// permessage-deflate parameters may not be duplicated.
|
| -TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; client_no_context_takeover; "
|
| - "client_no_context_takeover");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received duplicate permessage-deflate extension parameter "
|
| - "client_no_context_takeover",
|
| - failure_message());
|
| -}
|
| -
|
| -// permessage-deflate parameters must start with "client_" or "server_"
|
| -TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; absurd_no_context_takeover");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received an unexpected permessage-deflate extension parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// permessage-deflate parameters must be either *_no_context_takeover or
|
| -// *_max_window_bits
|
| -TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; client_max_content_bits=5");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received an unexpected permessage-deflate extension parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_no_context_takeover parameters must not have an argument
|
| -TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; client_no_context_takeover=true");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received invalid client_no_context_takeover parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_max_window_bits must have an argument
|
| -TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
|
| - CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "client_max_window_bits must have value",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_max_window_bits must be an integer
|
| -TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; server_max_window_bits=banana");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received invalid server_max_window_bits parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_max_window_bits must be >= 8
|
| -TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; server_max_window_bits=7");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received invalid server_max_window_bits parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_max_window_bits must be <= 15
|
| -TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; client_max_window_bits=16");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received invalid client_max_window_bits parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_max_window_bits must not start with 0
|
| -TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; client_max_window_bits=08");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received invalid client_max_window_bits parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// *_max_window_bits must not start with +
|
| -TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) {
|
| - CreateAndConnectWithExtensions(
|
| - "permessage-deflate; server_max_window_bits=+9");
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ(
|
| - "Error during WebSocket handshake: Error in permessage-deflate: "
|
| - "Received invalid server_max_window_bits parameter",
|
| - failure_message());
|
| -}
|
| -
|
| -// TODO(ricea): Check that WebSocketDeflateStream is initialised with the
|
| -// arguments from the server. This is difficult because the data written to the
|
| -// socket is randomly masked.
|
| -
|
| -// Additional Sec-WebSocket-Accept headers should be rejected.
|
| -TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
|
| - CreateAndConnectStandard(
|
| - "ws://localhost/", "localhost", "/", NoSubProtocols(), "http://localhost",
|
| - "", "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Sec-WebSocket-Accept' header must not appear "
|
| - "more than once in a response",
|
| - failure_message());
|
| -}
|
| -
|
| -// Response code 200 must be rejected.
|
| -TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
|
| - static const char kInvalidStatusCodeResponse[] =
|
| - "HTTP/1.1 200 OK\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kInvalidStatusCodeResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
|
| - failure_message());
|
| -}
|
| -
|
| -// Redirects are not followed (according to the WHATWG WebSocket API, which
|
| -// overrides RFC6455 for browser applications).
|
| -TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
|
| - static const char kRedirectResponse[] =
|
| - "HTTP/1.1 302 Moved Temporarily\r\n"
|
| - "Content-Type: text/html\r\n"
|
| - "Content-Length: 34\r\n"
|
| - "Connection: keep-alive\r\n"
|
| - "Location: ws://localhost/other\r\n"
|
| - "\r\n"
|
| - "<title>Moved</title><h1>Moved</h1>";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kRedirectResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
|
| - failure_message());
|
| -}
|
| -
|
| -// Malformed responses should be rejected. HttpStreamParser will accept just
|
| -// about any garbage in the middle of the headers. To make it give up, the junk
|
| -// has to be at the start of the response. Even then, it just gets treated as an
|
| -// HTTP/0.9 response.
|
| -TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
|
| - static const char kMalformedResponse[] =
|
| - "220 mx.google.com ESMTP\r\n"
|
| - "HTTP/1.1 101 OK\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kMalformedResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
|
| - failure_message());
|
| -}
|
| -
|
| -// Upgrade header must be present.
|
| -TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
|
| - static const char kMissingUpgradeResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kMissingUpgradeResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
|
| - failure_message());
|
| -}
|
| -
|
| -// There must only be one upgrade header.
|
| -TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
|
| - CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - "Upgrade: HTTP/2.0\r\n");
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Upgrade' header must not appear more than once in a response",
|
| - failure_message());
|
| -}
|
| -
|
| -// There must only be one correct upgrade header.
|
| -TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
|
| - static const char kMissingUpgradeResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "Upgrade: hogefuga\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kMissingUpgradeResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Upgrade' header value is not 'WebSocket': hogefuga",
|
| - failure_message());
|
| -}
|
| -
|
| -// Connection header must be present.
|
| -TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
|
| - static const char kMissingConnectionResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kMissingConnectionResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Connection' header is missing",
|
| - failure_message());
|
| -}
|
| -
|
| -// Connection header must contain "Upgrade".
|
| -TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
|
| - static const char kMissingConnectionResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "Connection: hogefuga\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kMissingConnectionResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Connection' header value must contain 'Upgrade'",
|
| - failure_message());
|
| -}
|
| -
|
| -// Connection header is permitted to contain other tokens.
|
| -TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
|
| - static const char kAdditionalConnectionTokenResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade, Keep-Alive\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kAdditionalConnectionTokenResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| -}
|
| -
|
| -// Sec-WebSocket-Accept header must be present.
|
| -TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
|
| - static const char kMissingAcceptResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kMissingAcceptResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "'Sec-WebSocket-Accept' header is missing",
|
| - failure_message());
|
| -}
|
| -
|
| -// Sec-WebSocket-Accept header must match the key that was sent.
|
| -TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
|
| - static const char kIncorrectAcceptResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
|
| - "\r\n";
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kIncorrectAcceptResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error during WebSocket handshake: "
|
| - "Incorrect 'Sec-WebSocket-Accept' header value",
|
| - failure_message());
|
| -}
|
| -
|
| -// Cancellation works.
|
| -TEST_F(WebSocketStreamCreateTest, Cancellation) {
|
| - CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "", "");
|
| - stream_request_.reset();
|
| - // WaitUntilConnectDone doesn't work in this case.
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_FALSE(request_info_);
|
| - EXPECT_FALSE(response_info_);
|
| -}
|
| -
|
| -// Connect failure must look just like negotiation failure.
|
| -TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
|
| - scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
|
| - socket_data->set_connect_data(
|
| - MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
|
| - CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
|
| - "http://localhost", socket_data.Pass());
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
|
| - failure_message());
|
| - EXPECT_FALSE(request_info_);
|
| - EXPECT_FALSE(response_info_);
|
| -}
|
| -
|
| -// Connect timeout must look just like any other failure.
|
| -TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
|
| - scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
|
| - socket_data->set_connect_data(
|
| - MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
|
| - CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
|
| - "http://localhost", socket_data.Pass());
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
|
| - failure_message());
|
| -}
|
| -
|
| -// The server doesn't respond to the opening handshake.
|
| -TEST_F(WebSocketStreamCreateTest, HandshakeTimeout) {
|
| - scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
|
| - socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
|
| - scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
|
| - base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
|
| - CreateAndConnectRawExpectations("ws://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - socket_data.Pass(),
|
| - timer.Pass());
|
| - EXPECT_FALSE(has_failed());
|
| - ASSERT_TRUE(weak_timer.get());
|
| - EXPECT_TRUE(weak_timer->IsRunning());
|
| -
|
| - weak_timer->Fire();
|
| - WaitUntilConnectDone();
|
| -
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
|
| - ASSERT_TRUE(weak_timer.get());
|
| - EXPECT_FALSE(weak_timer->IsRunning());
|
| -}
|
| -
|
| -// When the connection establishes the timer should be stopped.
|
| -TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnSuccess) {
|
| - scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
|
| - base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
|
| -
|
| - CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "", "",
|
| - timer.Pass());
|
| - ASSERT_TRUE(weak_timer);
|
| - EXPECT_TRUE(weak_timer->IsRunning());
|
| -
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| - ASSERT_TRUE(weak_timer);
|
| - EXPECT_FALSE(weak_timer->IsRunning());
|
| -}
|
| -
|
| -// When the connection fails the timer should be stopped.
|
| -TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnFailure) {
|
| - scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
|
| - socket_data->set_connect_data(
|
| - MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
|
| - scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
|
| - base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
|
| - CreateAndConnectRawExpectations("ws://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - socket_data.Pass(),
|
| - timer.Pass());
|
| - ASSERT_TRUE(weak_timer.get());
|
| - EXPECT_TRUE(weak_timer->IsRunning());
|
| -
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
|
| - failure_message());
|
| - ASSERT_TRUE(weak_timer.get());
|
| - EXPECT_FALSE(weak_timer->IsRunning());
|
| -}
|
| -
|
| -// Cancellation during connect works.
|
| -TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
|
| - scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
|
| - socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
|
| - CreateAndConnectRawExpectations("ws://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - socket_data.Pass());
|
| - stream_request_.reset();
|
| - // WaitUntilConnectDone doesn't work in this case.
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_FALSE(stream_);
|
| -}
|
| -
|
| -// Cancellation during write of the request headers works.
|
| -TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) {
|
| - // We seem to need at least two operations in order to use SetStop().
|
| - MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"),
|
| - MockWrite(ASYNC, 1, "1.1\r\n")};
|
| - // We keep a copy of the pointer so that we can call RunFor() on it later.
|
| - DeterministicSocketData* socket_data(
|
| - new DeterministicSocketData(NULL, 0, writes, arraysize(writes)));
|
| - socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
| - socket_data->SetStop(1);
|
| - CreateAndConnectRawExpectations("ws://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - make_scoped_ptr(socket_data));
|
| - socket_data->Run();
|
| - stream_request_.reset();
|
| - // WaitUntilConnectDone doesn't work in this case.
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(request_info_);
|
| - EXPECT_FALSE(response_info_);
|
| -}
|
| -
|
| -// Cancellation during read of the response headers works.
|
| -TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
|
| - std::string request =
|
| - WebSocketStandardRequest("/", "localhost", "http://localhost", "");
|
| - MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
|
| - MockRead reads[] = {
|
| - MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
|
| - };
|
| - scoped_ptr<DeterministicSocketData> socket_data(
|
| - BuildSocketData(reads, writes));
|
| - socket_data->SetStop(1);
|
| - DeterministicSocketData* socket_data_raw_ptr = socket_data.get();
|
| - CreateAndConnectRawExpectations("ws://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - socket_data.Pass());
|
| - socket_data_raw_ptr->Run();
|
| - stream_request_.reset();
|
| - // WaitUntilConnectDone doesn't work in this case.
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_TRUE(request_info_);
|
| - EXPECT_FALSE(response_info_);
|
| -}
|
| -
|
| -// Over-size response headers (> 256KB) should not cause a crash. This is a
|
| -// regression test for crbug.com/339456. It is based on the layout test
|
| -// "cookie-flood.html".
|
| -TEST_F(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
|
| - std::string set_cookie_headers;
|
| - set_cookie_headers.reserve(45 * 10000);
|
| - for (int i = 0; i < 10000; ++i) {
|
| - set_cookie_headers +=
|
| - base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i);
|
| - }
|
| - CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - set_cookie_headers);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_FALSE(response_info_);
|
| -}
|
| -
|
| -// If the remote host closes the connection without sending headers, we should
|
| -// log the console message "Connection closed before receiving a handshake
|
| -// response".
|
| -TEST_F(WebSocketStreamCreateTest, NoResponse) {
|
| - std::string request =
|
| - WebSocketStandardRequest("/", "localhost", "http://localhost", "");
|
| - MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
|
| - MockRead reads[] = {MockRead(ASYNC, 0, 1)};
|
| - scoped_ptr<DeterministicSocketData> socket_data(
|
| - BuildSocketData(reads, writes));
|
| - DeterministicSocketData* socket_data_raw_ptr = socket_data.get();
|
| - CreateAndConnectRawExpectations("ws://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - socket_data.Pass());
|
| - socket_data_raw_ptr->RunFor(2);
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_FALSE(stream_);
|
| - EXPECT_FALSE(response_info_);
|
| - EXPECT_EQ("Connection closed before receiving a handshake response",
|
| - failure_message());
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
|
| - ssl_data_.push_back(
|
| - new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
|
| - ssl_data_[0]->cert =
|
| - ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
|
| - ASSERT_TRUE(ssl_data_[0]->cert.get());
|
| - scoped_ptr<DeterministicSocketData> raw_socket_data(BuildNullSocketData());
|
| - CreateAndConnectRawExpectations("wss://localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - raw_socket_data.Pass());
|
| - // WaitUntilConnectDone doesn't work in this case.
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(has_failed());
|
| - ASSERT_TRUE(ssl_error_callbacks_);
|
| - ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
|
| - &ssl_info_);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
|
| - scoped_ptr<SSLSocketDataProvider> ssl_data(
|
| - new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
|
| - ssl_data->cert =
|
| - ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
|
| - ASSERT_TRUE(ssl_data->cert.get());
|
| - ssl_data_.push_back(ssl_data.release());
|
| - ssl_data.reset(new SSLSocketDataProvider(ASYNC, OK));
|
| - ssl_data_.push_back(ssl_data.release());
|
| - url_request_context_host_.AddRawExpectations(BuildNullSocketData());
|
| - CreateAndConnectStandard("wss://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "", "");
|
| - // WaitUntilConnectDone doesn't work in this case.
|
| - base::RunLoop().RunUntilIdle();
|
| - ASSERT_TRUE(ssl_error_callbacks_);
|
| - ssl_error_callbacks_->ContinueSSLRequest();
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| -}
|
| -
|
| -// If the server requests authorisation, but we have no credentials, the
|
| -// connection should fail cleanly.
|
| -TEST_F(WebSocketStreamCreateBasicAuthTest, FailureNoCredentials) {
|
| - CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
|
| - NoSubProtocols(), "http://localhost", "",
|
| - kUnauthorizedResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
|
| - failure_message());
|
| - EXPECT_TRUE(response_info_);
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateBasicAuthTest, SuccessPasswordInUrl) {
|
| - CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
|
| - "Zm9vOmJhcg==",
|
| - WebSocketStandardResponse(std::string()));
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| - ASSERT_TRUE(response_info_);
|
| - EXPECT_EQ(101, response_info_->status_code);
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateBasicAuthTest, FailureIncorrectPasswordInUrl) {
|
| - CreateAndConnectAuthHandshake(
|
| - "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse);
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_TRUE(response_info_);
|
| -}
|
| -
|
| -// Digest auth has the same connection semantics as Basic auth, so we can
|
| -// generally assume that whatever works for Basic auth will also work for
|
| -// Digest. There's just one test here, to confirm that it works at all.
|
| -TEST_F(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {
|
| - AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
|
| -
|
| - CreateAndConnectRawExpectations(
|
| - "ws://FooBar:pass@localhost/",
|
| - NoSubProtocols(),
|
| - "http://localhost",
|
| - helper_.BuildSocketData2(kAuthorizedRequest,
|
| - WebSocketStandardResponse(std::string())));
|
| - WaitUntilConnectDone();
|
| - EXPECT_FALSE(has_failed());
|
| - EXPECT_TRUE(stream_);
|
| - ASSERT_TRUE(response_info_);
|
| - EXPECT_EQ(101, response_info_->status_code);
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateUMATest, Incomplete) {
|
| - const std::string name("Net.WebSocket.HandshakeResult");
|
| - scoped_ptr<base::HistogramSamples> original(GetSamples(name));
|
| -
|
| - {
|
| - StreamCreation creation;
|
| - creation.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - creation.NoSubProtocols(),
|
| - "http://localhost", "", "");
|
| - }
|
| -
|
| - scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
|
| - ASSERT_TRUE(samples);
|
| - if (original) {
|
| - samples->Subtract(*original); // Cancel the original values.
|
| - }
|
| - EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
|
| - EXPECT_EQ(0, samples->GetCount(CONNECTED));
|
| - EXPECT_EQ(0, samples->GetCount(FAILED));
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateUMATest, Connected) {
|
| - const std::string name("Net.WebSocket.HandshakeResult");
|
| - scoped_ptr<base::HistogramSamples> original(GetSamples(name));
|
| -
|
| - {
|
| - StreamCreation creation;
|
| - creation.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
|
| - creation.NoSubProtocols(),
|
| - "http://localhost", "", "");
|
| - creation.WaitUntilConnectDone();
|
| - }
|
| -
|
| - scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
|
| - ASSERT_TRUE(samples);
|
| - if (original) {
|
| - samples->Subtract(*original); // Cancel the original values.
|
| - }
|
| - EXPECT_EQ(0, samples->GetCount(INCOMPLETE));
|
| - EXPECT_EQ(1, samples->GetCount(CONNECTED));
|
| - EXPECT_EQ(0, samples->GetCount(FAILED));
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateUMATest, Failed) {
|
| - const std::string name("Net.WebSocket.HandshakeResult");
|
| - scoped_ptr<base::HistogramSamples> original(GetSamples(name));
|
| -
|
| - {
|
| - StreamCreation creation;
|
| - static const char kInvalidStatusCodeResponse[] =
|
| - "HTTP/1.1 200 OK\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "\r\n";
|
| - creation.CreateAndConnectCustomResponse(
|
| - "ws://localhost/", "localhost", "/", creation.NoSubProtocols(),
|
| - "http://localhost", "", kInvalidStatusCodeResponse);
|
| - creation.WaitUntilConnectDone();
|
| - }
|
| -
|
| - scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
|
| - ASSERT_TRUE(samples);
|
| - if (original) {
|
| - samples->Subtract(*original); // Cancel the original values.
|
| - }
|
| - EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
|
| - EXPECT_EQ(0, samples->GetCount(CONNECTED));
|
| - EXPECT_EQ(0, samples->GetCount(FAILED));
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateTest, HandleErrConnectionClosed) {
|
| - static const char kTruncatedResponse[] =
|
| - "HTTP/1.1 101 Switching Protocols\r\n"
|
| - "Upgrade: websocket\r\n"
|
| - "Connection: Upgrade\r\n"
|
| - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
| - "Cache-Control: no-sto";
|
| -
|
| - std::string request =
|
| - WebSocketStandardRequest("/", "localhost", "http://localhost", "");
|
| - MockRead reads[] = {
|
| - MockRead(SYNCHRONOUS, 1, kTruncatedResponse),
|
| - MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED, 2),
|
| - };
|
| - MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, request.c_str())};
|
| - scoped_ptr<DeterministicSocketData> socket_data(
|
| - BuildSocketData(reads, writes));
|
| - socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
| - CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
|
| - "http://localhost", socket_data.Pass());
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| -}
|
| -
|
| -TEST_F(WebSocketStreamCreateTest, HandleErrTunnelConnectionFailed) {
|
| - static const char kConnectRequest[] =
|
| - "CONNECT localhost:80 HTTP/1.1\r\n"
|
| - "Host: localhost\r\n"
|
| - "Proxy-Connection: keep-alive\r\n"
|
| - "\r\n";
|
| -
|
| - static const char kProxyResponse[] =
|
| - "HTTP/1.1 403 Forbidden\r\n"
|
| - "Content-Type: text/html\r\n"
|
| - "Content-Length: 9\r\n"
|
| - "Connection: keep-alive\r\n"
|
| - "\r\n"
|
| - "Forbidden";
|
| -
|
| - MockRead reads[] = {MockRead(SYNCHRONOUS, 1, kProxyResponse)};
|
| - MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, kConnectRequest)};
|
| - scoped_ptr<DeterministicSocketData> socket_data(
|
| - BuildSocketData(reads, writes));
|
| - url_request_context_host_.SetProxyConfig("https=proxy:8000");
|
| - CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
|
| - "http://localhost", socket_data.Pass());
|
| - WaitUntilConnectDone();
|
| - EXPECT_TRUE(has_failed());
|
| - EXPECT_EQ("Establishing a tunnel via proxy server failed.",
|
| - failure_message());
|
| -}
|
| -
|
| -} // namespace
|
| -} // namespace net
|
|
|