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.
5 #include "net/websockets/websocket_stream.h"
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/proxy/proxy_service.h"
26 #include "net/socket/client_socket_handle.h"
27 #include "net/socket/socket_test_util.h"
28 #include "net/test/cert_test_util.h"
29 #include "net/url_request/url_request_test_util.h"
30 #include "net/websockets/websocket_basic_handshake_stream.h"
31 #include "net/websockets/websocket_frame.h"
32 #include "net/websockets/websocket_stream_create_test_base.h"
33 #include "net/websockets/websocket_test_util.h"
34 #include "testing/gtest/include/gtest/gtest.h"
36 #include "url/origin.h"
41 // Simple builder for a SequencedSocketData object to save repetitive code.
42 // It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
43 // be used in tests where the connect fails. In practice, those tests never have
44 // any read/write data and so can't benefit from it anyway. The arrays are not
45 // copied. It is up to the caller to ensure they stay in scope until the test
47 template <size_t reads_count
, size_t writes_count
>
48 scoped_ptr
<SequencedSocketData
> BuildSocketData(
49 MockRead(&reads
)[reads_count
],
50 MockWrite(&writes
)[writes_count
]) {
51 scoped_ptr
<SequencedSocketData
> socket_data(
52 new SequencedSocketData(reads
, reads_count
, writes
, writes_count
));
53 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
54 return socket_data
.Pass();
57 // Builder for a SequencedSocketData that expects nothing. This does not
58 // set the connect data, so the calling code must do that explicitly.
59 scoped_ptr
<SequencedSocketData
> BuildNullSocketData() {
60 return make_scoped_ptr(new SequencedSocketData(NULL
, 0, NULL
, 0));
63 class MockWeakTimer
: public base::MockTimer
,
64 public base::SupportsWeakPtr
<MockWeakTimer
> {
66 MockWeakTimer(bool retain_user_task
, bool is_repeating
)
67 : MockTimer(retain_user_task
, is_repeating
) {}
70 class WebSocketStreamCreateTest
: public ::testing::Test
,
71 public WebSocketStreamCreateTestBase
{
73 ~WebSocketStreamCreateTest() override
{
74 // Permit any endpoint locks to be released.
75 stream_request_
.reset();
77 base::RunLoop().RunUntilIdle();
80 void CreateAndConnectCustomResponse(
81 const std::string
& socket_url
,
82 const std::string
& socket_host
,
83 const std::string
& socket_path
,
84 const std::vector
<std::string
>& sub_protocols
,
85 const std::string
& origin
,
86 const std::string
& extra_request_headers
,
87 const std::string
& response_body
,
88 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
89 url_request_context_host_
.SetExpectations(
90 WebSocketStandardRequest(socket_path
, socket_host
, origin
,
91 extra_request_headers
),
93 CreateAndConnectStream(socket_url
, sub_protocols
, origin
, timer
.Pass());
96 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
97 // errors like "Unable to perform synchronous IO while stopped" will occur.
98 void CreateAndConnectStandard(
99 const std::string
& socket_url
,
100 const std::string
& socket_host
,
101 const std::string
& socket_path
,
102 const std::vector
<std::string
>& sub_protocols
,
103 const std::string
& origin
,
104 const std::string
& extra_request_headers
,
105 const std::string
& extra_response_headers
,
106 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
107 CreateAndConnectCustomResponse(
108 socket_url
, socket_host
, socket_path
, sub_protocols
, origin
,
109 extra_request_headers
,
110 WebSocketStandardResponse(extra_response_headers
), timer
.Pass());
113 void CreateAndConnectRawExpectations(
114 const std::string
& socket_url
,
115 const std::vector
<std::string
>& sub_protocols
,
116 const std::string
& origin
,
117 scoped_ptr
<SequencedSocketData
> socket_data
,
118 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
119 AddRawExpectations(socket_data
.Pass());
120 CreateAndConnectStream(socket_url
, sub_protocols
, origin
, timer
.Pass());
123 // Add additional raw expectations for sockets created before the final one.
124 void AddRawExpectations(scoped_ptr
<SequencedSocketData
> socket_data
) {
125 url_request_context_host_
.AddRawExpectations(socket_data
.Pass());
129 // There are enough tests of the Sec-WebSocket-Extensions header that they
130 // deserve their own test fixture.
131 class WebSocketStreamCreateExtensionTest
: public WebSocketStreamCreateTest
{
133 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
134 // header in the response set to |extensions_header_value|. Runs the event
135 // loop to allow the connect to complete.
136 void CreateAndConnectWithExtensions(
137 const std::string
& extensions_header_value
) {
138 CreateAndConnectStandard(
139 "ws://localhost/testing_path", "localhost", "/testing_path",
140 NoSubProtocols(), "http://localhost", "",
141 "Sec-WebSocket-Extensions: " + extensions_header_value
+ "\r\n");
142 WaitUntilConnectDone();
146 // Common code to construct expectations for authentication tests that receive
147 // the auth challenge on one connection and then create a second connection to
148 // send the authenticated request on.
149 class CommonAuthTestHelper
{
151 CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
153 scoped_ptr
<SequencedSocketData
> BuildSocketData1(
154 const std::string
& response
) {
156 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
157 writes1_
[0] = MockWrite(SYNCHRONOUS
, 0, request1_
.c_str());
158 response1_
= response
;
159 reads1_
[0] = MockRead(SYNCHRONOUS
, 1, response1_
.c_str());
160 reads1_
[1] = MockRead(SYNCHRONOUS
, OK
, 2); // Close connection
162 return BuildSocketData(reads1_
, writes1_
);
165 scoped_ptr
<SequencedSocketData
> BuildSocketData2(
166 const std::string
& request
,
167 const std::string
& response
) {
169 response2_
= response
;
170 writes2_
[0] = MockWrite(SYNCHRONOUS
, 0, request2_
.c_str());
171 reads2_
[0] = MockRead(SYNCHRONOUS
, 1, response2_
.c_str());
172 return BuildSocketData(reads2_
, writes2_
);
176 // These need to be object-scoped since they have to remain valid until all
177 // socket operations in the test are complete.
178 std::string request1_
;
179 std::string request2_
;
180 std::string response1_
;
181 std::string response2_
;
183 MockWrite writes1_
[1];
185 MockWrite writes2_
[1];
187 DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper
);
190 // Data and methods for BasicAuth tests.
191 class WebSocketStreamCreateBasicAuthTest
: public WebSocketStreamCreateTest
{
193 void CreateAndConnectAuthHandshake(const std::string
& url
,
194 const std::string
& base64_user_pass
,
195 const std::string
& response2
) {
196 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
198 static const char request2format
[] =
200 "Host: localhost\r\n"
201 "Connection: Upgrade\r\n"
202 "Pragma: no-cache\r\n"
203 "Cache-Control: no-cache\r\n"
204 "Authorization: Basic %s\r\n"
205 "Upgrade: websocket\r\n"
206 "Origin: http://localhost\r\n"
207 "Sec-WebSocket-Version: 13\r\n"
209 "Accept-Encoding: gzip, deflate\r\n"
210 "Accept-Language: en-us,fr\r\n"
211 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
212 "Sec-WebSocket-Extensions: permessage-deflate; "
213 "client_max_window_bits\r\n"
215 const std::string request
=
216 base::StringPrintf(request2format
, base64_user_pass
.c_str());
217 CreateAndConnectRawExpectations(
221 helper_
.BuildSocketData2(request
, response2
));
224 static const char kUnauthorizedResponse
[];
226 CommonAuthTestHelper helper_
;
229 class WebSocketStreamCreateDigestAuthTest
: public WebSocketStreamCreateTest
{
231 static const char kUnauthorizedResponse
[];
232 static const char kAuthorizedRequest
[];
234 CommonAuthTestHelper helper_
;
237 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse
[] =
238 "HTTP/1.1 401 Unauthorized\r\n"
239 "Content-Length: 0\r\n"
240 "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
243 // These negotiation values are borrowed from
244 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
245 // you are bored. Only the weakest (no qop) variants of Digest authentication
246 // can be tested by this method, because the others involve random input.
247 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse
[] =
248 "HTTP/1.1 401 Unauthorized\r\n"
249 "Content-Length: 0\r\n"
250 "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
253 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest
[] =
255 "Host: localhost\r\n"
256 "Connection: Upgrade\r\n"
257 "Pragma: no-cache\r\n"
258 "Cache-Control: no-cache\r\n"
259 "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
260 "nonce=\"nonce-value\", uri=\"/\", "
261 "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
262 "Upgrade: websocket\r\n"
263 "Origin: http://localhost\r\n"
264 "Sec-WebSocket-Version: 13\r\n"
266 "Accept-Encoding: gzip, deflate\r\n"
267 "Accept-Language: en-us,fr\r\n"
268 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
269 "Sec-WebSocket-Extensions: permessage-deflate; "
270 "client_max_window_bits\r\n"
273 class WebSocketStreamCreateUMATest
: public ::testing::Test
{
275 // This enum should match with the enum in Delegate in websocket_stream.cc.
276 enum HandshakeResult
{
280 NUM_HANDSHAKE_RESULT_TYPES
,
283 class StreamCreation
: public WebSocketStreamCreateTest
{
284 void TestBody() override
{}
287 scoped_ptr
<base::HistogramSamples
> GetSamples(const std::string
& name
) {
288 base::HistogramBase
* histogram
=
289 base::StatisticsRecorder::FindHistogram(name
);
290 return histogram
? histogram
->SnapshotSamples()
291 : scoped_ptr
<base::HistogramSamples
>();
295 // Confirm that the basic case works as expected.
296 TEST_F(WebSocketStreamCreateTest
, SimpleSuccess
) {
297 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
298 NoSubProtocols(), "http://localhost", "", "");
299 EXPECT_FALSE(request_info_
);
300 EXPECT_FALSE(response_info_
);
301 WaitUntilConnectDone();
302 EXPECT_FALSE(has_failed());
303 EXPECT_TRUE(stream_
);
304 EXPECT_TRUE(request_info_
);
305 EXPECT_TRUE(response_info_
);
308 TEST_F(WebSocketStreamCreateTest
, HandshakeInfo
) {
309 static const char kResponse
[] =
310 "HTTP/1.1 101 Switching Protocols\r\n"
311 "Upgrade: websocket\r\n"
312 "Connection: Upgrade\r\n"
313 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
319 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
320 NoSubProtocols(), "http://localhost", "",
322 EXPECT_FALSE(request_info_
);
323 EXPECT_FALSE(response_info_
);
324 WaitUntilConnectDone();
325 EXPECT_TRUE(stream_
);
326 ASSERT_TRUE(request_info_
);
327 ASSERT_TRUE(response_info_
);
328 std::vector
<HeaderKeyValuePair
> request_headers
=
329 RequestHeadersToVector(request_info_
->headers
);
330 // We examine the contents of request_info_ and response_info_
331 // mainly only in this test case.
332 EXPECT_EQ(GURL("ws://localhost/"), request_info_
->url
);
333 EXPECT_EQ(GURL("ws://localhost/"), response_info_
->url
);
334 EXPECT_EQ(101, response_info_
->status_code
);
335 EXPECT_EQ("Switching Protocols", response_info_
->status_text
);
336 ASSERT_EQ(12u, request_headers
.size());
337 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers
[0]);
338 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers
[1]);
339 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers
[2]);
340 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
342 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers
[4]);
343 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
345 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
347 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers
[7]);
348 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
350 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
352 EXPECT_EQ("Sec-WebSocket-Key", request_headers
[10].first
);
353 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
354 "permessage-deflate; client_max_window_bits"),
355 request_headers
[11]);
357 std::vector
<HeaderKeyValuePair
> response_headers
=
358 ResponseHeadersToVector(*response_info_
->headers
.get());
359 ASSERT_EQ(6u, response_headers
.size());
360 // Sort the headers for ease of verification.
361 std::sort(response_headers
.begin(), response_headers
.end());
363 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers
[0]);
364 EXPECT_EQ("Sec-WebSocket-Accept", response_headers
[1].first
);
365 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers
[2]);
366 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers
[3]);
367 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers
[4]);
368 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers
[5]);
371 // Confirm that the stream isn't established until the message loop runs.
372 TEST_F(WebSocketStreamCreateTest
, NeedsToRunLoop
) {
373 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
374 NoSubProtocols(), "http://localhost", "", "");
375 EXPECT_FALSE(has_failed());
376 EXPECT_FALSE(stream_
);
379 // Check the path is used.
380 TEST_F(WebSocketStreamCreateTest
, PathIsUsed
) {
381 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
382 "/testing_path", NoSubProtocols(),
383 "http://localhost", "", "");
384 WaitUntilConnectDone();
385 EXPECT_FALSE(has_failed());
386 EXPECT_TRUE(stream_
);
389 // Check that the origin is used.
390 TEST_F(WebSocketStreamCreateTest
, OriginIsUsed
) {
391 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
392 "/testing_path", NoSubProtocols(),
393 "http://google.com", "", "");
394 WaitUntilConnectDone();
395 EXPECT_FALSE(has_failed());
396 EXPECT_TRUE(stream_
);
399 // Check that sub-protocols are sent and parsed.
400 TEST_F(WebSocketStreamCreateTest
, SubProtocolIsUsed
) {
401 std::vector
<std::string
> sub_protocols
;
402 sub_protocols
.push_back("chatv11.chromium.org");
403 sub_protocols
.push_back("chatv20.chromium.org");
404 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
405 "/testing_path", sub_protocols
, "http://google.com",
406 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
407 "chatv20.chromium.org\r\n",
408 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
409 WaitUntilConnectDone();
410 EXPECT_TRUE(stream_
);
411 EXPECT_FALSE(has_failed());
412 EXPECT_EQ("chatv20.chromium.org", stream_
->GetSubProtocol());
415 // Unsolicited sub-protocols are rejected.
416 TEST_F(WebSocketStreamCreateTest
, UnsolicitedSubProtocol
) {
417 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
418 "/testing_path", NoSubProtocols(),
419 "http://google.com", "",
420 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
421 WaitUntilConnectDone();
422 EXPECT_FALSE(stream_
);
423 EXPECT_TRUE(has_failed());
424 EXPECT_EQ("Error during WebSocket handshake: "
425 "Response must not include 'Sec-WebSocket-Protocol' header "
426 "if not present in request: chatv20.chromium.org",
430 // Missing sub-protocol response is rejected.
431 TEST_F(WebSocketStreamCreateTest
, UnacceptedSubProtocol
) {
432 std::vector
<std::string
> sub_protocols
;
433 sub_protocols
.push_back("chat.example.com");
434 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
435 "/testing_path", sub_protocols
, "http://localhost",
436 "Sec-WebSocket-Protocol: chat.example.com\r\n", "");
437 WaitUntilConnectDone();
438 EXPECT_FALSE(stream_
);
439 EXPECT_TRUE(has_failed());
440 EXPECT_EQ("Error during WebSocket handshake: "
441 "Sent non-empty 'Sec-WebSocket-Protocol' header "
442 "but no response was received",
446 // Only one sub-protocol can be accepted.
447 TEST_F(WebSocketStreamCreateTest
, MultipleSubProtocolsInResponse
) {
448 std::vector
<std::string
> sub_protocols
;
449 sub_protocols
.push_back("chatv11.chromium.org");
450 sub_protocols
.push_back("chatv20.chromium.org");
451 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
452 "/testing_path", sub_protocols
, "http://google.com",
453 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
454 "chatv20.chromium.org\r\n",
455 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
456 "chatv20.chromium.org\r\n");
457 WaitUntilConnectDone();
458 EXPECT_FALSE(stream_
);
459 EXPECT_TRUE(has_failed());
460 EXPECT_EQ("Error during WebSocket handshake: "
461 "'Sec-WebSocket-Protocol' header must not appear "
462 "more than once in a response",
466 // Unmatched sub-protocol should be rejected.
467 TEST_F(WebSocketStreamCreateTest
, UnmatchedSubProtocolInResponse
) {
468 std::vector
<std::string
> sub_protocols
;
469 sub_protocols
.push_back("chatv11.chromium.org");
470 sub_protocols
.push_back("chatv20.chromium.org");
471 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
472 "/testing_path", sub_protocols
, "http://google.com",
473 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
474 "chatv20.chromium.org\r\n",
475 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
476 WaitUntilConnectDone();
477 EXPECT_FALSE(stream_
);
478 EXPECT_TRUE(has_failed());
479 EXPECT_EQ("Error during WebSocket handshake: "
480 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
481 "in response does not match any of sent values",
485 // permessage-deflate extension basic success case.
486 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateSuccess
) {
487 CreateAndConnectWithExtensions("permessage-deflate");
488 EXPECT_TRUE(stream_
);
489 EXPECT_FALSE(has_failed());
492 // permessage-deflate extensions success with all parameters.
493 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateParamsSuccess
) {
494 CreateAndConnectWithExtensions(
495 "permessage-deflate; client_no_context_takeover; "
496 "server_max_window_bits=11; client_max_window_bits=13; "
497 "server_no_context_takeover");
498 EXPECT_TRUE(stream_
);
499 EXPECT_FALSE(has_failed());
502 // Verify that incoming messages are actually decompressed with
503 // permessage-deflate enabled.
504 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateInflates
) {
505 CreateAndConnectCustomResponse(
506 "ws://localhost/testing_path", "localhost", "/testing_path",
507 NoSubProtocols(), "http://localhost", "",
508 WebSocketStandardResponse(
509 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
511 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
512 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
514 WaitUntilConnectDone();
516 ASSERT_TRUE(stream_
);
517 ScopedVector
<WebSocketFrame
> frames
;
518 CompletionCallback callback
;
519 ASSERT_EQ(OK
, stream_
->ReadFrames(&frames
, callback
));
520 ASSERT_EQ(1U, frames
.size());
521 ASSERT_EQ(5U, frames
[0]->header
.payload_length
);
522 EXPECT_EQ("Hello", std::string(frames
[0]->data
->data(), 5));
525 // Unknown extension in the response is rejected
526 TEST_F(WebSocketStreamCreateExtensionTest
, UnknownExtension
) {
527 CreateAndConnectWithExtensions("x-unknown-extension");
528 EXPECT_FALSE(stream_
);
529 EXPECT_TRUE(has_failed());
530 EXPECT_EQ("Error during WebSocket handshake: "
531 "Found an unsupported extension 'x-unknown-extension' "
532 "in 'Sec-WebSocket-Extensions' header",
536 // Malformed extensions are rejected (this file does not cover all possible
537 // parse failures, as the parser is covered thoroughly by its own unit tests).
538 TEST_F(WebSocketStreamCreateExtensionTest
, MalformedExtension
) {
539 CreateAndConnectWithExtensions(";");
540 EXPECT_FALSE(stream_
);
541 EXPECT_TRUE(has_failed());
543 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
544 "value is rejected by the parser: ;",
548 // The permessage-deflate extension may only be specified once.
549 TEST_F(WebSocketStreamCreateExtensionTest
, OnlyOnePerMessageDeflateAllowed
) {
550 CreateAndConnectWithExtensions(
551 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
552 EXPECT_FALSE(stream_
);
553 EXPECT_TRUE(has_failed());
555 "Error during WebSocket handshake: "
556 "Received duplicate permessage-deflate response",
560 // permessage-deflate parameters may not be duplicated.
561 TEST_F(WebSocketStreamCreateExtensionTest
, NoDuplicateParameters
) {
562 CreateAndConnectWithExtensions(
563 "permessage-deflate; client_no_context_takeover; "
564 "client_no_context_takeover");
565 EXPECT_FALSE(stream_
);
566 EXPECT_TRUE(has_failed());
568 "Error during WebSocket handshake: Error in permessage-deflate: "
569 "Received duplicate permessage-deflate extension parameter "
570 "client_no_context_takeover",
574 // permessage-deflate parameters must start with "client_" or "server_"
575 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterPrefix
) {
576 CreateAndConnectWithExtensions(
577 "permessage-deflate; absurd_no_context_takeover");
578 EXPECT_FALSE(stream_
);
579 EXPECT_TRUE(has_failed());
581 "Error during WebSocket handshake: Error in permessage-deflate: "
582 "Received an unexpected permessage-deflate extension parameter",
586 // permessage-deflate parameters must be either *_no_context_takeover or
588 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterSuffix
) {
589 CreateAndConnectWithExtensions(
590 "permessage-deflate; client_max_content_bits=5");
591 EXPECT_FALSE(stream_
);
592 EXPECT_TRUE(has_failed());
594 "Error during WebSocket handshake: Error in permessage-deflate: "
595 "Received an unexpected permessage-deflate extension parameter",
599 // *_no_context_takeover parameters must not have an argument
600 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterValue
) {
601 CreateAndConnectWithExtensions(
602 "permessage-deflate; client_no_context_takeover=true");
603 EXPECT_FALSE(stream_
);
604 EXPECT_TRUE(has_failed());
606 "Error during WebSocket handshake: Error in permessage-deflate: "
607 "Received invalid client_no_context_takeover parameter",
611 // *_max_window_bits must have an argument
612 TEST_F(WebSocketStreamCreateExtensionTest
, NoMaxWindowBitsArgument
) {
613 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
614 EXPECT_FALSE(stream_
);
615 EXPECT_TRUE(has_failed());
617 "Error during WebSocket handshake: Error in permessage-deflate: "
618 "client_max_window_bits must have value",
622 // *_max_window_bits must be an integer
623 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueInteger
) {
624 CreateAndConnectWithExtensions(
625 "permessage-deflate; server_max_window_bits=banana");
626 EXPECT_FALSE(stream_
);
627 EXPECT_TRUE(has_failed());
629 "Error during WebSocket handshake: Error in permessage-deflate: "
630 "Received invalid server_max_window_bits parameter",
634 // *_max_window_bits must be >= 8
635 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueTooSmall
) {
636 CreateAndConnectWithExtensions(
637 "permessage-deflate; server_max_window_bits=7");
638 EXPECT_FALSE(stream_
);
639 EXPECT_TRUE(has_failed());
641 "Error during WebSocket handshake: Error in permessage-deflate: "
642 "Received invalid server_max_window_bits parameter",
646 // *_max_window_bits must be <= 15
647 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueTooBig
) {
648 CreateAndConnectWithExtensions(
649 "permessage-deflate; client_max_window_bits=16");
650 EXPECT_FALSE(stream_
);
651 EXPECT_TRUE(has_failed());
653 "Error during WebSocket handshake: Error in permessage-deflate: "
654 "Received invalid client_max_window_bits parameter",
658 // *_max_window_bits must not start with 0
659 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueStartsWithZero
) {
660 CreateAndConnectWithExtensions(
661 "permessage-deflate; client_max_window_bits=08");
662 EXPECT_FALSE(stream_
);
663 EXPECT_TRUE(has_failed());
665 "Error during WebSocket handshake: Error in permessage-deflate: "
666 "Received invalid client_max_window_bits parameter",
670 // *_max_window_bits must not start with +
671 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueStartsWithPlus
) {
672 CreateAndConnectWithExtensions(
673 "permessage-deflate; server_max_window_bits=+9");
674 EXPECT_FALSE(stream_
);
675 EXPECT_TRUE(has_failed());
677 "Error during WebSocket handshake: Error in permessage-deflate: "
678 "Received invalid server_max_window_bits parameter",
682 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
683 // arguments from the server. This is difficult because the data written to the
684 // socket is randomly masked.
686 // Additional Sec-WebSocket-Accept headers should be rejected.
687 TEST_F(WebSocketStreamCreateTest
, DoubleAccept
) {
688 CreateAndConnectStandard(
689 "ws://localhost/", "localhost", "/", NoSubProtocols(), "http://localhost",
690 "", "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
691 WaitUntilConnectDone();
692 EXPECT_FALSE(stream_
);
693 EXPECT_TRUE(has_failed());
694 EXPECT_EQ("Error during WebSocket handshake: "
695 "'Sec-WebSocket-Accept' header must not appear "
696 "more than once in a response",
700 // Response code 200 must be rejected.
701 TEST_F(WebSocketStreamCreateTest
, InvalidStatusCode
) {
702 static const char kInvalidStatusCodeResponse
[] =
703 "HTTP/1.1 200 OK\r\n"
704 "Upgrade: websocket\r\n"
705 "Connection: Upgrade\r\n"
706 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
708 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
709 NoSubProtocols(), "http://localhost", "",
710 kInvalidStatusCodeResponse
);
711 WaitUntilConnectDone();
712 EXPECT_TRUE(has_failed());
713 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
717 // Redirects are not followed (according to the WHATWG WebSocket API, which
718 // overrides RFC6455 for browser applications).
719 TEST_F(WebSocketStreamCreateTest
, RedirectsRejected
) {
720 static const char kRedirectResponse
[] =
721 "HTTP/1.1 302 Moved Temporarily\r\n"
722 "Content-Type: text/html\r\n"
723 "Content-Length: 34\r\n"
724 "Connection: keep-alive\r\n"
725 "Location: ws://localhost/other\r\n"
727 "<title>Moved</title><h1>Moved</h1>";
728 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
729 NoSubProtocols(), "http://localhost", "",
731 WaitUntilConnectDone();
732 EXPECT_TRUE(has_failed());
733 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
737 // Malformed responses should be rejected. HttpStreamParser will accept just
738 // about any garbage in the middle of the headers. To make it give up, the junk
739 // has to be at the start of the response. Even then, it just gets treated as an
740 // HTTP/0.9 response.
741 TEST_F(WebSocketStreamCreateTest
, MalformedResponse
) {
742 static const char kMalformedResponse
[] =
743 "220 mx.google.com ESMTP\r\n"
744 "HTTP/1.1 101 OK\r\n"
745 "Upgrade: websocket\r\n"
746 "Connection: Upgrade\r\n"
747 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
749 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
750 NoSubProtocols(), "http://localhost", "",
752 WaitUntilConnectDone();
753 EXPECT_TRUE(has_failed());
754 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
758 // Upgrade header must be present.
759 TEST_F(WebSocketStreamCreateTest
, MissingUpgradeHeader
) {
760 static const char kMissingUpgradeResponse
[] =
761 "HTTP/1.1 101 Switching Protocols\r\n"
762 "Connection: Upgrade\r\n"
763 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
765 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
766 NoSubProtocols(), "http://localhost", "",
767 kMissingUpgradeResponse
);
768 WaitUntilConnectDone();
769 EXPECT_TRUE(has_failed());
770 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
774 // There must only be one upgrade header.
775 TEST_F(WebSocketStreamCreateTest
, DoubleUpgradeHeader
) {
776 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
777 NoSubProtocols(), "http://localhost", "",
778 "Upgrade: HTTP/2.0\r\n");
779 WaitUntilConnectDone();
780 EXPECT_TRUE(has_failed());
781 EXPECT_EQ("Error during WebSocket handshake: "
782 "'Upgrade' header must not appear more than once in a response",
786 // There must only be one correct upgrade header.
787 TEST_F(WebSocketStreamCreateTest
, IncorrectUpgradeHeader
) {
788 static const char kMissingUpgradeResponse
[] =
789 "HTTP/1.1 101 Switching Protocols\r\n"
790 "Connection: Upgrade\r\n"
791 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
792 "Upgrade: hogefuga\r\n"
794 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
795 NoSubProtocols(), "http://localhost", "",
796 kMissingUpgradeResponse
);
797 WaitUntilConnectDone();
798 EXPECT_TRUE(has_failed());
799 EXPECT_EQ("Error during WebSocket handshake: "
800 "'Upgrade' header value is not 'WebSocket': hogefuga",
804 // Connection header must be present.
805 TEST_F(WebSocketStreamCreateTest
, MissingConnectionHeader
) {
806 static const char kMissingConnectionResponse
[] =
807 "HTTP/1.1 101 Switching Protocols\r\n"
808 "Upgrade: websocket\r\n"
809 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
811 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
812 NoSubProtocols(), "http://localhost", "",
813 kMissingConnectionResponse
);
814 WaitUntilConnectDone();
815 EXPECT_TRUE(has_failed());
816 EXPECT_EQ("Error during WebSocket handshake: "
817 "'Connection' header is missing",
821 // Connection header must contain "Upgrade".
822 TEST_F(WebSocketStreamCreateTest
, IncorrectConnectionHeader
) {
823 static const char kMissingConnectionResponse
[] =
824 "HTTP/1.1 101 Switching Protocols\r\n"
825 "Upgrade: websocket\r\n"
826 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
827 "Connection: hogefuga\r\n"
829 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
830 NoSubProtocols(), "http://localhost", "",
831 kMissingConnectionResponse
);
832 WaitUntilConnectDone();
833 EXPECT_TRUE(has_failed());
834 EXPECT_EQ("Error during WebSocket handshake: "
835 "'Connection' header value must contain 'Upgrade'",
839 // Connection header is permitted to contain other tokens.
840 TEST_F(WebSocketStreamCreateTest
, AdditionalTokenInConnectionHeader
) {
841 static const char kAdditionalConnectionTokenResponse
[] =
842 "HTTP/1.1 101 Switching Protocols\r\n"
843 "Upgrade: websocket\r\n"
844 "Connection: Upgrade, Keep-Alive\r\n"
845 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
847 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
848 NoSubProtocols(), "http://localhost", "",
849 kAdditionalConnectionTokenResponse
);
850 WaitUntilConnectDone();
851 EXPECT_FALSE(has_failed());
852 EXPECT_TRUE(stream_
);
855 // Sec-WebSocket-Accept header must be present.
856 TEST_F(WebSocketStreamCreateTest
, MissingSecWebSocketAccept
) {
857 static const char kMissingAcceptResponse
[] =
858 "HTTP/1.1 101 Switching Protocols\r\n"
859 "Upgrade: websocket\r\n"
860 "Connection: Upgrade\r\n"
862 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
863 NoSubProtocols(), "http://localhost", "",
864 kMissingAcceptResponse
);
865 WaitUntilConnectDone();
866 EXPECT_TRUE(has_failed());
867 EXPECT_EQ("Error during WebSocket handshake: "
868 "'Sec-WebSocket-Accept' header is missing",
872 // Sec-WebSocket-Accept header must match the key that was sent.
873 TEST_F(WebSocketStreamCreateTest
, WrongSecWebSocketAccept
) {
874 static const char kIncorrectAcceptResponse
[] =
875 "HTTP/1.1 101 Switching Protocols\r\n"
876 "Upgrade: websocket\r\n"
877 "Connection: Upgrade\r\n"
878 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
880 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
881 NoSubProtocols(), "http://localhost", "",
882 kIncorrectAcceptResponse
);
883 WaitUntilConnectDone();
884 EXPECT_TRUE(has_failed());
885 EXPECT_EQ("Error during WebSocket handshake: "
886 "Incorrect 'Sec-WebSocket-Accept' header value",
890 // Cancellation works.
891 TEST_F(WebSocketStreamCreateTest
, Cancellation
) {
892 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
893 NoSubProtocols(), "http://localhost", "", "");
894 stream_request_
.reset();
895 // WaitUntilConnectDone doesn't work in this case.
896 base::RunLoop().RunUntilIdle();
897 EXPECT_FALSE(has_failed());
898 EXPECT_FALSE(stream_
);
899 EXPECT_FALSE(request_info_
);
900 EXPECT_FALSE(response_info_
);
903 // Connect failure must look just like negotiation failure.
904 TEST_F(WebSocketStreamCreateTest
, ConnectionFailure
) {
905 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
906 socket_data
->set_connect_data(
907 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
908 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
909 "http://localhost", socket_data
.Pass());
910 WaitUntilConnectDone();
911 EXPECT_TRUE(has_failed());
912 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
914 EXPECT_FALSE(request_info_
);
915 EXPECT_FALSE(response_info_
);
918 // Connect timeout must look just like any other failure.
919 TEST_F(WebSocketStreamCreateTest
, ConnectionTimeout
) {
920 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
921 socket_data
->set_connect_data(
922 MockConnect(ASYNC
, ERR_CONNECTION_TIMED_OUT
));
923 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
924 "http://localhost", socket_data
.Pass());
925 WaitUntilConnectDone();
926 EXPECT_TRUE(has_failed());
927 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
931 // The server doesn't respond to the opening handshake.
932 TEST_F(WebSocketStreamCreateTest
, HandshakeTimeout
) {
933 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
934 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
935 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
936 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
937 CreateAndConnectRawExpectations("ws://localhost/",
942 EXPECT_FALSE(has_failed());
943 ASSERT_TRUE(weak_timer
.get());
944 EXPECT_TRUE(weak_timer
->IsRunning());
947 WaitUntilConnectDone();
949 EXPECT_TRUE(has_failed());
950 EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
951 ASSERT_TRUE(weak_timer
.get());
952 EXPECT_FALSE(weak_timer
->IsRunning());
955 // When the connection establishes the timer should be stopped.
956 TEST_F(WebSocketStreamCreateTest
, HandshakeTimerOnSuccess
) {
957 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
958 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
960 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
961 NoSubProtocols(), "http://localhost", "", "",
963 ASSERT_TRUE(weak_timer
);
964 EXPECT_TRUE(weak_timer
->IsRunning());
966 WaitUntilConnectDone();
967 EXPECT_FALSE(has_failed());
968 EXPECT_TRUE(stream_
);
969 ASSERT_TRUE(weak_timer
);
970 EXPECT_FALSE(weak_timer
->IsRunning());
973 // When the connection fails the timer should be stopped.
974 TEST_F(WebSocketStreamCreateTest
, HandshakeTimerOnFailure
) {
975 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
976 socket_data
->set_connect_data(
977 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
978 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
979 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
980 CreateAndConnectRawExpectations("ws://localhost/",
985 ASSERT_TRUE(weak_timer
.get());
986 EXPECT_TRUE(weak_timer
->IsRunning());
988 WaitUntilConnectDone();
989 EXPECT_TRUE(has_failed());
990 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
992 ASSERT_TRUE(weak_timer
.get());
993 EXPECT_FALSE(weak_timer
->IsRunning());
996 // Cancellation during connect works.
997 TEST_F(WebSocketStreamCreateTest
, CancellationDuringConnect
) {
998 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
999 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
1000 CreateAndConnectRawExpectations("ws://localhost/",
1003 socket_data
.Pass());
1004 stream_request_
.reset();
1005 // WaitUntilConnectDone doesn't work in this case.
1006 base::RunLoop().RunUntilIdle();
1007 EXPECT_FALSE(has_failed());
1008 EXPECT_FALSE(stream_
);
1011 // Cancellation during write of the request headers works.
1012 TEST_F(WebSocketStreamCreateTest
, CancellationDuringWrite
) {
1013 // First write never completes.
1014 MockWrite writes
[] = {MockWrite(SYNCHRONOUS
, ERR_IO_PENDING
, 0)};
1015 SequencedSocketData
* socket_data(
1016 new SequencedSocketData(NULL
, 0, writes
, arraysize(writes
)));
1017 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
1018 CreateAndConnectRawExpectations("ws://localhost/",
1021 make_scoped_ptr(socket_data
));
1022 base::RunLoop().RunUntilIdle();
1023 EXPECT_TRUE(socket_data
->AllWriteDataConsumed());
1024 stream_request_
.reset();
1025 // WaitUntilConnectDone doesn't work in this case.
1026 base::RunLoop().RunUntilIdle();
1027 EXPECT_FALSE(has_failed());
1028 EXPECT_FALSE(stream_
);
1029 EXPECT_TRUE(request_info_
);
1030 EXPECT_FALSE(response_info_
);
1033 // Cancellation during read of the response headers works.
1034 TEST_F(WebSocketStreamCreateTest
, CancellationDuringRead
) {
1035 std::string request
=
1036 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
1037 MockWrite writes
[] = {MockWrite(ASYNC
, 0, request
.c_str())};
1038 MockRead reads
[] = {
1039 MockRead(SYNCHRONOUS
, ERR_IO_PENDING
, 1),
1041 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
1042 SequencedSocketData
* socket_data_raw_ptr
= socket_data
.get();
1043 CreateAndConnectRawExpectations("ws://localhost/",
1046 socket_data
.Pass());
1047 base::RunLoop().RunUntilIdle();
1048 EXPECT_TRUE(socket_data_raw_ptr
->AllReadDataConsumed());
1049 stream_request_
.reset();
1050 // WaitUntilConnectDone doesn't work in this case.
1051 base::RunLoop().RunUntilIdle();
1052 EXPECT_FALSE(has_failed());
1053 EXPECT_FALSE(stream_
);
1054 EXPECT_TRUE(request_info_
);
1055 EXPECT_FALSE(response_info_
);
1058 // Over-size response headers (> 256KB) should not cause a crash. This is a
1059 // regression test for crbug.com/339456. It is based on the layout test
1060 // "cookie-flood.html".
1061 TEST_F(WebSocketStreamCreateTest
, VeryLargeResponseHeaders
) {
1062 std::string set_cookie_headers
;
1063 set_cookie_headers
.reserve(45 * 10000);
1064 for (int i
= 0; i
< 10000; ++i
) {
1065 set_cookie_headers
+=
1066 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i
);
1068 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1069 NoSubProtocols(), "http://localhost", "",
1070 set_cookie_headers
);
1071 WaitUntilConnectDone();
1072 EXPECT_TRUE(has_failed());
1073 EXPECT_FALSE(response_info_
);
1076 // If the remote host closes the connection without sending headers, we should
1077 // log the console message "Connection closed before receiving a handshake
1079 TEST_F(WebSocketStreamCreateTest
, NoResponse
) {
1080 std::string request
=
1081 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
1082 MockWrite writes
[] = {MockWrite(ASYNC
, request
.data(), request
.size(), 0)};
1083 MockRead reads
[] = {MockRead(ASYNC
, 0, 1)};
1084 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
1085 SequencedSocketData
* socket_data_raw_ptr
= socket_data
.get();
1086 CreateAndConnectRawExpectations("ws://localhost/",
1089 socket_data
.Pass());
1090 base::RunLoop().RunUntilIdle();
1091 EXPECT_TRUE(socket_data_raw_ptr
->AllReadDataConsumed());
1092 EXPECT_TRUE(has_failed());
1093 EXPECT_FALSE(stream_
);
1094 EXPECT_FALSE(response_info_
);
1095 EXPECT_EQ("Connection closed before receiving a handshake response",
1099 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateFailure
) {
1100 ssl_data_
.push_back(
1101 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1102 ssl_data_
[0]->cert
=
1103 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1104 ASSERT_TRUE(ssl_data_
[0]->cert
.get());
1105 scoped_ptr
<SequencedSocketData
> raw_socket_data(BuildNullSocketData());
1106 CreateAndConnectRawExpectations("wss://localhost/",
1109 raw_socket_data
.Pass());
1110 // WaitUntilConnectDone doesn't work in this case.
1111 base::RunLoop().RunUntilIdle();
1112 EXPECT_FALSE(has_failed());
1113 ASSERT_TRUE(ssl_error_callbacks_
);
1114 ssl_error_callbacks_
->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID
,
1116 WaitUntilConnectDone();
1117 EXPECT_TRUE(has_failed());
1120 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateSuccess
) {
1121 scoped_ptr
<SSLSocketDataProvider
> ssl_data(
1122 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1124 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1125 ASSERT_TRUE(ssl_data
->cert
.get());
1126 ssl_data_
.push_back(ssl_data
.Pass());
1127 ssl_data
.reset(new SSLSocketDataProvider(ASYNC
, OK
));
1128 ssl_data_
.push_back(ssl_data
.Pass());
1129 url_request_context_host_
.AddRawExpectations(BuildNullSocketData());
1130 CreateAndConnectStandard("wss://localhost/", "localhost", "/",
1131 NoSubProtocols(), "http://localhost", "", "");
1132 // WaitUntilConnectDone doesn't work in this case.
1133 base::RunLoop().RunUntilIdle();
1134 ASSERT_TRUE(ssl_error_callbacks_
);
1135 ssl_error_callbacks_
->ContinueSSLRequest();
1136 WaitUntilConnectDone();
1137 EXPECT_FALSE(has_failed());
1138 EXPECT_TRUE(stream_
);
1141 // If the server requests authorisation, but we have no credentials, the
1142 // connection should fail cleanly.
1143 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureNoCredentials
) {
1144 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
1145 NoSubProtocols(), "http://localhost", "",
1146 kUnauthorizedResponse
);
1147 WaitUntilConnectDone();
1148 EXPECT_TRUE(has_failed());
1149 EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1151 EXPECT_TRUE(response_info_
);
1154 TEST_F(WebSocketStreamCreateBasicAuthTest
, SuccessPasswordInUrl
) {
1155 CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
1157 WebSocketStandardResponse(std::string()));
1158 WaitUntilConnectDone();
1159 EXPECT_FALSE(has_failed());
1160 EXPECT_TRUE(stream_
);
1161 ASSERT_TRUE(response_info_
);
1162 EXPECT_EQ(101, response_info_
->status_code
);
1165 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureIncorrectPasswordInUrl
) {
1166 CreateAndConnectAuthHandshake(
1167 "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse
);
1168 WaitUntilConnectDone();
1169 EXPECT_TRUE(has_failed());
1170 EXPECT_TRUE(response_info_
);
1173 // Digest auth has the same connection semantics as Basic auth, so we can
1174 // generally assume that whatever works for Basic auth will also work for
1175 // Digest. There's just one test here, to confirm that it works at all.
1176 TEST_F(WebSocketStreamCreateDigestAuthTest
, DigestPasswordInUrl
) {
1177 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
1179 CreateAndConnectRawExpectations(
1180 "ws://FooBar:pass@localhost/",
1183 helper_
.BuildSocketData2(kAuthorizedRequest
,
1184 WebSocketStandardResponse(std::string())));
1185 WaitUntilConnectDone();
1186 EXPECT_FALSE(has_failed());
1187 EXPECT_TRUE(stream_
);
1188 ASSERT_TRUE(response_info_
);
1189 EXPECT_EQ(101, response_info_
->status_code
);
1192 TEST_F(WebSocketStreamCreateUMATest
, Incomplete
) {
1193 const std::string
name("Net.WebSocket.HandshakeResult");
1194 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1197 StreamCreation creation
;
1198 creation
.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1199 creation
.NoSubProtocols(),
1200 "http://localhost", "", "");
1203 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1204 ASSERT_TRUE(samples
);
1206 samples
->Subtract(*original
); // Cancel the original values.
1208 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1209 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1210 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1213 TEST_F(WebSocketStreamCreateUMATest
, Connected
) {
1214 const std::string
name("Net.WebSocket.HandshakeResult");
1215 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1218 StreamCreation creation
;
1219 creation
.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1220 creation
.NoSubProtocols(),
1221 "http://localhost", "", "");
1222 creation
.WaitUntilConnectDone();
1225 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1226 ASSERT_TRUE(samples
);
1228 samples
->Subtract(*original
); // Cancel the original values.
1230 EXPECT_EQ(0, samples
->GetCount(INCOMPLETE
));
1231 EXPECT_EQ(1, samples
->GetCount(CONNECTED
));
1232 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1235 TEST_F(WebSocketStreamCreateUMATest
, Failed
) {
1236 const std::string
name("Net.WebSocket.HandshakeResult");
1237 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1240 StreamCreation creation
;
1241 static const char kInvalidStatusCodeResponse
[] =
1242 "HTTP/1.1 200 OK\r\n"
1243 "Upgrade: websocket\r\n"
1244 "Connection: Upgrade\r\n"
1245 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1247 creation
.CreateAndConnectCustomResponse(
1248 "ws://localhost/", "localhost", "/", creation
.NoSubProtocols(),
1249 "http://localhost", "", kInvalidStatusCodeResponse
);
1250 creation
.WaitUntilConnectDone();
1253 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1254 ASSERT_TRUE(samples
);
1256 samples
->Subtract(*original
); // Cancel the original values.
1258 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1259 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1260 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1263 TEST_F(WebSocketStreamCreateTest
, HandleErrConnectionClosed
) {
1264 static const char kTruncatedResponse
[] =
1265 "HTTP/1.1 101 Switching Protocols\r\n"
1266 "Upgrade: websocket\r\n"
1267 "Connection: Upgrade\r\n"
1268 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1269 "Cache-Control: no-sto";
1271 std::string request
=
1272 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
1273 MockRead reads
[] = {
1274 MockRead(SYNCHRONOUS
, 1, kTruncatedResponse
),
1275 MockRead(SYNCHRONOUS
, ERR_CONNECTION_CLOSED
, 2),
1277 MockWrite writes
[] = {MockWrite(SYNCHRONOUS
, 0, request
.c_str())};
1278 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
1279 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
1280 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1281 "http://localhost", socket_data
.Pass());
1282 WaitUntilConnectDone();
1283 EXPECT_TRUE(has_failed());
1286 TEST_F(WebSocketStreamCreateTest
, HandleErrTunnelConnectionFailed
) {
1287 static const char kConnectRequest
[] =
1288 "CONNECT localhost:80 HTTP/1.1\r\n"
1289 "Host: localhost:80\r\n"
1290 "Proxy-Connection: keep-alive\r\n"
1293 static const char kProxyResponse
[] =
1294 "HTTP/1.1 403 Forbidden\r\n"
1295 "Content-Type: text/html\r\n"
1296 "Content-Length: 9\r\n"
1297 "Connection: keep-alive\r\n"
1301 MockRead reads
[] = {MockRead(SYNCHRONOUS
, 1, kProxyResponse
)};
1302 MockWrite writes
[] = {MockWrite(SYNCHRONOUS
, 0, kConnectRequest
)};
1303 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
1304 url_request_context_host_
.SetProxyConfig("https=proxy:8000");
1305 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1306 "http://localhost", socket_data
.Pass());
1307 WaitUntilConnectDone();
1308 EXPECT_TRUE(has_failed());
1309 EXPECT_EQ("Establishing a tunnel via proxy server failed.",