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 static url::Origin
LocalhostOrigin() {
71 return url::Origin(GURL("http://localhost/"));
74 static url::Origin
GoogleOrigin() {
75 return url::Origin(GURL("http://google.com/"));
78 class WebSocketStreamCreateTest
: public ::testing::Test
,
79 public WebSocketStreamCreateTestBase
{
81 ~WebSocketStreamCreateTest() override
{
82 // Permit any endpoint locks to be released.
83 stream_request_
.reset();
85 base::RunLoop().RunUntilIdle();
88 void CreateAndConnectCustomResponse(
89 const std::string
& socket_url
,
90 const std::string
& socket_host
,
91 const std::string
& socket_path
,
92 const std::vector
<std::string
>& sub_protocols
,
93 const url::Origin
& origin
,
94 const std::string
& extra_request_headers
,
95 const std::string
& response_body
,
96 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
97 url_request_context_host_
.SetExpectations(
98 WebSocketStandardRequest(socket_path
, socket_host
, origin
,
99 extra_request_headers
),
101 CreateAndConnectStream(socket_url
, sub_protocols
, origin
, timer
.Pass());
104 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
105 // errors like "Unable to perform synchronous IO while stopped" will occur.
106 void CreateAndConnectStandard(
107 const std::string
& socket_url
,
108 const std::string
& socket_host
,
109 const std::string
& socket_path
,
110 const std::vector
<std::string
>& sub_protocols
,
111 const url::Origin
& origin
,
112 const std::string
& extra_request_headers
,
113 const std::string
& extra_response_headers
,
114 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
115 CreateAndConnectCustomResponse(
116 socket_url
, socket_host
, socket_path
, sub_protocols
, origin
,
117 extra_request_headers
,
118 WebSocketStandardResponse(extra_response_headers
), timer
.Pass());
121 void CreateAndConnectRawExpectations(
122 const std::string
& socket_url
,
123 const std::vector
<std::string
>& sub_protocols
,
124 const url::Origin
& origin
,
125 scoped_ptr
<SequencedSocketData
> socket_data
,
126 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
127 AddRawExpectations(socket_data
.Pass());
128 CreateAndConnectStream(socket_url
, sub_protocols
, origin
, timer
.Pass());
131 // Add additional raw expectations for sockets created before the final one.
132 void AddRawExpectations(scoped_ptr
<SequencedSocketData
> socket_data
) {
133 url_request_context_host_
.AddRawExpectations(socket_data
.Pass());
137 // There are enough tests of the Sec-WebSocket-Extensions header that they
138 // deserve their own test fixture.
139 class WebSocketStreamCreateExtensionTest
: public WebSocketStreamCreateTest
{
141 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
142 // header in the response set to |extensions_header_value|. Runs the event
143 // loop to allow the connect to complete.
144 void CreateAndConnectWithExtensions(
145 const std::string
& extensions_header_value
) {
146 CreateAndConnectStandard(
147 "ws://localhost/testing_path", "localhost", "/testing_path",
148 NoSubProtocols(), LocalhostOrigin(), "",
149 "Sec-WebSocket-Extensions: " + extensions_header_value
+ "\r\n");
150 WaitUntilConnectDone();
154 // Common code to construct expectations for authentication tests that receive
155 // the auth challenge on one connection and then create a second connection to
156 // send the authenticated request on.
157 class CommonAuthTestHelper
{
159 CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
161 scoped_ptr
<SequencedSocketData
> BuildSocketData1(
162 const std::string
& response
) {
164 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
165 writes1_
[0] = MockWrite(SYNCHRONOUS
, 0, request1_
.c_str());
166 response1_
= response
;
167 reads1_
[0] = MockRead(SYNCHRONOUS
, 1, response1_
.c_str());
168 reads1_
[1] = MockRead(SYNCHRONOUS
, OK
, 2); // Close connection
170 return BuildSocketData(reads1_
, writes1_
);
173 scoped_ptr
<SequencedSocketData
> BuildSocketData2(
174 const std::string
& request
,
175 const std::string
& response
) {
177 response2_
= response
;
178 writes2_
[0] = MockWrite(SYNCHRONOUS
, 0, request2_
.c_str());
179 reads2_
[0] = MockRead(SYNCHRONOUS
, 1, response2_
.c_str());
180 return BuildSocketData(reads2_
, writes2_
);
184 // These need to be object-scoped since they have to remain valid until all
185 // socket operations in the test are complete.
186 std::string request1_
;
187 std::string request2_
;
188 std::string response1_
;
189 std::string response2_
;
191 MockWrite writes1_
[1];
193 MockWrite writes2_
[1];
195 DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper
);
198 // Data and methods for BasicAuth tests.
199 class WebSocketStreamCreateBasicAuthTest
: public WebSocketStreamCreateTest
{
201 void CreateAndConnectAuthHandshake(const std::string
& url
,
202 const std::string
& base64_user_pass
,
203 const std::string
& response2
) {
204 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
206 static const char request2format
[] =
208 "Host: localhost\r\n"
209 "Connection: Upgrade\r\n"
210 "Pragma: no-cache\r\n"
211 "Cache-Control: no-cache\r\n"
212 "Authorization: Basic %s\r\n"
213 "Upgrade: websocket\r\n"
214 "Origin: http://localhost\r\n"
215 "Sec-WebSocket-Version: 13\r\n"
217 "Accept-Encoding: gzip, deflate\r\n"
218 "Accept-Language: en-us,fr\r\n"
219 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
220 "Sec-WebSocket-Extensions: permessage-deflate; "
221 "client_max_window_bits\r\n"
223 const std::string request
=
224 base::StringPrintf(request2format
, base64_user_pass
.c_str());
225 CreateAndConnectRawExpectations(
226 url
, NoSubProtocols(), LocalhostOrigin(),
227 helper_
.BuildSocketData2(request
, response2
));
230 static const char kUnauthorizedResponse
[];
232 CommonAuthTestHelper helper_
;
235 class WebSocketStreamCreateDigestAuthTest
: public WebSocketStreamCreateTest
{
237 static const char kUnauthorizedResponse
[];
238 static const char kAuthorizedRequest
[];
240 CommonAuthTestHelper helper_
;
243 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse
[] =
244 "HTTP/1.1 401 Unauthorized\r\n"
245 "Content-Length: 0\r\n"
246 "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
249 // These negotiation values are borrowed from
250 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
251 // you are bored. Only the weakest (no qop) variants of Digest authentication
252 // can be tested by this method, because the others involve random input.
253 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse
[] =
254 "HTTP/1.1 401 Unauthorized\r\n"
255 "Content-Length: 0\r\n"
256 "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
259 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest
[] =
261 "Host: localhost\r\n"
262 "Connection: Upgrade\r\n"
263 "Pragma: no-cache\r\n"
264 "Cache-Control: no-cache\r\n"
265 "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
266 "nonce=\"nonce-value\", uri=\"/\", "
267 "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
268 "Upgrade: websocket\r\n"
269 "Origin: http://localhost\r\n"
270 "Sec-WebSocket-Version: 13\r\n"
272 "Accept-Encoding: gzip, deflate\r\n"
273 "Accept-Language: en-us,fr\r\n"
274 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
275 "Sec-WebSocket-Extensions: permessage-deflate; "
276 "client_max_window_bits\r\n"
279 class WebSocketStreamCreateUMATest
: public ::testing::Test
{
281 // This enum should match with the enum in Delegate in websocket_stream.cc.
282 enum HandshakeResult
{
286 NUM_HANDSHAKE_RESULT_TYPES
,
289 class StreamCreation
: public WebSocketStreamCreateTest
{
290 void TestBody() override
{}
293 scoped_ptr
<base::HistogramSamples
> GetSamples(const std::string
& name
) {
294 base::HistogramBase
* histogram
=
295 base::StatisticsRecorder::FindHistogram(name
);
296 return histogram
? histogram
->SnapshotSamples()
297 : scoped_ptr
<base::HistogramSamples
>();
301 // Confirm that the basic case works as expected.
302 TEST_F(WebSocketStreamCreateTest
, SimpleSuccess
) {
303 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
304 NoSubProtocols(), LocalhostOrigin(), "", "");
305 EXPECT_FALSE(request_info_
);
306 EXPECT_FALSE(response_info_
);
307 WaitUntilConnectDone();
308 EXPECT_FALSE(has_failed());
309 EXPECT_TRUE(stream_
);
310 EXPECT_TRUE(request_info_
);
311 EXPECT_TRUE(response_info_
);
314 TEST_F(WebSocketStreamCreateTest
, HandshakeInfo
) {
315 static const char kResponse
[] =
316 "HTTP/1.1 101 Switching Protocols\r\n"
317 "Upgrade: websocket\r\n"
318 "Connection: Upgrade\r\n"
319 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
325 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
326 NoSubProtocols(), LocalhostOrigin(), "",
328 EXPECT_FALSE(request_info_
);
329 EXPECT_FALSE(response_info_
);
330 WaitUntilConnectDone();
331 EXPECT_TRUE(stream_
);
332 ASSERT_TRUE(request_info_
);
333 ASSERT_TRUE(response_info_
);
334 std::vector
<HeaderKeyValuePair
> request_headers
=
335 RequestHeadersToVector(request_info_
->headers
);
336 // We examine the contents of request_info_ and response_info_
337 // mainly only in this test case.
338 EXPECT_EQ(GURL("ws://localhost/"), request_info_
->url
);
339 EXPECT_EQ(GURL("ws://localhost/"), response_info_
->url
);
340 EXPECT_EQ(101, response_info_
->status_code
);
341 EXPECT_EQ("Switching Protocols", response_info_
->status_text
);
342 ASSERT_EQ(12u, request_headers
.size());
343 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers
[0]);
344 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers
[1]);
345 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers
[2]);
346 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
348 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers
[4]);
349 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
351 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
353 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers
[7]);
354 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
356 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
358 EXPECT_EQ("Sec-WebSocket-Key", request_headers
[10].first
);
359 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
360 "permessage-deflate; client_max_window_bits"),
361 request_headers
[11]);
363 std::vector
<HeaderKeyValuePair
> response_headers
=
364 ResponseHeadersToVector(*response_info_
->headers
.get());
365 ASSERT_EQ(6u, response_headers
.size());
366 // Sort the headers for ease of verification.
367 std::sort(response_headers
.begin(), response_headers
.end());
369 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers
[0]);
370 EXPECT_EQ("Sec-WebSocket-Accept", response_headers
[1].first
);
371 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers
[2]);
372 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers
[3]);
373 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers
[4]);
374 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers
[5]);
377 // Confirm that the stream isn't established until the message loop runs.
378 TEST_F(WebSocketStreamCreateTest
, NeedsToRunLoop
) {
379 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
380 NoSubProtocols(), LocalhostOrigin(), "", "");
381 EXPECT_FALSE(has_failed());
382 EXPECT_FALSE(stream_
);
385 // Check the path is used.
386 TEST_F(WebSocketStreamCreateTest
, PathIsUsed
) {
387 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
388 "/testing_path", NoSubProtocols(), LocalhostOrigin(),
390 WaitUntilConnectDone();
391 EXPECT_FALSE(has_failed());
392 EXPECT_TRUE(stream_
);
395 // Check that the origin is used.
396 TEST_F(WebSocketStreamCreateTest
, OriginIsUsed
) {
397 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
398 "/testing_path", NoSubProtocols(), GoogleOrigin(),
400 WaitUntilConnectDone();
401 EXPECT_FALSE(has_failed());
402 EXPECT_TRUE(stream_
);
405 // Check that sub-protocols are sent and parsed.
406 TEST_F(WebSocketStreamCreateTest
, SubProtocolIsUsed
) {
407 std::vector
<std::string
> sub_protocols
;
408 sub_protocols
.push_back("chatv11.chromium.org");
409 sub_protocols
.push_back("chatv20.chromium.org");
410 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
411 "/testing_path", sub_protocols
, GoogleOrigin(),
412 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
413 "chatv20.chromium.org\r\n",
414 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
415 WaitUntilConnectDone();
416 EXPECT_TRUE(stream_
);
417 EXPECT_FALSE(has_failed());
418 EXPECT_EQ("chatv20.chromium.org", stream_
->GetSubProtocol());
421 // Unsolicited sub-protocols are rejected.
422 TEST_F(WebSocketStreamCreateTest
, UnsolicitedSubProtocol
) {
423 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
424 "/testing_path", NoSubProtocols(), GoogleOrigin(),
426 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
427 WaitUntilConnectDone();
428 EXPECT_FALSE(stream_
);
429 EXPECT_TRUE(has_failed());
430 EXPECT_EQ("Error during WebSocket handshake: "
431 "Response must not include 'Sec-WebSocket-Protocol' header "
432 "if not present in request: chatv20.chromium.org",
436 // Missing sub-protocol response is rejected.
437 TEST_F(WebSocketStreamCreateTest
, UnacceptedSubProtocol
) {
438 std::vector
<std::string
> sub_protocols
;
439 sub_protocols
.push_back("chat.example.com");
440 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
441 "/testing_path", sub_protocols
, LocalhostOrigin(),
442 "Sec-WebSocket-Protocol: chat.example.com\r\n", "");
443 WaitUntilConnectDone();
444 EXPECT_FALSE(stream_
);
445 EXPECT_TRUE(has_failed());
446 EXPECT_EQ("Error during WebSocket handshake: "
447 "Sent non-empty 'Sec-WebSocket-Protocol' header "
448 "but no response was received",
452 // Only one sub-protocol can be accepted.
453 TEST_F(WebSocketStreamCreateTest
, MultipleSubProtocolsInResponse
) {
454 std::vector
<std::string
> sub_protocols
;
455 sub_protocols
.push_back("chatv11.chromium.org");
456 sub_protocols
.push_back("chatv20.chromium.org");
457 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
458 "/testing_path", sub_protocols
, GoogleOrigin(),
459 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
460 "chatv20.chromium.org\r\n",
461 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
462 "chatv20.chromium.org\r\n");
463 WaitUntilConnectDone();
464 EXPECT_FALSE(stream_
);
465 EXPECT_TRUE(has_failed());
466 EXPECT_EQ("Error during WebSocket handshake: "
467 "'Sec-WebSocket-Protocol' header must not appear "
468 "more than once in a response",
472 // Unmatched sub-protocol should be rejected.
473 TEST_F(WebSocketStreamCreateTest
, UnmatchedSubProtocolInResponse
) {
474 std::vector
<std::string
> sub_protocols
;
475 sub_protocols
.push_back("chatv11.chromium.org");
476 sub_protocols
.push_back("chatv20.chromium.org");
477 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
478 "/testing_path", sub_protocols
, GoogleOrigin(),
479 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
480 "chatv20.chromium.org\r\n",
481 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
482 WaitUntilConnectDone();
483 EXPECT_FALSE(stream_
);
484 EXPECT_TRUE(has_failed());
485 EXPECT_EQ("Error during WebSocket handshake: "
486 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
487 "in response does not match any of sent values",
491 // permessage-deflate extension basic success case.
492 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateSuccess
) {
493 CreateAndConnectWithExtensions("permessage-deflate");
494 EXPECT_TRUE(stream_
);
495 EXPECT_FALSE(has_failed());
498 // permessage-deflate extensions success with all parameters.
499 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateParamsSuccess
) {
500 CreateAndConnectWithExtensions(
501 "permessage-deflate; client_no_context_takeover; "
502 "server_max_window_bits=11; client_max_window_bits=13; "
503 "server_no_context_takeover");
504 EXPECT_TRUE(stream_
);
505 EXPECT_FALSE(has_failed());
508 // Verify that incoming messages are actually decompressed with
509 // permessage-deflate enabled.
510 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateInflates
) {
511 CreateAndConnectCustomResponse(
512 "ws://localhost/testing_path", "localhost", "/testing_path",
513 NoSubProtocols(), LocalhostOrigin(), "",
514 WebSocketStandardResponse(
515 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
517 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
518 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
520 WaitUntilConnectDone();
522 ASSERT_TRUE(stream_
);
523 ScopedVector
<WebSocketFrame
> frames
;
524 CompletionCallback callback
;
525 ASSERT_EQ(OK
, stream_
->ReadFrames(&frames
, callback
));
526 ASSERT_EQ(1U, frames
.size());
527 ASSERT_EQ(5U, frames
[0]->header
.payload_length
);
528 EXPECT_EQ("Hello", std::string(frames
[0]->data
->data(), 5));
531 // Unknown extension in the response is rejected
532 TEST_F(WebSocketStreamCreateExtensionTest
, UnknownExtension
) {
533 CreateAndConnectWithExtensions("x-unknown-extension");
534 EXPECT_FALSE(stream_
);
535 EXPECT_TRUE(has_failed());
536 EXPECT_EQ("Error during WebSocket handshake: "
537 "Found an unsupported extension 'x-unknown-extension' "
538 "in 'Sec-WebSocket-Extensions' header",
542 // Malformed extensions are rejected (this file does not cover all possible
543 // parse failures, as the parser is covered thoroughly by its own unit tests).
544 TEST_F(WebSocketStreamCreateExtensionTest
, MalformedExtension
) {
545 CreateAndConnectWithExtensions(";");
546 EXPECT_FALSE(stream_
);
547 EXPECT_TRUE(has_failed());
549 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
550 "value is rejected by the parser: ;",
554 // The permessage-deflate extension may only be specified once.
555 TEST_F(WebSocketStreamCreateExtensionTest
, OnlyOnePerMessageDeflateAllowed
) {
556 CreateAndConnectWithExtensions(
557 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
558 EXPECT_FALSE(stream_
);
559 EXPECT_TRUE(has_failed());
561 "Error during WebSocket handshake: "
562 "Received duplicate permessage-deflate response",
566 // client_max_window_bits must have an argument
567 TEST_F(WebSocketStreamCreateExtensionTest
, NoMaxWindowBitsArgument
) {
568 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
569 EXPECT_FALSE(stream_
);
570 EXPECT_TRUE(has_failed());
572 "Error during WebSocket handshake: Error in permessage-deflate: "
573 "client_max_window_bits must have value",
577 // Other cases for permessage-deflate parameters are tested in
578 // websocket_deflate_parameters_test.cc.
580 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
581 // arguments from the server. This is difficult because the data written to the
582 // socket is randomly masked.
584 // Additional Sec-WebSocket-Accept headers should be rejected.
585 TEST_F(WebSocketStreamCreateTest
, DoubleAccept
) {
586 CreateAndConnectStandard(
587 "ws://localhost/", "localhost", "/", NoSubProtocols(), LocalhostOrigin(),
588 "", "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
589 WaitUntilConnectDone();
590 EXPECT_FALSE(stream_
);
591 EXPECT_TRUE(has_failed());
592 EXPECT_EQ("Error during WebSocket handshake: "
593 "'Sec-WebSocket-Accept' header must not appear "
594 "more than once in a response",
598 // Response code 200 must be rejected.
599 TEST_F(WebSocketStreamCreateTest
, InvalidStatusCode
) {
600 static const char kInvalidStatusCodeResponse
[] =
601 "HTTP/1.1 200 OK\r\n"
602 "Upgrade: websocket\r\n"
603 "Connection: Upgrade\r\n"
604 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
606 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
607 NoSubProtocols(), LocalhostOrigin(), "",
608 kInvalidStatusCodeResponse
);
609 WaitUntilConnectDone();
610 EXPECT_TRUE(has_failed());
611 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
615 // Redirects are not followed (according to the WHATWG WebSocket API, which
616 // overrides RFC6455 for browser applications).
617 TEST_F(WebSocketStreamCreateTest
, RedirectsRejected
) {
618 static const char kRedirectResponse
[] =
619 "HTTP/1.1 302 Moved Temporarily\r\n"
620 "Content-Type: text/html\r\n"
621 "Content-Length: 34\r\n"
622 "Connection: keep-alive\r\n"
623 "Location: ws://localhost/other\r\n"
625 "<title>Moved</title><h1>Moved</h1>";
626 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
627 NoSubProtocols(), LocalhostOrigin(), "",
629 WaitUntilConnectDone();
630 EXPECT_TRUE(has_failed());
631 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
635 // Malformed responses should be rejected. HttpStreamParser will accept just
636 // about any garbage in the middle of the headers. To make it give up, the junk
637 // has to be at the start of the response. Even then, it just gets treated as an
638 // HTTP/0.9 response.
639 TEST_F(WebSocketStreamCreateTest
, MalformedResponse
) {
640 static const char kMalformedResponse
[] =
641 "220 mx.google.com ESMTP\r\n"
642 "HTTP/1.1 101 OK\r\n"
643 "Upgrade: websocket\r\n"
644 "Connection: Upgrade\r\n"
645 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
647 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
648 NoSubProtocols(), LocalhostOrigin(), "",
650 WaitUntilConnectDone();
651 EXPECT_TRUE(has_failed());
652 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
656 // Upgrade header must be present.
657 TEST_F(WebSocketStreamCreateTest
, MissingUpgradeHeader
) {
658 static const char kMissingUpgradeResponse
[] =
659 "HTTP/1.1 101 Switching Protocols\r\n"
660 "Connection: Upgrade\r\n"
661 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
663 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
664 NoSubProtocols(), LocalhostOrigin(), "",
665 kMissingUpgradeResponse
);
666 WaitUntilConnectDone();
667 EXPECT_TRUE(has_failed());
668 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
672 // There must only be one upgrade header.
673 TEST_F(WebSocketStreamCreateTest
, DoubleUpgradeHeader
) {
674 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
675 NoSubProtocols(), LocalhostOrigin(), "",
676 "Upgrade: HTTP/2.0\r\n");
677 WaitUntilConnectDone();
678 EXPECT_TRUE(has_failed());
679 EXPECT_EQ("Error during WebSocket handshake: "
680 "'Upgrade' header must not appear more than once in a response",
684 // There must only be one correct upgrade header.
685 TEST_F(WebSocketStreamCreateTest
, IncorrectUpgradeHeader
) {
686 static const char kMissingUpgradeResponse
[] =
687 "HTTP/1.1 101 Switching Protocols\r\n"
688 "Connection: Upgrade\r\n"
689 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
690 "Upgrade: hogefuga\r\n"
692 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
693 NoSubProtocols(), LocalhostOrigin(), "",
694 kMissingUpgradeResponse
);
695 WaitUntilConnectDone();
696 EXPECT_TRUE(has_failed());
697 EXPECT_EQ("Error during WebSocket handshake: "
698 "'Upgrade' header value is not 'WebSocket': hogefuga",
702 // Connection header must be present.
703 TEST_F(WebSocketStreamCreateTest
, MissingConnectionHeader
) {
704 static const char kMissingConnectionResponse
[] =
705 "HTTP/1.1 101 Switching Protocols\r\n"
706 "Upgrade: websocket\r\n"
707 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
709 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
710 NoSubProtocols(), LocalhostOrigin(), "",
711 kMissingConnectionResponse
);
712 WaitUntilConnectDone();
713 EXPECT_TRUE(has_failed());
714 EXPECT_EQ("Error during WebSocket handshake: "
715 "'Connection' header is missing",
719 // Connection header must contain "Upgrade".
720 TEST_F(WebSocketStreamCreateTest
, IncorrectConnectionHeader
) {
721 static const char kMissingConnectionResponse
[] =
722 "HTTP/1.1 101 Switching Protocols\r\n"
723 "Upgrade: websocket\r\n"
724 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
725 "Connection: hogefuga\r\n"
727 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
728 NoSubProtocols(), LocalhostOrigin(), "",
729 kMissingConnectionResponse
);
730 WaitUntilConnectDone();
731 EXPECT_TRUE(has_failed());
732 EXPECT_EQ("Error during WebSocket handshake: "
733 "'Connection' header value must contain 'Upgrade'",
737 // Connection header is permitted to contain other tokens.
738 TEST_F(WebSocketStreamCreateTest
, AdditionalTokenInConnectionHeader
) {
739 static const char kAdditionalConnectionTokenResponse
[] =
740 "HTTP/1.1 101 Switching Protocols\r\n"
741 "Upgrade: websocket\r\n"
742 "Connection: Upgrade, Keep-Alive\r\n"
743 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
745 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
746 NoSubProtocols(), LocalhostOrigin(), "",
747 kAdditionalConnectionTokenResponse
);
748 WaitUntilConnectDone();
749 EXPECT_FALSE(has_failed());
750 EXPECT_TRUE(stream_
);
753 // Sec-WebSocket-Accept header must be present.
754 TEST_F(WebSocketStreamCreateTest
, MissingSecWebSocketAccept
) {
755 static const char kMissingAcceptResponse
[] =
756 "HTTP/1.1 101 Switching Protocols\r\n"
757 "Upgrade: websocket\r\n"
758 "Connection: Upgrade\r\n"
760 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
761 NoSubProtocols(), LocalhostOrigin(), "",
762 kMissingAcceptResponse
);
763 WaitUntilConnectDone();
764 EXPECT_TRUE(has_failed());
765 EXPECT_EQ("Error during WebSocket handshake: "
766 "'Sec-WebSocket-Accept' header is missing",
770 // Sec-WebSocket-Accept header must match the key that was sent.
771 TEST_F(WebSocketStreamCreateTest
, WrongSecWebSocketAccept
) {
772 static const char kIncorrectAcceptResponse
[] =
773 "HTTP/1.1 101 Switching Protocols\r\n"
774 "Upgrade: websocket\r\n"
775 "Connection: Upgrade\r\n"
776 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
778 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
779 NoSubProtocols(), LocalhostOrigin(), "",
780 kIncorrectAcceptResponse
);
781 WaitUntilConnectDone();
782 EXPECT_TRUE(has_failed());
783 EXPECT_EQ("Error during WebSocket handshake: "
784 "Incorrect 'Sec-WebSocket-Accept' header value",
788 // Cancellation works.
789 TEST_F(WebSocketStreamCreateTest
, Cancellation
) {
790 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
791 NoSubProtocols(), LocalhostOrigin(), "", "");
792 stream_request_
.reset();
793 // WaitUntilConnectDone doesn't work in this case.
794 base::RunLoop().RunUntilIdle();
795 EXPECT_FALSE(has_failed());
796 EXPECT_FALSE(stream_
);
797 EXPECT_FALSE(request_info_
);
798 EXPECT_FALSE(response_info_
);
801 // Connect failure must look just like negotiation failure.
802 TEST_F(WebSocketStreamCreateTest
, ConnectionFailure
) {
803 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
804 socket_data
->set_connect_data(
805 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
806 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
807 LocalhostOrigin(), socket_data
.Pass());
808 WaitUntilConnectDone();
809 EXPECT_TRUE(has_failed());
810 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
812 EXPECT_FALSE(request_info_
);
813 EXPECT_FALSE(response_info_
);
816 // Connect timeout must look just like any other failure.
817 TEST_F(WebSocketStreamCreateTest
, ConnectionTimeout
) {
818 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
819 socket_data
->set_connect_data(
820 MockConnect(ASYNC
, ERR_CONNECTION_TIMED_OUT
));
821 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
822 LocalhostOrigin(), socket_data
.Pass());
823 WaitUntilConnectDone();
824 EXPECT_TRUE(has_failed());
825 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
829 // The server doesn't respond to the opening handshake.
830 TEST_F(WebSocketStreamCreateTest
, HandshakeTimeout
) {
831 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
832 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
833 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
834 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
835 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
836 LocalhostOrigin(), socket_data
.Pass(),
838 EXPECT_FALSE(has_failed());
839 ASSERT_TRUE(weak_timer
.get());
840 EXPECT_TRUE(weak_timer
->IsRunning());
843 WaitUntilConnectDone();
845 EXPECT_TRUE(has_failed());
846 EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
847 ASSERT_TRUE(weak_timer
.get());
848 EXPECT_FALSE(weak_timer
->IsRunning());
851 // When the connection establishes the timer should be stopped.
852 TEST_F(WebSocketStreamCreateTest
, HandshakeTimerOnSuccess
) {
853 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
854 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
856 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
857 NoSubProtocols(), LocalhostOrigin(), "", "",
859 ASSERT_TRUE(weak_timer
);
860 EXPECT_TRUE(weak_timer
->IsRunning());
862 WaitUntilConnectDone();
863 EXPECT_FALSE(has_failed());
864 EXPECT_TRUE(stream_
);
865 ASSERT_TRUE(weak_timer
);
866 EXPECT_FALSE(weak_timer
->IsRunning());
869 // When the connection fails the timer should be stopped.
870 TEST_F(WebSocketStreamCreateTest
, HandshakeTimerOnFailure
) {
871 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
872 socket_data
->set_connect_data(
873 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
874 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
875 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
876 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
877 LocalhostOrigin(), socket_data
.Pass(),
879 ASSERT_TRUE(weak_timer
.get());
880 EXPECT_TRUE(weak_timer
->IsRunning());
882 WaitUntilConnectDone();
883 EXPECT_TRUE(has_failed());
884 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
886 ASSERT_TRUE(weak_timer
.get());
887 EXPECT_FALSE(weak_timer
->IsRunning());
890 // Cancellation during connect works.
891 TEST_F(WebSocketStreamCreateTest
, CancellationDuringConnect
) {
892 scoped_ptr
<SequencedSocketData
> socket_data(BuildNullSocketData());
893 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
894 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
895 LocalhostOrigin(), socket_data
.Pass());
896 stream_request_
.reset();
897 // WaitUntilConnectDone doesn't work in this case.
898 base::RunLoop().RunUntilIdle();
899 EXPECT_FALSE(has_failed());
900 EXPECT_FALSE(stream_
);
903 // Cancellation during write of the request headers works.
904 TEST_F(WebSocketStreamCreateTest
, CancellationDuringWrite
) {
905 // First write never completes.
906 MockWrite writes
[] = {MockWrite(SYNCHRONOUS
, ERR_IO_PENDING
, 0)};
907 SequencedSocketData
* socket_data(
908 new SequencedSocketData(NULL
, 0, writes
, arraysize(writes
)));
909 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
910 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
912 make_scoped_ptr(socket_data
));
913 base::RunLoop().RunUntilIdle();
914 EXPECT_TRUE(socket_data
->AllWriteDataConsumed());
915 stream_request_
.reset();
916 // WaitUntilConnectDone doesn't work in this case.
917 base::RunLoop().RunUntilIdle();
918 EXPECT_FALSE(has_failed());
919 EXPECT_FALSE(stream_
);
920 EXPECT_TRUE(request_info_
);
921 EXPECT_FALSE(response_info_
);
924 // Cancellation during read of the response headers works.
925 TEST_F(WebSocketStreamCreateTest
, CancellationDuringRead
) {
926 std::string request
=
927 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
928 MockWrite writes
[] = {MockWrite(ASYNC
, 0, request
.c_str())};
930 MockRead(SYNCHRONOUS
, ERR_IO_PENDING
, 1),
932 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
933 SequencedSocketData
* socket_data_raw_ptr
= socket_data
.get();
934 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
935 LocalhostOrigin(), socket_data
.Pass());
936 base::RunLoop().RunUntilIdle();
937 EXPECT_TRUE(socket_data_raw_ptr
->AllReadDataConsumed());
938 stream_request_
.reset();
939 // WaitUntilConnectDone doesn't work in this case.
940 base::RunLoop().RunUntilIdle();
941 EXPECT_FALSE(has_failed());
942 EXPECT_FALSE(stream_
);
943 EXPECT_TRUE(request_info_
);
944 EXPECT_FALSE(response_info_
);
947 // Over-size response headers (> 256KB) should not cause a crash. This is a
948 // regression test for crbug.com/339456. It is based on the layout test
949 // "cookie-flood.html".
950 TEST_F(WebSocketStreamCreateTest
, VeryLargeResponseHeaders
) {
951 std::string set_cookie_headers
;
952 set_cookie_headers
.reserve(45 * 10000);
953 for (int i
= 0; i
< 10000; ++i
) {
954 set_cookie_headers
+=
955 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i
);
957 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
958 NoSubProtocols(), LocalhostOrigin(), "",
960 WaitUntilConnectDone();
961 EXPECT_TRUE(has_failed());
962 EXPECT_FALSE(response_info_
);
965 // If the remote host closes the connection without sending headers, we should
966 // log the console message "Connection closed before receiving a handshake
968 TEST_F(WebSocketStreamCreateTest
, NoResponse
) {
969 std::string request
=
970 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
971 MockWrite writes
[] = {MockWrite(ASYNC
, request
.data(), request
.size(), 0)};
972 MockRead reads
[] = {MockRead(ASYNC
, 0, 1)};
973 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
974 SequencedSocketData
* socket_data_raw_ptr
= socket_data
.get();
975 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
976 LocalhostOrigin(), socket_data
.Pass());
977 base::RunLoop().RunUntilIdle();
978 EXPECT_TRUE(socket_data_raw_ptr
->AllReadDataConsumed());
979 EXPECT_TRUE(has_failed());
980 EXPECT_FALSE(stream_
);
981 EXPECT_FALSE(response_info_
);
982 EXPECT_EQ("Connection closed before receiving a handshake response",
986 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateFailure
) {
988 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
990 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
991 ASSERT_TRUE(ssl_data_
[0]->cert
.get());
992 scoped_ptr
<SequencedSocketData
> raw_socket_data(BuildNullSocketData());
993 CreateAndConnectRawExpectations("wss://localhost/", NoSubProtocols(),
994 LocalhostOrigin(), raw_socket_data
.Pass());
995 // WaitUntilConnectDone doesn't work in this case.
996 base::RunLoop().RunUntilIdle();
997 EXPECT_FALSE(has_failed());
998 ASSERT_TRUE(ssl_error_callbacks_
);
999 ssl_error_callbacks_
->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID
,
1001 WaitUntilConnectDone();
1002 EXPECT_TRUE(has_failed());
1005 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateSuccess
) {
1006 scoped_ptr
<SSLSocketDataProvider
> ssl_data(
1007 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1009 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1010 ASSERT_TRUE(ssl_data
->cert
.get());
1011 ssl_data_
.push_back(ssl_data
.Pass());
1012 ssl_data
.reset(new SSLSocketDataProvider(ASYNC
, OK
));
1013 ssl_data_
.push_back(ssl_data
.Pass());
1014 url_request_context_host_
.AddRawExpectations(BuildNullSocketData());
1015 CreateAndConnectStandard("wss://localhost/", "localhost", "/",
1016 NoSubProtocols(), LocalhostOrigin(), "", "");
1017 // WaitUntilConnectDone doesn't work in this case.
1018 base::RunLoop().RunUntilIdle();
1019 ASSERT_TRUE(ssl_error_callbacks_
);
1020 ssl_error_callbacks_
->ContinueSSLRequest();
1021 WaitUntilConnectDone();
1022 EXPECT_FALSE(has_failed());
1023 EXPECT_TRUE(stream_
);
1026 // If the server requests authorisation, but we have no credentials, the
1027 // connection should fail cleanly.
1028 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureNoCredentials
) {
1029 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
1030 NoSubProtocols(), LocalhostOrigin(), "",
1031 kUnauthorizedResponse
);
1032 WaitUntilConnectDone();
1033 EXPECT_TRUE(has_failed());
1034 EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1036 EXPECT_TRUE(response_info_
);
1039 TEST_F(WebSocketStreamCreateBasicAuthTest
, SuccessPasswordInUrl
) {
1040 CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
1042 WebSocketStandardResponse(std::string()));
1043 WaitUntilConnectDone();
1044 EXPECT_FALSE(has_failed());
1045 EXPECT_TRUE(stream_
);
1046 ASSERT_TRUE(response_info_
);
1047 EXPECT_EQ(101, response_info_
->status_code
);
1050 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureIncorrectPasswordInUrl
) {
1051 CreateAndConnectAuthHandshake(
1052 "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse
);
1053 WaitUntilConnectDone();
1054 EXPECT_TRUE(has_failed());
1055 EXPECT_TRUE(response_info_
);
1058 // Digest auth has the same connection semantics as Basic auth, so we can
1059 // generally assume that whatever works for Basic auth will also work for
1060 // Digest. There's just one test here, to confirm that it works at all.
1061 TEST_F(WebSocketStreamCreateDigestAuthTest
, DigestPasswordInUrl
) {
1062 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
1064 CreateAndConnectRawExpectations(
1065 "ws://FooBar:pass@localhost/", NoSubProtocols(), LocalhostOrigin(),
1066 helper_
.BuildSocketData2(kAuthorizedRequest
,
1067 WebSocketStandardResponse(std::string())));
1068 WaitUntilConnectDone();
1069 EXPECT_FALSE(has_failed());
1070 EXPECT_TRUE(stream_
);
1071 ASSERT_TRUE(response_info_
);
1072 EXPECT_EQ(101, response_info_
->status_code
);
1075 TEST_F(WebSocketStreamCreateUMATest
, Incomplete
) {
1076 const std::string
name("Net.WebSocket.HandshakeResult");
1077 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1080 StreamCreation creation
;
1081 creation
.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1082 creation
.NoSubProtocols(),
1083 LocalhostOrigin(), "", "");
1086 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1087 ASSERT_TRUE(samples
);
1089 samples
->Subtract(*original
); // Cancel the original values.
1091 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1092 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1093 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1096 TEST_F(WebSocketStreamCreateUMATest
, Connected
) {
1097 const std::string
name("Net.WebSocket.HandshakeResult");
1098 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1101 StreamCreation creation
;
1102 creation
.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1103 creation
.NoSubProtocols(),
1104 LocalhostOrigin(), "", "");
1105 creation
.WaitUntilConnectDone();
1108 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1109 ASSERT_TRUE(samples
);
1111 samples
->Subtract(*original
); // Cancel the original values.
1113 EXPECT_EQ(0, samples
->GetCount(INCOMPLETE
));
1114 EXPECT_EQ(1, samples
->GetCount(CONNECTED
));
1115 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1118 TEST_F(WebSocketStreamCreateUMATest
, Failed
) {
1119 const std::string
name("Net.WebSocket.HandshakeResult");
1120 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1123 StreamCreation creation
;
1124 static const char kInvalidStatusCodeResponse
[] =
1125 "HTTP/1.1 200 OK\r\n"
1126 "Upgrade: websocket\r\n"
1127 "Connection: Upgrade\r\n"
1128 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1130 creation
.CreateAndConnectCustomResponse(
1131 "ws://localhost/", "localhost", "/", creation
.NoSubProtocols(),
1132 LocalhostOrigin(), "", kInvalidStatusCodeResponse
);
1133 creation
.WaitUntilConnectDone();
1136 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1137 ASSERT_TRUE(samples
);
1139 samples
->Subtract(*original
); // Cancel the original values.
1141 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1142 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1143 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1146 TEST_F(WebSocketStreamCreateTest
, HandleErrConnectionClosed
) {
1147 static const char kTruncatedResponse
[] =
1148 "HTTP/1.1 101 Switching Protocols\r\n"
1149 "Upgrade: websocket\r\n"
1150 "Connection: Upgrade\r\n"
1151 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1152 "Cache-Control: no-sto";
1154 std::string request
=
1155 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
1156 MockRead reads
[] = {
1157 MockRead(SYNCHRONOUS
, 1, kTruncatedResponse
),
1158 MockRead(SYNCHRONOUS
, ERR_CONNECTION_CLOSED
, 2),
1160 MockWrite writes
[] = {MockWrite(SYNCHRONOUS
, 0, request
.c_str())};
1161 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
1162 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
1163 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1164 LocalhostOrigin(), socket_data
.Pass());
1165 WaitUntilConnectDone();
1166 EXPECT_TRUE(has_failed());
1169 TEST_F(WebSocketStreamCreateTest
, HandleErrTunnelConnectionFailed
) {
1170 static const char kConnectRequest
[] =
1171 "CONNECT localhost:80 HTTP/1.1\r\n"
1172 "Host: localhost:80\r\n"
1173 "Proxy-Connection: keep-alive\r\n"
1176 static const char kProxyResponse
[] =
1177 "HTTP/1.1 403 Forbidden\r\n"
1178 "Content-Type: text/html\r\n"
1179 "Content-Length: 9\r\n"
1180 "Connection: keep-alive\r\n"
1184 MockRead reads
[] = {MockRead(SYNCHRONOUS
, 1, kProxyResponse
)};
1185 MockWrite writes
[] = {MockWrite(SYNCHRONOUS
, 0, kConnectRequest
)};
1186 scoped_ptr
<SequencedSocketData
> socket_data(BuildSocketData(reads
, writes
));
1187 url_request_context_host_
.SetProxyConfig("https=proxy:8000");
1188 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1189 LocalhostOrigin(), socket_data
.Pass());
1190 WaitUntilConnectDone();
1191 EXPECT_TRUE(has_failed());
1192 EXPECT_EQ("Establishing a tunnel via proxy server failed.",