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
39namespace net {
40namespace {
41
42typedef std::pair<std::string, std::string> HeaderKeyValuePair;
43
44std::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
52std::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.
67template <size_t reads_count, size_t writes_count>
68scoped_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.
80scoped_ptr<DeterministicSocketData> BuildNullSocketData() {
81  return make_scoped_ptr(new DeterministicSocketData(NULL, 0, NULL, 0));
82}
83
84class 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.
93class 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
107class 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.
256class 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.
277class 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.
318class 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
356class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {
357 protected:
358  static const char kUnauthorizedResponse[];
359  static const char kAuthorizedRequest[];
360
361  CommonAuthTestHelper helper_;
362};
363
364const 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.
374const 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
380const 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
400class 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.
423TEST_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
435TEST_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.
503TEST_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.
511TEST_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.
524TEST_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.
537TEST_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.
555TEST_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.
572TEST_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.
591TEST_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.
613TEST_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.
634TEST_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.
641TEST_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.
652TEST_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
677TEST_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).
689TEST_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.
700TEST_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.
712TEST_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_"
726TEST_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
739TEST_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
751TEST_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
763TEST_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
774TEST_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
786TEST_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
798TEST_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
810TEST_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 +
822TEST_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.
838TEST_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.
856TEST_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).
877TEST_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.
902TEST_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.
923TEST_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.
942TEST_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.
957TEST_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.
978TEST_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".
998TEST_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.
1019TEST_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.
1038TEST_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.
1058TEST_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.
1079TEST_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.
1091TEST_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.
1106TEST_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.
1119TEST_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.
1141TEST_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.
1159TEST_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.
1180TEST_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.
1194TEST_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.
1217TEST_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".
1243TEST_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".
1260TEST_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
1279TEST_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
1299TEST_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.
1321TEST_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
1335TEST_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
1346TEST_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.
1357TEST_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
1373TEST_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
1397TEST_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
1422TEST_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