Home | History | Annotate | Download | only in websockets
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/websockets/websocket_stream.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 #include <utility>
     10 #include <vector>
     11 
     12 #include "base/compiler_specific.h"
     13 #include "base/memory/scoped_vector.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/metrics/histogram_samples.h"
     16 #include "base/metrics/statistics_recorder.h"
     17 #include "base/run_loop.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/timer/mock_timer.h"
     20 #include "base/timer/timer.h"
     21 #include "net/base/net_errors.h"
     22 #include "net/base/test_data_directory.h"
     23 #include "net/http/http_request_headers.h"
     24 #include "net/http/http_response_headers.h"
     25 #include "net/socket/client_socket_handle.h"
     26 #include "net/socket/socket_test_util.h"
     27 #include "net/test/cert_test_util.h"
     28 #include "net/url_request/url_request_test_util.h"
     29 #include "net/websockets/websocket_basic_handshake_stream.h"
     30 #include "net/websockets/websocket_frame.h"
     31 #include "net/websockets/websocket_handshake_request_info.h"
     32 #include "net/websockets/websocket_handshake_response_info.h"
     33 #include "net/websockets/websocket_handshake_stream_create_helper.h"
     34 #include "net/websockets/websocket_test_util.h"
     35 #include "testing/gtest/include/gtest/gtest.h"
     36 #include "url/gurl.h"
     37 #include "url/origin.h"
     38 
     39 namespace net {
     40 namespace {
     41 
     42 typedef std::pair<std::string, std::string> HeaderKeyValuePair;
     43 
     44 std::vector<HeaderKeyValuePair> ToVector(const HttpRequestHeaders& headers) {
     45   HttpRequestHeaders::Iterator it(headers);
     46   std::vector<HeaderKeyValuePair> result;
     47   while (it.GetNext())
     48     result.push_back(HeaderKeyValuePair(it.name(), it.value()));
     49   return result;
     50 }
     51 
     52 std::vector<HeaderKeyValuePair> ToVector(const HttpResponseHeaders& headers) {
     53   void* iter = NULL;
     54   std::string name, value;
     55   std::vector<HeaderKeyValuePair> result;
     56   while (headers.EnumerateHeaderLines(&iter, &name, &value))
     57     result.push_back(HeaderKeyValuePair(name, value));
     58   return result;
     59 }
     60 
     61 // Simple builder for a DeterministicSocketData object to save repetitive code.
     62 // It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
     63 // be used in tests where the connect fails. In practice, those tests never have
     64 // any read/write data and so can't benefit from it anyway.  The arrays are not
     65 // copied. It is up to the caller to ensure they stay in scope until the test
     66 // ends.
     67 template <size_t reads_count, size_t writes_count>
     68 scoped_ptr<DeterministicSocketData> BuildSocketData(
     69     MockRead (&reads)[reads_count],
     70     MockWrite (&writes)[writes_count]) {
     71   scoped_ptr<DeterministicSocketData> socket_data(
     72       new DeterministicSocketData(reads, reads_count, writes, writes_count));
     73   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
     74   socket_data->SetStop(reads_count + writes_count);
     75   return socket_data.Pass();
     76 }
     77 
     78 // Builder for a DeterministicSocketData that expects nothing. This does not
     79 // set the connect data, so the calling code must do that explicitly.
     80 scoped_ptr<DeterministicSocketData> BuildNullSocketData() {
     81   return make_scoped_ptr(new DeterministicSocketData(NULL, 0, NULL, 0));
     82 }
     83 
     84 class MockWeakTimer : public base::MockTimer,
     85                       public base::SupportsWeakPtr<MockWeakTimer> {
     86  public:
     87   MockWeakTimer(bool retain_user_task, bool is_repeating)
     88       : MockTimer(retain_user_task, is_repeating) {}
     89 };
     90 
     91 // A sub-class of WebSocketHandshakeStreamCreateHelper which always sets a
     92 // deterministic key to use in the WebSocket handshake.
     93 class DeterministicKeyWebSocketHandshakeStreamCreateHelper
     94     : public WebSocketHandshakeStreamCreateHelper {
     95  public:
     96   DeterministicKeyWebSocketHandshakeStreamCreateHelper(
     97       WebSocketStream::ConnectDelegate* connect_delegate,
     98       const std::vector<std::string>& requested_subprotocols)
     99       : WebSocketHandshakeStreamCreateHelper(connect_delegate,
    100                                              requested_subprotocols) {}
    101 
    102   virtual void OnStreamCreated(WebSocketBasicHandshakeStream* stream) OVERRIDE {
    103     stream->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
    104   }
    105 };
    106 
    107 class WebSocketStreamCreateTest : public ::testing::Test {
    108  public:
    109   WebSocketStreamCreateTest() : has_failed_(false), ssl_fatal_(false) {}
    110 
    111   void CreateAndConnectCustomResponse(
    112       const std::string& socket_url,
    113       const std::string& socket_path,
    114       const std::vector<std::string>& sub_protocols,
    115       const std::string& origin,
    116       const std::string& extra_request_headers,
    117       const std::string& response_body,
    118       scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
    119     url_request_context_host_.SetExpectations(
    120         WebSocketStandardRequest(socket_path, origin, extra_request_headers),
    121         response_body);
    122     CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
    123   }
    124 
    125   // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
    126   // errors like "Unable to perform synchronous IO while stopped" will occur.
    127   void CreateAndConnectStandard(const std::string& socket_url,
    128                                 const std::string& socket_path,
    129                                 const std::vector<std::string>& sub_protocols,
    130                                 const std::string& origin,
    131                                 const std::string& extra_request_headers,
    132                                 const std::string& extra_response_headers,
    133                                 scoped_ptr<base::Timer> timer =
    134                                 scoped_ptr<base::Timer>()) {
    135     CreateAndConnectCustomResponse(
    136         socket_url,
    137         socket_path,
    138         sub_protocols,
    139         origin,
    140         extra_request_headers,
    141         WebSocketStandardResponse(extra_response_headers),
    142         timer.Pass());
    143   }
    144 
    145   void CreateAndConnectRawExpectations(
    146       const std::string& socket_url,
    147       const std::vector<std::string>& sub_protocols,
    148       const std::string& origin,
    149       scoped_ptr<DeterministicSocketData> socket_data,
    150       scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
    151     AddRawExpectations(socket_data.Pass());
    152     CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
    153   }
    154 
    155   // Add additional raw expectations for sockets created before the final one.
    156   void AddRawExpectations(scoped_ptr<DeterministicSocketData> socket_data) {
    157     url_request_context_host_.AddRawExpectations(socket_data.Pass());
    158   }
    159 
    160   // A wrapper for CreateAndConnectStreamForTesting that knows about our default
    161   // parameters.
    162   void CreateAndConnectStream(const std::string& socket_url,
    163                               const std::vector<std::string>& sub_protocols,
    164                               const std::string& origin,
    165                               scoped_ptr<base::Timer> timer) {
    166     for (size_t i = 0; i < ssl_data_.size(); ++i) {
    167       scoped_ptr<SSLSocketDataProvider> ssl_data(ssl_data_[i]);
    168       ssl_data_[i] = NULL;
    169       url_request_context_host_.AddSSLSocketDataProvider(ssl_data.Pass());
    170     }
    171     ssl_data_.clear();
    172     scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
    173         new TestConnectDelegate(this));
    174     WebSocketStream::ConnectDelegate* delegate = connect_delegate.get();
    175     scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper(
    176         new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
    177             delegate, sub_protocols));
    178     stream_request_ = ::net::CreateAndConnectStreamForTesting(
    179         GURL(socket_url),
    180         create_helper.Pass(),
    181         url::Origin(origin),
    182         url_request_context_host_.GetURLRequestContext(),
    183         BoundNetLog(),
    184         connect_delegate.Pass(),
    185         timer ? timer.Pass() : scoped_ptr<base::Timer>(
    186             new base::Timer(false, false)));
    187   }
    188 
    189   static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
    190 
    191   // A simple function to make the tests more readable. Creates an empty vector.
    192   static std::vector<std::string> NoSubProtocols() {
    193     return std::vector<std::string>();
    194   }
    195 
    196   const std::string& failure_message() const { return failure_message_; }
    197   bool has_failed() const { return has_failed_; }
    198 
    199   class TestConnectDelegate : public WebSocketStream::ConnectDelegate {
    200    public:
    201     explicit TestConnectDelegate(WebSocketStreamCreateTest* owner)
    202         : owner_(owner) {}
    203 
    204     virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
    205       stream.swap(owner_->stream_);
    206     }
    207 
    208     virtual void OnFailure(const std::string& message) OVERRIDE {
    209       owner_->has_failed_ = true;
    210       owner_->failure_message_ = message;
    211     }
    212 
    213     virtual void OnStartOpeningHandshake(
    214         scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {
    215       // Can be called multiple times (in the case of HTTP auth). Last call
    216       // wins.
    217       owner_->request_info_ = request.Pass();
    218     }
    219     virtual void OnFinishOpeningHandshake(
    220         scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {
    221       if (owner_->response_info_)
    222         ADD_FAILURE();
    223       owner_->response_info_ = response.Pass();
    224     }
    225     virtual void OnSSLCertificateError(
    226         scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
    227             ssl_error_callbacks,
    228         const SSLInfo& ssl_info,
    229         bool fatal) OVERRIDE {
    230       owner_->ssl_error_callbacks_ = ssl_error_callbacks.Pass();
    231       owner_->ssl_info_ = ssl_info;
    232       owner_->ssl_fatal_ = fatal;
    233     }
    234 
    235    private:
    236     WebSocketStreamCreateTest* owner_;
    237   };
    238 
    239   WebSocketTestURLRequestContextHost url_request_context_host_;
    240   scoped_ptr<WebSocketStreamRequest> stream_request_;
    241   // Only set if the connection succeeded.
    242   scoped_ptr<WebSocketStream> stream_;
    243   // Only set if the connection failed.
    244   std::string failure_message_;
    245   bool has_failed_;
    246   scoped_ptr<WebSocketHandshakeRequestInfo> request_info_;
    247   scoped_ptr<WebSocketHandshakeResponseInfo> response_info_;
    248   scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks_;
    249   SSLInfo ssl_info_;
    250   bool ssl_fatal_;
    251   ScopedVector<SSLSocketDataProvider> ssl_data_;
    252 };
    253 
    254 // There are enough tests of the Sec-WebSocket-Extensions header that they
    255 // deserve their own test fixture.
    256 class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest {
    257  public:
    258   // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
    259   // header in the response set to |extensions_header_value|. Runs the event
    260   // loop to allow the connect to complete.
    261   void CreateAndConnectWithExtensions(
    262       const std::string& extensions_header_value) {
    263     CreateAndConnectStandard(
    264         "ws://localhost/testing_path",
    265         "/testing_path",
    266         NoSubProtocols(),
    267         "http://localhost",
    268         "",
    269         "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n");
    270     RunUntilIdle();
    271   }
    272 };
    273 
    274 // Common code to construct expectations for authentication tests that receive
    275 // the auth challenge on one connection and then create a second connection to
    276 // send the authenticated request on.
    277 class CommonAuthTestHelper {
    278  public:
    279   CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
    280 
    281   scoped_ptr<DeterministicSocketData> BuildSocketData1(
    282       const std::string& response) {
    283     request1_ = WebSocketStandardRequest("/", "http://localhost", "");
    284     writes1_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
    285     response1_ = response;
    286     reads1_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
    287     reads1_[1] = MockRead(SYNCHRONOUS, OK, 2);  // Close connection
    288 
    289     return BuildSocketData(reads1_, writes1_);
    290   }
    291 
    292   scoped_ptr<DeterministicSocketData> BuildSocketData2(
    293       const std::string& request,
    294       const std::string& response) {
    295     request2_ = request;
    296     response2_ = response;
    297     writes2_[0] = MockWrite(SYNCHRONOUS, 0, request2_.c_str());
    298     reads2_[0] = MockRead(SYNCHRONOUS, 1, response2_.c_str());
    299     return BuildSocketData(reads2_, writes2_);
    300   }
    301 
    302  private:
    303   // These need to be object-scoped since they have to remain valid until all
    304   // socket operations in the test are complete.
    305   std::string request1_;
    306   std::string request2_;
    307   std::string response1_;
    308   std::string response2_;
    309   MockRead reads1_[2];
    310   MockWrite writes1_[1];
    311   MockRead reads2_[1];
    312   MockWrite writes2_[1];
    313 
    314   DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper);
    315 };
    316 
    317 // Data and methods for BasicAuth tests.
    318 class WebSocketStreamCreateBasicAuthTest : public WebSocketStreamCreateTest {
    319  protected:
    320   void CreateAndConnectAuthHandshake(const std::string& url,
    321                                      const std::string& base64_user_pass,
    322                                      const std::string& response2) {
    323     AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
    324 
    325     static const char request2format[] =
    326         "GET / HTTP/1.1\r\n"
    327         "Host: localhost\r\n"
    328         "Connection: Upgrade\r\n"
    329         "Pragma: no-cache\r\n"
    330         "Cache-Control: no-cache\r\n"
    331         "Authorization: Basic %s\r\n"
    332         "Upgrade: websocket\r\n"
    333         "Origin: http://localhost\r\n"
    334         "Sec-WebSocket-Version: 13\r\n"
    335         "User-Agent:\r\n"
    336         "Accept-Encoding: gzip, deflate\r\n"
    337         "Accept-Language: en-us,fr\r\n"
    338         "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
    339         "Sec-WebSocket-Extensions: permessage-deflate; "
    340         "client_max_window_bits\r\n"
    341         "\r\n";
    342     const std::string request =
    343         base::StringPrintf(request2format, base64_user_pass.c_str());
    344     CreateAndConnectRawExpectations(
    345         url,
    346         NoSubProtocols(),
    347         "http://localhost",
    348         helper_.BuildSocketData2(request, response2));
    349   }
    350 
    351   static const char kUnauthorizedResponse[];
    352 
    353   CommonAuthTestHelper helper_;
    354 };
    355 
    356 class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {
    357  protected:
    358   static const char kUnauthorizedResponse[];
    359   static const char kAuthorizedRequest[];
    360 
    361   CommonAuthTestHelper helper_;
    362 };
    363 
    364 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse[] =
    365     "HTTP/1.1 401 Unauthorized\r\n"
    366     "Content-Length: 0\r\n"
    367     "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
    368     "\r\n";
    369 
    370 // These negotiation values are borrowed from
    371 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
    372 // you are bored. Only the weakest (no qop) variants of Digest authentication
    373 // can be tested by this method, because the others involve random input.
    374 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse[] =
    375     "HTTP/1.1 401 Unauthorized\r\n"
    376     "Content-Length: 0\r\n"
    377     "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
    378     "\r\n";
    379 
    380 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest[] =
    381     "GET / HTTP/1.1\r\n"
    382     "Host: localhost\r\n"
    383     "Connection: Upgrade\r\n"
    384     "Pragma: no-cache\r\n"
    385     "Cache-Control: no-cache\r\n"
    386     "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
    387     "nonce=\"nonce-value\", uri=\"/\", "
    388     "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
    389     "Upgrade: websocket\r\n"
    390     "Origin: http://localhost\r\n"
    391     "Sec-WebSocket-Version: 13\r\n"
    392     "User-Agent:\r\n"
    393     "Accept-Encoding: gzip, deflate\r\n"
    394     "Accept-Language: en-us,fr\r\n"
    395     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
    396     "Sec-WebSocket-Extensions: permessage-deflate; "
    397     "client_max_window_bits\r\n"
    398     "\r\n";
    399 
    400 class WebSocketStreamCreateUMATest : public ::testing::Test {
    401  public:
    402   // This enum should match with the enum in Delegate in websocket_stream.cc.
    403   enum HandshakeResult {
    404     INCOMPLETE,
    405     CONNECTED,
    406     FAILED,
    407     NUM_HANDSHAKE_RESULT_TYPES,
    408   };
    409 
    410   class StreamCreation : public WebSocketStreamCreateTest {
    411     virtual void TestBody() OVERRIDE {}
    412   };
    413 
    414   scoped_ptr<base::HistogramSamples> GetSamples(const std::string& name) {
    415     base::HistogramBase* histogram =
    416         base::StatisticsRecorder::FindHistogram(name);
    417     return histogram ? histogram->SnapshotSamples()
    418                      : scoped_ptr<base::HistogramSamples>();
    419   }
    420 };
    421 
    422 // Confirm that the basic case works as expected.
    423 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
    424   CreateAndConnectStandard(
    425       "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
    426   EXPECT_FALSE(request_info_);
    427   EXPECT_FALSE(response_info_);
    428   RunUntilIdle();
    429   EXPECT_FALSE(has_failed());
    430   EXPECT_TRUE(stream_);
    431   EXPECT_TRUE(request_info_);
    432   EXPECT_TRUE(response_info_);
    433 }
    434 
    435 TEST_F(WebSocketStreamCreateTest, HandshakeInfo) {
    436   static const char kResponse[] =
    437       "HTTP/1.1 101 Switching Protocols\r\n"
    438       "Upgrade: websocket\r\n"
    439       "Connection: Upgrade\r\n"
    440       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
    441       "foo: bar, baz\r\n"
    442       "hoge: fuga\r\n"
    443       "hoge: piyo\r\n"
    444       "\r\n";
    445 
    446   CreateAndConnectCustomResponse(
    447       "ws://localhost/",
    448       "/",
    449       NoSubProtocols(),
    450       "http://localhost",
    451       "",
    452       kResponse);
    453   EXPECT_FALSE(request_info_);
    454   EXPECT_FALSE(response_info_);
    455   RunUntilIdle();
    456   EXPECT_TRUE(stream_);
    457   ASSERT_TRUE(request_info_);
    458   ASSERT_TRUE(response_info_);
    459   std::vector<HeaderKeyValuePair> request_headers =
    460       ToVector(request_info_->headers);
    461   // We examine the contents of request_info_ and response_info_
    462   // mainly only in this test case.
    463   EXPECT_EQ(GURL("ws://localhost/"), request_info_->url);
    464   EXPECT_EQ(GURL("ws://localhost/"), response_info_->url);
    465   EXPECT_EQ(101, response_info_->status_code);
    466   EXPECT_EQ("Switching Protocols", response_info_->status_text);
    467   ASSERT_EQ(12u, request_headers.size());
    468   EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]);
    469   EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
    470   EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
    471   EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
    472             request_headers[3]);
    473   EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
    474   EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
    475             request_headers[5]);
    476   EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
    477             request_headers[6]);
    478   EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
    479   EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
    480             request_headers[8]);
    481   EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
    482             request_headers[9]);
    483   EXPECT_EQ("Sec-WebSocket-Key",  request_headers[10].first);
    484   EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
    485                                "permessage-deflate; client_max_window_bits"),
    486             request_headers[11]);
    487 
    488   std::vector<HeaderKeyValuePair> response_headers =
    489       ToVector(*response_info_->headers.get());
    490   ASSERT_EQ(6u, response_headers.size());
    491   // Sort the headers for ease of verification.
    492   std::sort(response_headers.begin(), response_headers.end());
    493 
    494   EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
    495   EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
    496   EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
    497   EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
    498   EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
    499   EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
    500 }
    501 
    502 // Confirm that the stream isn't established until the message loop runs.
    503 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
    504   CreateAndConnectStandard(
    505       "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
    506   EXPECT_FALSE(has_failed());
    507   EXPECT_FALSE(stream_);
    508 }
    509 
    510 // Check the path is used.
    511 TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
    512   CreateAndConnectStandard("ws://localhost/testing_path",
    513                            "/testing_path",
    514                            NoSubProtocols(),
    515                            "http://localhost",
    516                            "",
    517                            "");
    518   RunUntilIdle();
    519   EXPECT_FALSE(has_failed());
    520   EXPECT_TRUE(stream_);
    521 }
    522 
    523 // Check that the origin is used.
    524 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
    525   CreateAndConnectStandard("ws://localhost/testing_path",
    526                            "/testing_path",
    527                            NoSubProtocols(),
    528                            "http://google.com",
    529                            "",
    530                            "");
    531   RunUntilIdle();
    532   EXPECT_FALSE(has_failed());
    533   EXPECT_TRUE(stream_);
    534 }
    535 
    536 // Check that sub-protocols are sent and parsed.
    537 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
    538   std::vector<std::string> sub_protocols;
    539   sub_protocols.push_back("chatv11.chromium.org");
    540   sub_protocols.push_back("chatv20.chromium.org");
    541   CreateAndConnectStandard("ws://localhost/testing_path",
    542                            "/testing_path",
    543                            sub_protocols,
    544                            "http://google.com",
    545                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
    546                            "chatv20.chromium.org\r\n",
    547                            "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
    548   RunUntilIdle();
    549   EXPECT_TRUE(stream_);
    550   EXPECT_FALSE(has_failed());
    551   EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
    552 }
    553 
    554 // Unsolicited sub-protocols are rejected.
    555 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
    556   CreateAndConnectStandard("ws://localhost/testing_path",
    557                            "/testing_path",
    558                            NoSubProtocols(),
    559                            "http://google.com",
    560                            "",
    561                            "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
    562   RunUntilIdle();
    563   EXPECT_FALSE(stream_);
    564   EXPECT_TRUE(has_failed());
    565   EXPECT_EQ("Error during WebSocket handshake: "
    566             "Response must not include 'Sec-WebSocket-Protocol' header "
    567             "if not present in request: chatv20.chromium.org",
    568             failure_message());
    569 }
    570 
    571 // Missing sub-protocol response is rejected.
    572 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
    573   std::vector<std::string> sub_protocols;
    574   sub_protocols.push_back("chat.example.com");
    575   CreateAndConnectStandard("ws://localhost/testing_path",
    576                            "/testing_path",
    577                            sub_protocols,
    578                            "http://localhost",
    579                            "Sec-WebSocket-Protocol: chat.example.com\r\n",
    580                            "");
    581   RunUntilIdle();
    582   EXPECT_FALSE(stream_);
    583   EXPECT_TRUE(has_failed());
    584   EXPECT_EQ("Error during WebSocket handshake: "
    585             "Sent non-empty 'Sec-WebSocket-Protocol' header "
    586             "but no response was received",
    587             failure_message());
    588 }
    589 
    590 // Only one sub-protocol can be accepted.
    591 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
    592   std::vector<std::string> sub_protocols;
    593   sub_protocols.push_back("chatv11.chromium.org");
    594   sub_protocols.push_back("chatv20.chromium.org");
    595   CreateAndConnectStandard("ws://localhost/testing_path",
    596                            "/testing_path",
    597                            sub_protocols,
    598                            "http://google.com",
    599                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
    600                            "chatv20.chromium.org\r\n",
    601                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
    602                            "chatv20.chromium.org\r\n");
    603   RunUntilIdle();
    604   EXPECT_FALSE(stream_);
    605   EXPECT_TRUE(has_failed());
    606   EXPECT_EQ("Error during WebSocket handshake: "
    607             "'Sec-WebSocket-Protocol' header must not appear "
    608             "more than once in a response",
    609             failure_message());
    610 }
    611 
    612 // Unmatched sub-protocol should be rejected.
    613 TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) {
    614   std::vector<std::string> sub_protocols;
    615   sub_protocols.push_back("chatv11.chromium.org");
    616   sub_protocols.push_back("chatv20.chromium.org");
    617   CreateAndConnectStandard("ws://localhost/testing_path",
    618                            "/testing_path",
    619                            sub_protocols,
    620                            "http://google.com",
    621                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
    622                            "chatv20.chromium.org\r\n",
    623                            "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
    624   RunUntilIdle();
    625   EXPECT_FALSE(stream_);
    626   EXPECT_TRUE(has_failed());
    627   EXPECT_EQ("Error during WebSocket handshake: "
    628             "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
    629             "in response does not match any of sent values",
    630             failure_message());
    631 }
    632 
    633 // permessage-deflate extension basic success case.
    634 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
    635   CreateAndConnectWithExtensions("permessage-deflate");
    636   EXPECT_TRUE(stream_);
    637   EXPECT_FALSE(has_failed());
    638 }
    639 
    640 // permessage-deflate extensions success with all parameters.
    641 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
    642   CreateAndConnectWithExtensions(
    643       "permessage-deflate; client_no_context_takeover; "
    644       "server_max_window_bits=11; client_max_window_bits=13; "
    645       "server_no_context_takeover");
    646   EXPECT_TRUE(stream_);
    647   EXPECT_FALSE(has_failed());
    648 }
    649 
    650 // Verify that incoming messages are actually decompressed with
    651 // permessage-deflate enabled.
    652 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
    653   CreateAndConnectCustomResponse(
    654       "ws://localhost/testing_path",
    655       "/testing_path",
    656       NoSubProtocols(),
    657       "http://localhost",
    658       "",
    659       WebSocketStandardResponse(
    660           "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
    661           std::string(
    662               "\xc1\x07"  // WebSocket header (FIN + RSV1, Text payload 7 bytes)
    663               "\xf2\x48\xcd\xc9\xc9\x07\x00",  // "Hello" DEFLATE compressed
    664               9));
    665   RunUntilIdle();
    666 
    667   ASSERT_TRUE(stream_);
    668   ScopedVector<WebSocketFrame> frames;
    669   CompletionCallback callback;
    670   ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback));
    671   ASSERT_EQ(1U, frames.size());
    672   ASSERT_EQ(5U, frames[0]->header.payload_length);
    673   EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5));
    674 }
    675 
    676 // Unknown extension in the response is rejected
    677 TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) {
    678   CreateAndConnectWithExtensions("x-unknown-extension");
    679   EXPECT_FALSE(stream_);
    680   EXPECT_TRUE(has_failed());
    681   EXPECT_EQ("Error during WebSocket handshake: "
    682             "Found an unsupported extension 'x-unknown-extension' "
    683             "in 'Sec-WebSocket-Extensions' header",
    684             failure_message());
    685 }
    686 
    687 // Malformed extensions are rejected (this file does not cover all possible
    688 // parse failures, as the parser is covered thoroughly by its own unit tests).
    689 TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) {
    690   CreateAndConnectWithExtensions(";");
    691   EXPECT_FALSE(stream_);
    692   EXPECT_TRUE(has_failed());
    693   EXPECT_EQ(
    694       "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
    695       "value is rejected by the parser: ;",
    696       failure_message());
    697 }
    698 
    699 // The permessage-deflate extension may only be specified once.
    700 TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
    701   CreateAndConnectWithExtensions(
    702       "permessage-deflate, permessage-deflate; client_max_window_bits=10");
    703   EXPECT_FALSE(stream_);
    704   EXPECT_TRUE(has_failed());
    705   EXPECT_EQ(
    706       "Error during WebSocket handshake: "
    707       "Received duplicate permessage-deflate response",
    708       failure_message());
    709 }
    710 
    711 // permessage-deflate parameters may not be duplicated.
    712 TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) {
    713   CreateAndConnectWithExtensions(
    714       "permessage-deflate; client_no_context_takeover; "
    715       "client_no_context_takeover");
    716   EXPECT_FALSE(stream_);
    717   EXPECT_TRUE(has_failed());
    718   EXPECT_EQ(
    719       "Error during WebSocket handshake: Error in permessage-deflate: "
    720       "Received duplicate permessage-deflate extension parameter "
    721       "client_no_context_takeover",
    722       failure_message());
    723 }
    724 
    725 // permessage-deflate parameters must start with "client_" or "server_"
    726 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) {
    727   CreateAndConnectWithExtensions(
    728       "permessage-deflate; absurd_no_context_takeover");
    729   EXPECT_FALSE(stream_);
    730   EXPECT_TRUE(has_failed());
    731   EXPECT_EQ(
    732       "Error during WebSocket handshake: Error in permessage-deflate: "
    733       "Received an unexpected permessage-deflate extension parameter",
    734       failure_message());
    735 }
    736 
    737 // permessage-deflate parameters must be either *_no_context_takeover or
    738 // *_max_window_bits
    739 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) {
    740   CreateAndConnectWithExtensions(
    741       "permessage-deflate; client_max_content_bits=5");
    742   EXPECT_FALSE(stream_);
    743   EXPECT_TRUE(has_failed());
    744   EXPECT_EQ(
    745       "Error during WebSocket handshake: Error in permessage-deflate: "
    746       "Received an unexpected permessage-deflate extension parameter",
    747       failure_message());
    748 }
    749 
    750 // *_no_context_takeover parameters must not have an argument
    751 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) {
    752   CreateAndConnectWithExtensions(
    753       "permessage-deflate; client_no_context_takeover=true");
    754   EXPECT_FALSE(stream_);
    755   EXPECT_TRUE(has_failed());
    756   EXPECT_EQ(
    757       "Error during WebSocket handshake: Error in permessage-deflate: "
    758       "Received invalid client_no_context_takeover parameter",
    759       failure_message());
    760 }
    761 
    762 // *_max_window_bits must have an argument
    763 TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
    764   CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
    765   EXPECT_FALSE(stream_);
    766   EXPECT_TRUE(has_failed());
    767   EXPECT_EQ(
    768       "Error during WebSocket handshake: Error in permessage-deflate: "
    769       "client_max_window_bits must have value",
    770       failure_message());
    771 }
    772 
    773 // *_max_window_bits must be an integer
    774 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) {
    775   CreateAndConnectWithExtensions(
    776       "permessage-deflate; server_max_window_bits=banana");
    777   EXPECT_FALSE(stream_);
    778   EXPECT_TRUE(has_failed());
    779   EXPECT_EQ(
    780       "Error during WebSocket handshake: Error in permessage-deflate: "
    781       "Received invalid server_max_window_bits parameter",
    782       failure_message());
    783 }
    784 
    785 // *_max_window_bits must be >= 8
    786 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) {
    787   CreateAndConnectWithExtensions(
    788       "permessage-deflate; server_max_window_bits=7");
    789   EXPECT_FALSE(stream_);
    790   EXPECT_TRUE(has_failed());
    791   EXPECT_EQ(
    792       "Error during WebSocket handshake: Error in permessage-deflate: "
    793       "Received invalid server_max_window_bits parameter",
    794       failure_message());
    795 }
    796 
    797 // *_max_window_bits must be <= 15
    798 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) {
    799   CreateAndConnectWithExtensions(
    800       "permessage-deflate; client_max_window_bits=16");
    801   EXPECT_FALSE(stream_);
    802   EXPECT_TRUE(has_failed());
    803   EXPECT_EQ(
    804       "Error during WebSocket handshake: Error in permessage-deflate: "
    805       "Received invalid client_max_window_bits parameter",
    806       failure_message());
    807 }
    808 
    809 // *_max_window_bits must not start with 0
    810 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) {
    811   CreateAndConnectWithExtensions(
    812       "permessage-deflate; client_max_window_bits=08");
    813   EXPECT_FALSE(stream_);
    814   EXPECT_TRUE(has_failed());
    815   EXPECT_EQ(
    816       "Error during WebSocket handshake: Error in permessage-deflate: "
    817       "Received invalid client_max_window_bits parameter",
    818       failure_message());
    819 }
    820 
    821 // *_max_window_bits must not start with +
    822 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) {
    823   CreateAndConnectWithExtensions(
    824       "permessage-deflate; server_max_window_bits=+9");
    825   EXPECT_FALSE(stream_);
    826   EXPECT_TRUE(has_failed());
    827   EXPECT_EQ(
    828       "Error during WebSocket handshake: Error in permessage-deflate: "
    829       "Received invalid server_max_window_bits parameter",
    830       failure_message());
    831 }
    832 
    833 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
    834 // arguments from the server. This is difficult because the data written to the
    835 // socket is randomly masked.
    836 
    837 // Additional Sec-WebSocket-Accept headers should be rejected.
    838 TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
    839   CreateAndConnectStandard(
    840       "ws://localhost/",
    841       "/",
    842       NoSubProtocols(),
    843       "http://localhost",
    844       "",
    845       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
    846   RunUntilIdle();
    847   EXPECT_FALSE(stream_);
    848   EXPECT_TRUE(has_failed());
    849   EXPECT_EQ("Error during WebSocket handshake: "
    850             "'Sec-WebSocket-Accept' header must not appear "
    851             "more than once in a response",
    852             failure_message());
    853 }
    854 
    855 // Response code 200 must be rejected.
    856 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
    857   static const char kInvalidStatusCodeResponse[] =
    858       "HTTP/1.1 200 OK\r\n"
    859       "Upgrade: websocket\r\n"
    860       "Connection: Upgrade\r\n"
    861       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
    862       "\r\n";
    863   CreateAndConnectCustomResponse("ws://localhost/",
    864                                  "/",
    865                                  NoSubProtocols(),
    866                                  "http://localhost",
    867                                  "",
    868                                  kInvalidStatusCodeResponse);
    869   RunUntilIdle();
    870   EXPECT_TRUE(has_failed());
    871   EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
    872             failure_message());
    873 }
    874 
    875 // Redirects are not followed (according to the WHATWG WebSocket API, which
    876 // overrides RFC6455 for browser applications).
    877 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
    878   static const char kRedirectResponse[] =
    879       "HTTP/1.1 302 Moved Temporarily\r\n"
    880       "Content-Type: text/html\r\n"
    881       "Content-Length: 34\r\n"
    882       "Connection: keep-alive\r\n"
    883       "Location: ws://localhost/other\r\n"
    884       "\r\n"
    885       "<title>Moved</title><h1>Moved</h1>";
    886   CreateAndConnectCustomResponse("ws://localhost/",
    887                                  "/",
    888                                  NoSubProtocols(),
    889                                  "http://localhost",
    890                                  "",
    891                                  kRedirectResponse);
    892   RunUntilIdle();
    893   EXPECT_TRUE(has_failed());
    894   EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
    895             failure_message());
    896 }
    897 
    898 // Malformed responses should be rejected. HttpStreamParser will accept just
    899 // about any garbage in the middle of the headers. To make it give up, the junk
    900 // has to be at the start of the response. Even then, it just gets treated as an
    901 // HTTP/0.9 response.
    902 TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
    903   static const char kMalformedResponse[] =
    904       "220 mx.google.com ESMTP\r\n"
    905       "HTTP/1.1 101 OK\r\n"
    906       "Upgrade: websocket\r\n"
    907       "Connection: Upgrade\r\n"
    908       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
    909       "\r\n";
    910   CreateAndConnectCustomResponse("ws://localhost/",
    911                                  "/",
    912                                  NoSubProtocols(),
    913                                  "http://localhost",
    914                                  "",
    915                                  kMalformedResponse);
    916   RunUntilIdle();
    917   EXPECT_TRUE(has_failed());
    918   EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
    919             failure_message());
    920 }
    921 
    922 // Upgrade header must be present.
    923 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
    924   static const char kMissingUpgradeResponse[] =
    925       "HTTP/1.1 101 Switching Protocols\r\n"
    926       "Connection: Upgrade\r\n"
    927       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
    928       "\r\n";
    929   CreateAndConnectCustomResponse("ws://localhost/",
    930                                  "/",
    931                                  NoSubProtocols(),
    932                                  "http://localhost",
    933                                  "",
    934                                  kMissingUpgradeResponse);
    935   RunUntilIdle();
    936   EXPECT_TRUE(has_failed());
    937   EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
    938             failure_message());
    939 }
    940 
    941 // There must only be one upgrade header.
    942 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
    943   CreateAndConnectStandard(
    944       "ws://localhost/",
    945       "/",
    946       NoSubProtocols(),
    947       "http://localhost",
    948       "", "Upgrade: HTTP/2.0\r\n");
    949   RunUntilIdle();
    950   EXPECT_TRUE(has_failed());
    951   EXPECT_EQ("Error during WebSocket handshake: "
    952             "'Upgrade' header must not appear more than once in a response",
    953             failure_message());
    954 }
    955 
    956 // There must only be one correct upgrade header.
    957 TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
    958   static const char kMissingUpgradeResponse[] =
    959       "HTTP/1.1 101 Switching Protocols\r\n"
    960       "Connection: Upgrade\r\n"
    961       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
    962       "Upgrade: hogefuga\r\n"
    963       "\r\n";
    964   CreateAndConnectCustomResponse("ws://localhost/",
    965                                  "/",
    966                                  NoSubProtocols(),
    967                                  "http://localhost",
    968                                  "",
    969                                  kMissingUpgradeResponse);
    970   RunUntilIdle();
    971   EXPECT_TRUE(has_failed());
    972   EXPECT_EQ("Error during WebSocket handshake: "
    973             "'Upgrade' header value is not 'WebSocket': hogefuga",
    974             failure_message());
    975 }
    976 
    977 // Connection header must be present.
    978 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
    979   static const char kMissingConnectionResponse[] =
    980       "HTTP/1.1 101 Switching Protocols\r\n"
    981       "Upgrade: websocket\r\n"
    982       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
    983       "\r\n";
    984   CreateAndConnectCustomResponse("ws://localhost/",
    985                                  "/",
    986                                  NoSubProtocols(),
    987                                  "http://localhost",
    988                                  "",
    989                                  kMissingConnectionResponse);
    990   RunUntilIdle();
    991   EXPECT_TRUE(has_failed());
    992   EXPECT_EQ("Error during WebSocket handshake: "
    993             "'Connection' header is missing",
    994             failure_message());
    995 }
    996 
    997 // Connection header must contain "Upgrade".
    998 TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
    999   static const char kMissingConnectionResponse[] =
   1000       "HTTP/1.1 101 Switching Protocols\r\n"
   1001       "Upgrade: websocket\r\n"
   1002       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
   1003       "Connection: hogefuga\r\n"
   1004       "\r\n";
   1005   CreateAndConnectCustomResponse("ws://localhost/",
   1006                                  "/",
   1007                                  NoSubProtocols(),
   1008                                  "http://localhost",
   1009                                  "",
   1010                                  kMissingConnectionResponse);
   1011   RunUntilIdle();
   1012   EXPECT_TRUE(has_failed());
   1013   EXPECT_EQ("Error during WebSocket handshake: "
   1014             "'Connection' header value must contain 'Upgrade'",
   1015             failure_message());
   1016 }
   1017 
   1018 // Connection header is permitted to contain other tokens.
   1019 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
   1020   static const char kAdditionalConnectionTokenResponse[] =
   1021       "HTTP/1.1 101 Switching Protocols\r\n"
   1022       "Upgrade: websocket\r\n"
   1023       "Connection: Upgrade, Keep-Alive\r\n"
   1024       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
   1025       "\r\n";
   1026   CreateAndConnectCustomResponse("ws://localhost/",
   1027                                  "/",
   1028                                  NoSubProtocols(),
   1029                                  "http://localhost",
   1030                                  "",
   1031                                  kAdditionalConnectionTokenResponse);
   1032   RunUntilIdle();
   1033   EXPECT_FALSE(has_failed());
   1034   EXPECT_TRUE(stream_);
   1035 }
   1036 
   1037 // Sec-WebSocket-Accept header must be present.
   1038 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
   1039   static const char kMissingAcceptResponse[] =
   1040       "HTTP/1.1 101 Switching Protocols\r\n"
   1041       "Upgrade: websocket\r\n"
   1042       "Connection: Upgrade\r\n"
   1043       "\r\n";
   1044   CreateAndConnectCustomResponse("ws://localhost/",
   1045                                  "/",
   1046                                  NoSubProtocols(),
   1047                                  "http://localhost",
   1048                                  "",
   1049                                  kMissingAcceptResponse);
   1050   RunUntilIdle();
   1051   EXPECT_TRUE(has_failed());
   1052   EXPECT_EQ("Error during WebSocket handshake: "
   1053             "'Sec-WebSocket-Accept' header is missing",
   1054             failure_message());
   1055 }
   1056 
   1057 // Sec-WebSocket-Accept header must match the key that was sent.
   1058 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
   1059   static const char kIncorrectAcceptResponse[] =
   1060       "HTTP/1.1 101 Switching Protocols\r\n"
   1061       "Upgrade: websocket\r\n"
   1062       "Connection: Upgrade\r\n"
   1063       "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
   1064       "\r\n";
   1065   CreateAndConnectCustomResponse("ws://localhost/",
   1066                                  "/",
   1067                                  NoSubProtocols(),
   1068                                  "http://localhost",
   1069                                  "",
   1070                                  kIncorrectAcceptResponse);
   1071   RunUntilIdle();
   1072   EXPECT_TRUE(has_failed());
   1073   EXPECT_EQ("Error during WebSocket handshake: "
   1074             "Incorrect 'Sec-WebSocket-Accept' header value",
   1075             failure_message());
   1076 }
   1077 
   1078 // Cancellation works.
   1079 TEST_F(WebSocketStreamCreateTest, Cancellation) {
   1080   CreateAndConnectStandard(
   1081       "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
   1082   stream_request_.reset();
   1083   RunUntilIdle();
   1084   EXPECT_FALSE(has_failed());
   1085   EXPECT_FALSE(stream_);
   1086   EXPECT_FALSE(request_info_);
   1087   EXPECT_FALSE(response_info_);
   1088 }
   1089 
   1090 // Connect failure must look just like negotiation failure.
   1091 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
   1092   scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
   1093   socket_data->set_connect_data(
   1094       MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
   1095   CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
   1096                                   "http://localhost", socket_data.Pass());
   1097   RunUntilIdle();
   1098   EXPECT_TRUE(has_failed());
   1099   EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
   1100             failure_message());
   1101   EXPECT_FALSE(request_info_);
   1102   EXPECT_FALSE(response_info_);
   1103 }
   1104 
   1105 // Connect timeout must look just like any other failure.
   1106 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
   1107   scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
   1108   socket_data->set_connect_data(
   1109       MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
   1110   CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
   1111                                   "http://localhost", socket_data.Pass());
   1112   RunUntilIdle();
   1113   EXPECT_TRUE(has_failed());
   1114   EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
   1115             failure_message());
   1116 }
   1117 
   1118 // The server doesn't respond to the opening handshake.
   1119 TEST_F(WebSocketStreamCreateTest, HandshakeTimeout) {
   1120   scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
   1121   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   1122   scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
   1123   base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
   1124   CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
   1125                                   "http://localhost", socket_data.Pass(),
   1126                                   timer.PassAs<base::Timer>());
   1127   EXPECT_FALSE(has_failed());
   1128   ASSERT_TRUE(weak_timer.get());
   1129   EXPECT_TRUE(weak_timer->IsRunning());
   1130 
   1131   weak_timer->Fire();
   1132   RunUntilIdle();
   1133 
   1134   EXPECT_TRUE(has_failed());
   1135   EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
   1136   ASSERT_TRUE(weak_timer.get());
   1137   EXPECT_FALSE(weak_timer->IsRunning());
   1138 }
   1139 
   1140 // When the connection establishes the timer should be stopped.
   1141 TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnSuccess) {
   1142   scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
   1143   base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
   1144 
   1145   CreateAndConnectStandard(
   1146       "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "",
   1147       timer.PassAs<base::Timer>());
   1148   ASSERT_TRUE(weak_timer);
   1149   EXPECT_TRUE(weak_timer->IsRunning());
   1150 
   1151   RunUntilIdle();
   1152   EXPECT_FALSE(has_failed());
   1153   EXPECT_TRUE(stream_);
   1154   ASSERT_TRUE(weak_timer);
   1155   EXPECT_FALSE(weak_timer->IsRunning());
   1156 }
   1157 
   1158 // When the connection fails the timer should be stopped.
   1159 TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnFailure) {
   1160   scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
   1161   socket_data->set_connect_data(
   1162       MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
   1163   scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
   1164   base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
   1165   CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
   1166                                   "http://localhost", socket_data.Pass(),
   1167                                   timer.PassAs<base::Timer>());
   1168   ASSERT_TRUE(weak_timer.get());
   1169   EXPECT_TRUE(weak_timer->IsRunning());
   1170 
   1171   RunUntilIdle();
   1172   EXPECT_TRUE(has_failed());
   1173   EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
   1174             failure_message());
   1175   ASSERT_TRUE(weak_timer.get());
   1176   EXPECT_FALSE(weak_timer->IsRunning());
   1177 }
   1178 
   1179 // Cancellation during connect works.
   1180 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
   1181   scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
   1182   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   1183   CreateAndConnectRawExpectations("ws://localhost/",
   1184                                   NoSubProtocols(),
   1185                                   "http://localhost",
   1186                                   socket_data.Pass());
   1187   stream_request_.reset();
   1188   RunUntilIdle();
   1189   EXPECT_FALSE(has_failed());
   1190   EXPECT_FALSE(stream_);
   1191 }
   1192 
   1193 // Cancellation during write of the request headers works.
   1194 TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) {
   1195   // We seem to need at least two operations in order to use SetStop().
   1196   MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"),
   1197                         MockWrite(ASYNC, 1, "1.1\r\n")};
   1198   // We keep a copy of the pointer so that we can call RunFor() on it later.
   1199   DeterministicSocketData* socket_data(
   1200       new DeterministicSocketData(NULL, 0, writes, arraysize(writes)));
   1201   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
   1202   socket_data->SetStop(1);
   1203   CreateAndConnectRawExpectations("ws://localhost/",
   1204                                   NoSubProtocols(),
   1205                                   "http://localhost",
   1206                                   make_scoped_ptr(socket_data));
   1207   socket_data->Run();
   1208   stream_request_.reset();
   1209   RunUntilIdle();
   1210   EXPECT_FALSE(has_failed());
   1211   EXPECT_FALSE(stream_);
   1212   EXPECT_TRUE(request_info_);
   1213   EXPECT_FALSE(response_info_);
   1214 }
   1215 
   1216 // Cancellation during read of the response headers works.
   1217 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
   1218   std::string request = WebSocketStandardRequest("/", "http://localhost", "");
   1219   MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
   1220   MockRead reads[] = {
   1221     MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
   1222   };
   1223   scoped_ptr<DeterministicSocketData> socket_data(
   1224       BuildSocketData(reads, writes));
   1225   socket_data->SetStop(1);
   1226   DeterministicSocketData* socket_data_raw_ptr = socket_data.get();
   1227   CreateAndConnectRawExpectations("ws://localhost/",
   1228                                   NoSubProtocols(),
   1229                                   "http://localhost",
   1230                                   socket_data.Pass());
   1231   socket_data_raw_ptr->Run();
   1232   stream_request_.reset();
   1233   RunUntilIdle();
   1234   EXPECT_FALSE(has_failed());
   1235   EXPECT_FALSE(stream_);
   1236   EXPECT_TRUE(request_info_);
   1237   EXPECT_FALSE(response_info_);
   1238 }
   1239 
   1240 // Over-size response headers (> 256KB) should not cause a crash.  This is a
   1241 // regression test for crbug.com/339456. It is based on the layout test
   1242 // "cookie-flood.html".
   1243 TEST_F(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
   1244   std::string set_cookie_headers;
   1245   set_cookie_headers.reserve(45 * 10000);
   1246   for (int i = 0; i < 10000; ++i) {
   1247     set_cookie_headers +=
   1248         base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i);
   1249   }
   1250   CreateAndConnectStandard("ws://localhost/", "/", NoSubProtocols(),
   1251                            "http://localhost", "", set_cookie_headers);
   1252   RunUntilIdle();
   1253   EXPECT_TRUE(has_failed());
   1254   EXPECT_FALSE(response_info_);
   1255 }
   1256 
   1257 // If the remote host closes the connection without sending headers, we should
   1258 // log the console message "Connection closed before receiving a handshake
   1259 // response".
   1260 TEST_F(WebSocketStreamCreateTest, NoResponse) {
   1261   std::string request = WebSocketStandardRequest("/", "http://localhost", "");
   1262   MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
   1263   MockRead reads[] = {MockRead(ASYNC, 0, 1)};
   1264   scoped_ptr<DeterministicSocketData> socket_data(
   1265       BuildSocketData(reads, writes));
   1266   DeterministicSocketData* socket_data_raw_ptr = socket_data.get();
   1267   CreateAndConnectRawExpectations("ws://localhost/",
   1268                                   NoSubProtocols(),
   1269                                   "http://localhost",
   1270                                   socket_data.Pass());
   1271   socket_data_raw_ptr->RunFor(2);
   1272   EXPECT_TRUE(has_failed());
   1273   EXPECT_FALSE(stream_);
   1274   EXPECT_FALSE(response_info_);
   1275   EXPECT_EQ("Connection closed before receiving a handshake response",
   1276             failure_message());
   1277 }
   1278 
   1279 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
   1280   ssl_data_.push_back(
   1281       new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
   1282   ssl_data_[0]->cert =
   1283       ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
   1284   ASSERT_TRUE(ssl_data_[0]->cert.get());
   1285   scoped_ptr<DeterministicSocketData> raw_socket_data(BuildNullSocketData());
   1286   CreateAndConnectRawExpectations("wss://localhost/",
   1287                                   NoSubProtocols(),
   1288                                   "http://localhost",
   1289                                   raw_socket_data.Pass());
   1290   RunUntilIdle();
   1291   EXPECT_FALSE(has_failed());
   1292   ASSERT_TRUE(ssl_error_callbacks_);
   1293   ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
   1294                                          &ssl_info_);
   1295   RunUntilIdle();
   1296   EXPECT_TRUE(has_failed());
   1297 }
   1298 
   1299 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
   1300   scoped_ptr<SSLSocketDataProvider> ssl_data(
   1301       new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
   1302   ssl_data->cert =
   1303       ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
   1304   ASSERT_TRUE(ssl_data->cert.get());
   1305   ssl_data_.push_back(ssl_data.release());
   1306   ssl_data.reset(new SSLSocketDataProvider(ASYNC, OK));
   1307   ssl_data_.push_back(ssl_data.release());
   1308   url_request_context_host_.AddRawExpectations(BuildNullSocketData());
   1309   CreateAndConnectStandard(
   1310       "wss://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
   1311   RunUntilIdle();
   1312   ASSERT_TRUE(ssl_error_callbacks_);
   1313   ssl_error_callbacks_->ContinueSSLRequest();
   1314   RunUntilIdle();
   1315   EXPECT_FALSE(has_failed());
   1316   EXPECT_TRUE(stream_);
   1317 }
   1318 
   1319 // If the server requests authorisation, but we have no credentials, the
   1320 // connection should fail cleanly.
   1321 TEST_F(WebSocketStreamCreateBasicAuthTest, FailureNoCredentials) {
   1322   CreateAndConnectCustomResponse("ws://localhost/",
   1323                                  "/",
   1324                                  NoSubProtocols(),
   1325                                  "http://localhost",
   1326                                  "",
   1327                                  kUnauthorizedResponse);
   1328   RunUntilIdle();
   1329   EXPECT_TRUE(has_failed());
   1330   EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
   1331             failure_message());
   1332   EXPECT_TRUE(response_info_);
   1333 }
   1334 
   1335 TEST_F(WebSocketStreamCreateBasicAuthTest, SuccessPasswordInUrl) {
   1336   CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
   1337                                 "Zm9vOmJhcg==",
   1338                                 WebSocketStandardResponse(std::string()));
   1339   RunUntilIdle();
   1340   EXPECT_FALSE(has_failed());
   1341   EXPECT_TRUE(stream_);
   1342   ASSERT_TRUE(response_info_);
   1343   EXPECT_EQ(101, response_info_->status_code);
   1344 }
   1345 
   1346 TEST_F(WebSocketStreamCreateBasicAuthTest, FailureIncorrectPasswordInUrl) {
   1347   CreateAndConnectAuthHandshake(
   1348       "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse);
   1349   RunUntilIdle();
   1350   EXPECT_TRUE(has_failed());
   1351   EXPECT_TRUE(response_info_);
   1352 }
   1353 
   1354 // Digest auth has the same connection semantics as Basic auth, so we can
   1355 // generally assume that whatever works for Basic auth will also work for
   1356 // Digest. There's just one test here, to confirm that it works at all.
   1357 TEST_F(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {
   1358   AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
   1359 
   1360   CreateAndConnectRawExpectations(
   1361       "ws://FooBar:pass@localhost/",
   1362       NoSubProtocols(),
   1363       "http://localhost",
   1364       helper_.BuildSocketData2(kAuthorizedRequest,
   1365                                WebSocketStandardResponse(std::string())));
   1366   RunUntilIdle();
   1367   EXPECT_FALSE(has_failed());
   1368   EXPECT_TRUE(stream_);
   1369   ASSERT_TRUE(response_info_);
   1370   EXPECT_EQ(101, response_info_->status_code);
   1371 }
   1372 
   1373 TEST_F(WebSocketStreamCreateUMATest, Incomplete) {
   1374   const std::string name("Net.WebSocket.HandshakeResult");
   1375   scoped_ptr<base::HistogramSamples> original(GetSamples(name));
   1376 
   1377   {
   1378     StreamCreation creation;
   1379     creation.CreateAndConnectStandard("ws://localhost/",
   1380                                       "/",
   1381                                       creation.NoSubProtocols(),
   1382                                       "http://localhost",
   1383                                       "",
   1384                                       "");
   1385   }
   1386 
   1387   scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
   1388   ASSERT_TRUE(samples);
   1389   if (original) {
   1390     samples->Subtract(*original);  // Cancel the original values.
   1391   }
   1392   EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
   1393   EXPECT_EQ(0, samples->GetCount(CONNECTED));
   1394   EXPECT_EQ(0, samples->GetCount(FAILED));
   1395 }
   1396 
   1397 TEST_F(WebSocketStreamCreateUMATest, Connected) {
   1398   const std::string name("Net.WebSocket.HandshakeResult");
   1399   scoped_ptr<base::HistogramSamples> original(GetSamples(name));
   1400 
   1401   {
   1402     StreamCreation creation;
   1403     creation.CreateAndConnectStandard("ws://localhost/",
   1404                                       "/",
   1405                                       creation.NoSubProtocols(),
   1406                                       "http://localhost",
   1407                                       "",
   1408                                       "");
   1409     creation.RunUntilIdle();
   1410   }
   1411 
   1412   scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
   1413   ASSERT_TRUE(samples);
   1414   if (original) {
   1415     samples->Subtract(*original);  // Cancel the original values.
   1416   }
   1417   EXPECT_EQ(0, samples->GetCount(INCOMPLETE));
   1418   EXPECT_EQ(1, samples->GetCount(CONNECTED));
   1419   EXPECT_EQ(0, samples->GetCount(FAILED));
   1420 }
   1421 
   1422 TEST_F(WebSocketStreamCreateUMATest, Failed) {
   1423   const std::string name("Net.WebSocket.HandshakeResult");
   1424   scoped_ptr<base::HistogramSamples> original(GetSamples(name));
   1425 
   1426   {
   1427     StreamCreation creation;
   1428     static const char kInvalidStatusCodeResponse[] =
   1429         "HTTP/1.1 200 OK\r\n"
   1430         "Upgrade: websocket\r\n"
   1431         "Connection: Upgrade\r\n"
   1432         "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
   1433         "\r\n";
   1434     creation.CreateAndConnectCustomResponse("ws://localhost/",
   1435                                             "/",
   1436                                             creation.NoSubProtocols(),
   1437                                             "http://localhost",
   1438                                             "",
   1439                                             kInvalidStatusCodeResponse);
   1440     creation.RunUntilIdle();
   1441   }
   1442 
   1443   scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
   1444   ASSERT_TRUE(samples);
   1445   if (original) {
   1446     samples->Subtract(*original);  // Cancel the original values.
   1447   }
   1448   EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
   1449   EXPECT_EQ(0, samples->GetCount(CONNECTED));
   1450   EXPECT_EQ(0, samples->GetCount(FAILED));
   1451 }
   1452 
   1453 }  // namespace
   1454 }  // namespace net
   1455