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/socket/client_socket_handle.h"
26 #include "net/socket/socket_test_util.h"
27 #include "net/test/cert_test_util.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "net/websockets/websocket_basic_handshake_stream.h"
30 #include "net/websockets/websocket_frame.h"
31 #include "net/websockets/websocket_handshake_request_info.h"
32 #include "net/websockets/websocket_handshake_response_info.h"
33 #include "net/websockets/websocket_handshake_stream_create_helper.h"
34 #include "net/websockets/websocket_test_util.h"
35 #include "testing/gtest/include/gtest/gtest.h"
37 #include "url/origin.h"
42 typedef std::pair
<std::string
, std::string
> HeaderKeyValuePair
;
44 std::vector
<HeaderKeyValuePair
> ToVector(const HttpRequestHeaders
& headers
) {
45 HttpRequestHeaders::Iterator
it(headers
);
46 std::vector
<HeaderKeyValuePair
> result
;
48 result
.push_back(HeaderKeyValuePair(it
.name(), it
.value()));
52 std::vector
<HeaderKeyValuePair
> ToVector(const HttpResponseHeaders
& headers
) {
54 std::string name
, value
;
55 std::vector
<HeaderKeyValuePair
> result
;
56 while (headers
.EnumerateHeaderLines(&iter
, &name
, &value
))
57 result
.push_back(HeaderKeyValuePair(name
, value
));
61 // Simple builder for a DeterministicSocketData object to save repetitive code.
62 // It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
63 // be used in tests where the connect fails. In practice, those tests never have
64 // any read/write data and so can't benefit from it anyway. The arrays are not
65 // copied. It is up to the caller to ensure they stay in scope until the test
67 template <size_t reads_count
, size_t writes_count
>
68 scoped_ptr
<DeterministicSocketData
> BuildSocketData(
69 MockRead (&reads
)[reads_count
],
70 MockWrite (&writes
)[writes_count
]) {
71 scoped_ptr
<DeterministicSocketData
> socket_data(
72 new DeterministicSocketData(reads
, reads_count
, writes
, writes_count
));
73 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
74 socket_data
->SetStop(reads_count
+ writes_count
);
75 return socket_data
.Pass();
78 // Builder for a DeterministicSocketData that expects nothing. This does not
79 // set the connect data, so the calling code must do that explicitly.
80 scoped_ptr
<DeterministicSocketData
> BuildNullSocketData() {
81 return make_scoped_ptr(new DeterministicSocketData(NULL
, 0, NULL
, 0));
84 class MockWeakTimer
: public base::MockTimer
,
85 public base::SupportsWeakPtr
<MockWeakTimer
> {
87 MockWeakTimer(bool retain_user_task
, bool is_repeating
)
88 : MockTimer(retain_user_task
, is_repeating
) {}
91 // A sub-class of WebSocketHandshakeStreamCreateHelper which always sets a
92 // deterministic key to use in the WebSocket handshake.
93 class DeterministicKeyWebSocketHandshakeStreamCreateHelper
94 : public WebSocketHandshakeStreamCreateHelper
{
96 DeterministicKeyWebSocketHandshakeStreamCreateHelper(
97 WebSocketStream::ConnectDelegate
* connect_delegate
,
98 const std::vector
<std::string
>& requested_subprotocols
)
99 : WebSocketHandshakeStreamCreateHelper(connect_delegate
,
100 requested_subprotocols
) {}
102 virtual void OnStreamCreated(WebSocketBasicHandshakeStream
* stream
) override
{
103 stream
->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
107 class WebSocketStreamCreateTest
: public ::testing::Test
{
109 WebSocketStreamCreateTest() : has_failed_(false), ssl_fatal_(false) {}
111 void CreateAndConnectCustomResponse(
112 const std::string
& socket_url
,
113 const std::string
& socket_path
,
114 const std::vector
<std::string
>& sub_protocols
,
115 const std::string
& origin
,
116 const std::string
& extra_request_headers
,
117 const std::string
& response_body
,
118 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
119 url_request_context_host_
.SetExpectations(
120 WebSocketStandardRequest(socket_path
, origin
, extra_request_headers
),
122 CreateAndConnectStream(socket_url
, sub_protocols
, origin
, timer
.Pass());
125 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
126 // errors like "Unable to perform synchronous IO while stopped" will occur.
127 void CreateAndConnectStandard(const std::string
& socket_url
,
128 const std::string
& socket_path
,
129 const std::vector
<std::string
>& sub_protocols
,
130 const std::string
& origin
,
131 const std::string
& extra_request_headers
,
132 const std::string
& extra_response_headers
,
133 scoped_ptr
<base::Timer
> timer
=
134 scoped_ptr
<base::Timer
>()) {
135 CreateAndConnectCustomResponse(
140 extra_request_headers
,
141 WebSocketStandardResponse(extra_response_headers
),
145 void CreateAndConnectRawExpectations(
146 const std::string
& socket_url
,
147 const std::vector
<std::string
>& sub_protocols
,
148 const std::string
& origin
,
149 scoped_ptr
<DeterministicSocketData
> socket_data
,
150 scoped_ptr
<base::Timer
> timer
= scoped_ptr
<base::Timer
>()) {
151 AddRawExpectations(socket_data
.Pass());
152 CreateAndConnectStream(socket_url
, sub_protocols
, origin
, timer
.Pass());
155 // Add additional raw expectations for sockets created before the final one.
156 void AddRawExpectations(scoped_ptr
<DeterministicSocketData
> socket_data
) {
157 url_request_context_host_
.AddRawExpectations(socket_data
.Pass());
160 // A wrapper for CreateAndConnectStreamForTesting that knows about our default
162 void CreateAndConnectStream(const std::string
& socket_url
,
163 const std::vector
<std::string
>& sub_protocols
,
164 const std::string
& origin
,
165 scoped_ptr
<base::Timer
> timer
) {
166 for (size_t i
= 0; i
< ssl_data_
.size(); ++i
) {
167 scoped_ptr
<SSLSocketDataProvider
> ssl_data(ssl_data_
[i
]);
169 url_request_context_host_
.AddSSLSocketDataProvider(ssl_data
.Pass());
172 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate(
173 new TestConnectDelegate(this));
174 WebSocketStream::ConnectDelegate
* delegate
= connect_delegate
.get();
175 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper(
176 new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
177 delegate
, sub_protocols
));
178 stream_request_
= ::net::CreateAndConnectStreamForTesting(
180 create_helper
.Pass(),
182 url_request_context_host_
.GetURLRequestContext(),
184 connect_delegate
.Pass(),
185 timer
? timer
.Pass() : scoped_ptr
<base::Timer
>(
186 new base::Timer(false, false)));
189 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
191 // A simple function to make the tests more readable. Creates an empty vector.
192 static std::vector
<std::string
> NoSubProtocols() {
193 return std::vector
<std::string
>();
196 const std::string
& failure_message() const { return failure_message_
; }
197 bool has_failed() const { return has_failed_
; }
199 class TestConnectDelegate
: public WebSocketStream::ConnectDelegate
{
201 explicit TestConnectDelegate(WebSocketStreamCreateTest
* owner
)
204 virtual void OnSuccess(scoped_ptr
<WebSocketStream
> stream
) override
{
205 stream
.swap(owner_
->stream_
);
208 virtual void OnFailure(const std::string
& message
) override
{
209 owner_
->has_failed_
= true;
210 owner_
->failure_message_
= message
;
213 virtual void OnStartOpeningHandshake(
214 scoped_ptr
<WebSocketHandshakeRequestInfo
> request
) override
{
215 // Can be called multiple times (in the case of HTTP auth). Last call
217 owner_
->request_info_
= request
.Pass();
219 virtual void OnFinishOpeningHandshake(
220 scoped_ptr
<WebSocketHandshakeResponseInfo
> response
) override
{
221 if (owner_
->response_info_
)
223 owner_
->response_info_
= response
.Pass();
225 virtual void OnSSLCertificateError(
226 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
>
228 const SSLInfo
& ssl_info
,
229 bool fatal
) override
{
230 owner_
->ssl_error_callbacks_
= ssl_error_callbacks
.Pass();
231 owner_
->ssl_info_
= ssl_info
;
232 owner_
->ssl_fatal_
= fatal
;
236 WebSocketStreamCreateTest
* owner_
;
239 WebSocketTestURLRequestContextHost url_request_context_host_
;
240 scoped_ptr
<WebSocketStreamRequest
> stream_request_
;
241 // Only set if the connection succeeded.
242 scoped_ptr
<WebSocketStream
> stream_
;
243 // Only set if the connection failed.
244 std::string failure_message_
;
246 scoped_ptr
<WebSocketHandshakeRequestInfo
> request_info_
;
247 scoped_ptr
<WebSocketHandshakeResponseInfo
> response_info_
;
248 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
> ssl_error_callbacks_
;
251 ScopedVector
<SSLSocketDataProvider
> ssl_data_
;
254 // There are enough tests of the Sec-WebSocket-Extensions header that they
255 // deserve their own test fixture.
256 class WebSocketStreamCreateExtensionTest
: public WebSocketStreamCreateTest
{
258 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
259 // header in the response set to |extensions_header_value|. Runs the event
260 // loop to allow the connect to complete.
261 void CreateAndConnectWithExtensions(
262 const std::string
& extensions_header_value
) {
263 CreateAndConnectStandard(
264 "ws://localhost/testing_path",
269 "Sec-WebSocket-Extensions: " + extensions_header_value
+ "\r\n");
274 // Common code to construct expectations for authentication tests that receive
275 // the auth challenge on one connection and then create a second connection to
276 // send the authenticated request on.
277 class CommonAuthTestHelper
{
279 CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
281 scoped_ptr
<DeterministicSocketData
> BuildSocketData1(
282 const std::string
& response
) {
283 request1_
= WebSocketStandardRequest("/", "http://localhost", "");
284 writes1_
[0] = MockWrite(SYNCHRONOUS
, 0, request1_
.c_str());
285 response1_
= response
;
286 reads1_
[0] = MockRead(SYNCHRONOUS
, 1, response1_
.c_str());
287 reads1_
[1] = MockRead(SYNCHRONOUS
, OK
, 2); // Close connection
289 return BuildSocketData(reads1_
, writes1_
);
292 scoped_ptr
<DeterministicSocketData
> BuildSocketData2(
293 const std::string
& request
,
294 const std::string
& response
) {
296 response2_
= response
;
297 writes2_
[0] = MockWrite(SYNCHRONOUS
, 0, request2_
.c_str());
298 reads2_
[0] = MockRead(SYNCHRONOUS
, 1, response2_
.c_str());
299 return BuildSocketData(reads2_
, writes2_
);
303 // These need to be object-scoped since they have to remain valid until all
304 // socket operations in the test are complete.
305 std::string request1_
;
306 std::string request2_
;
307 std::string response1_
;
308 std::string response2_
;
310 MockWrite writes1_
[1];
312 MockWrite writes2_
[1];
314 DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper
);
317 // Data and methods for BasicAuth tests.
318 class WebSocketStreamCreateBasicAuthTest
: public WebSocketStreamCreateTest
{
320 void CreateAndConnectAuthHandshake(const std::string
& url
,
321 const std::string
& base64_user_pass
,
322 const std::string
& response2
) {
323 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
325 static const char request2format
[] =
327 "Host: localhost\r\n"
328 "Connection: Upgrade\r\n"
329 "Pragma: no-cache\r\n"
330 "Cache-Control: no-cache\r\n"
331 "Authorization: Basic %s\r\n"
332 "Upgrade: websocket\r\n"
333 "Origin: http://localhost\r\n"
334 "Sec-WebSocket-Version: 13\r\n"
336 "Accept-Encoding: gzip, deflate\r\n"
337 "Accept-Language: en-us,fr\r\n"
338 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
339 "Sec-WebSocket-Extensions: permessage-deflate; "
340 "client_max_window_bits\r\n"
342 const std::string request
=
343 base::StringPrintf(request2format
, base64_user_pass
.c_str());
344 CreateAndConnectRawExpectations(
348 helper_
.BuildSocketData2(request
, response2
));
351 static const char kUnauthorizedResponse
[];
353 CommonAuthTestHelper helper_
;
356 class WebSocketStreamCreateDigestAuthTest
: public WebSocketStreamCreateTest
{
358 static const char kUnauthorizedResponse
[];
359 static const char kAuthorizedRequest
[];
361 CommonAuthTestHelper helper_
;
364 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse
[] =
365 "HTTP/1.1 401 Unauthorized\r\n"
366 "Content-Length: 0\r\n"
367 "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
370 // These negotiation values are borrowed from
371 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
372 // you are bored. Only the weakest (no qop) variants of Digest authentication
373 // can be tested by this method, because the others involve random input.
374 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse
[] =
375 "HTTP/1.1 401 Unauthorized\r\n"
376 "Content-Length: 0\r\n"
377 "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
380 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest
[] =
382 "Host: localhost\r\n"
383 "Connection: Upgrade\r\n"
384 "Pragma: no-cache\r\n"
385 "Cache-Control: no-cache\r\n"
386 "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
387 "nonce=\"nonce-value\", uri=\"/\", "
388 "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
389 "Upgrade: websocket\r\n"
390 "Origin: http://localhost\r\n"
391 "Sec-WebSocket-Version: 13\r\n"
393 "Accept-Encoding: gzip, deflate\r\n"
394 "Accept-Language: en-us,fr\r\n"
395 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
396 "Sec-WebSocket-Extensions: permessage-deflate; "
397 "client_max_window_bits\r\n"
400 class WebSocketStreamCreateUMATest
: public ::testing::Test
{
402 // This enum should match with the enum in Delegate in websocket_stream.cc.
403 enum HandshakeResult
{
407 NUM_HANDSHAKE_RESULT_TYPES
,
410 class StreamCreation
: public WebSocketStreamCreateTest
{
411 virtual void TestBody() override
{}
414 scoped_ptr
<base::HistogramSamples
> GetSamples(const std::string
& name
) {
415 base::HistogramBase
* histogram
=
416 base::StatisticsRecorder::FindHistogram(name
);
417 return histogram
? histogram
->SnapshotSamples()
418 : scoped_ptr
<base::HistogramSamples
>();
422 // Confirm that the basic case works as expected.
423 TEST_F(WebSocketStreamCreateTest
, SimpleSuccess
) {
424 CreateAndConnectStandard(
425 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
426 EXPECT_FALSE(request_info_
);
427 EXPECT_FALSE(response_info_
);
429 EXPECT_FALSE(has_failed());
430 EXPECT_TRUE(stream_
);
431 EXPECT_TRUE(request_info_
);
432 EXPECT_TRUE(response_info_
);
435 TEST_F(WebSocketStreamCreateTest
, HandshakeInfo
) {
436 static const char kResponse
[] =
437 "HTTP/1.1 101 Switching Protocols\r\n"
438 "Upgrade: websocket\r\n"
439 "Connection: Upgrade\r\n"
440 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
446 CreateAndConnectCustomResponse(
453 EXPECT_FALSE(request_info_
);
454 EXPECT_FALSE(response_info_
);
456 EXPECT_TRUE(stream_
);
457 ASSERT_TRUE(request_info_
);
458 ASSERT_TRUE(response_info_
);
459 std::vector
<HeaderKeyValuePair
> request_headers
=
460 ToVector(request_info_
->headers
);
461 // We examine the contents of request_info_ and response_info_
462 // mainly only in this test case.
463 EXPECT_EQ(GURL("ws://localhost/"), request_info_
->url
);
464 EXPECT_EQ(GURL("ws://localhost/"), response_info_
->url
);
465 EXPECT_EQ(101, response_info_
->status_code
);
466 EXPECT_EQ("Switching Protocols", response_info_
->status_text
);
467 ASSERT_EQ(12u, request_headers
.size());
468 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers
[0]);
469 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers
[1]);
470 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers
[2]);
471 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
473 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers
[4]);
474 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
476 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
478 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers
[7]);
479 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
481 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
483 EXPECT_EQ("Sec-WebSocket-Key", request_headers
[10].first
);
484 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
485 "permessage-deflate; client_max_window_bits"),
486 request_headers
[11]);
488 std::vector
<HeaderKeyValuePair
> response_headers
=
489 ToVector(*response_info_
->headers
.get());
490 ASSERT_EQ(6u, response_headers
.size());
491 // Sort the headers for ease of verification.
492 std::sort(response_headers
.begin(), response_headers
.end());
494 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers
[0]);
495 EXPECT_EQ("Sec-WebSocket-Accept", response_headers
[1].first
);
496 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers
[2]);
497 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers
[3]);
498 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers
[4]);
499 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers
[5]);
502 // Confirm that the stream isn't established until the message loop runs.
503 TEST_F(WebSocketStreamCreateTest
, NeedsToRunLoop
) {
504 CreateAndConnectStandard(
505 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
506 EXPECT_FALSE(has_failed());
507 EXPECT_FALSE(stream_
);
510 // Check the path is used.
511 TEST_F(WebSocketStreamCreateTest
, PathIsUsed
) {
512 CreateAndConnectStandard("ws://localhost/testing_path",
519 EXPECT_FALSE(has_failed());
520 EXPECT_TRUE(stream_
);
523 // Check that the origin is used.
524 TEST_F(WebSocketStreamCreateTest
, OriginIsUsed
) {
525 CreateAndConnectStandard("ws://localhost/testing_path",
532 EXPECT_FALSE(has_failed());
533 EXPECT_TRUE(stream_
);
536 // Check that sub-protocols are sent and parsed.
537 TEST_F(WebSocketStreamCreateTest
, SubProtocolIsUsed
) {
538 std::vector
<std::string
> sub_protocols
;
539 sub_protocols
.push_back("chatv11.chromium.org");
540 sub_protocols
.push_back("chatv20.chromium.org");
541 CreateAndConnectStandard("ws://localhost/testing_path",
545 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
546 "chatv20.chromium.org\r\n",
547 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
549 EXPECT_TRUE(stream_
);
550 EXPECT_FALSE(has_failed());
551 EXPECT_EQ("chatv20.chromium.org", stream_
->GetSubProtocol());
554 // Unsolicited sub-protocols are rejected.
555 TEST_F(WebSocketStreamCreateTest
, UnsolicitedSubProtocol
) {
556 CreateAndConnectStandard("ws://localhost/testing_path",
561 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
563 EXPECT_FALSE(stream_
);
564 EXPECT_TRUE(has_failed());
565 EXPECT_EQ("Error during WebSocket handshake: "
566 "Response must not include 'Sec-WebSocket-Protocol' header "
567 "if not present in request: chatv20.chromium.org",
571 // Missing sub-protocol response is rejected.
572 TEST_F(WebSocketStreamCreateTest
, UnacceptedSubProtocol
) {
573 std::vector
<std::string
> sub_protocols
;
574 sub_protocols
.push_back("chat.example.com");
575 CreateAndConnectStandard("ws://localhost/testing_path",
579 "Sec-WebSocket-Protocol: chat.example.com\r\n",
582 EXPECT_FALSE(stream_
);
583 EXPECT_TRUE(has_failed());
584 EXPECT_EQ("Error during WebSocket handshake: "
585 "Sent non-empty 'Sec-WebSocket-Protocol' header "
586 "but no response was received",
590 // Only one sub-protocol can be accepted.
591 TEST_F(WebSocketStreamCreateTest
, MultipleSubProtocolsInResponse
) {
592 std::vector
<std::string
> sub_protocols
;
593 sub_protocols
.push_back("chatv11.chromium.org");
594 sub_protocols
.push_back("chatv20.chromium.org");
595 CreateAndConnectStandard("ws://localhost/testing_path",
599 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
600 "chatv20.chromium.org\r\n",
601 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
602 "chatv20.chromium.org\r\n");
604 EXPECT_FALSE(stream_
);
605 EXPECT_TRUE(has_failed());
606 EXPECT_EQ("Error during WebSocket handshake: "
607 "'Sec-WebSocket-Protocol' header must not appear "
608 "more than once in a response",
612 // Unmatched sub-protocol should be rejected.
613 TEST_F(WebSocketStreamCreateTest
, UnmatchedSubProtocolInResponse
) {
614 std::vector
<std::string
> sub_protocols
;
615 sub_protocols
.push_back("chatv11.chromium.org");
616 sub_protocols
.push_back("chatv20.chromium.org");
617 CreateAndConnectStandard("ws://localhost/testing_path",
621 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
622 "chatv20.chromium.org\r\n",
623 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
625 EXPECT_FALSE(stream_
);
626 EXPECT_TRUE(has_failed());
627 EXPECT_EQ("Error during WebSocket handshake: "
628 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
629 "in response does not match any of sent values",
633 // permessage-deflate extension basic success case.
634 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateSuccess
) {
635 CreateAndConnectWithExtensions("permessage-deflate");
636 EXPECT_TRUE(stream_
);
637 EXPECT_FALSE(has_failed());
640 // permessage-deflate extensions success with all parameters.
641 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateParamsSuccess
) {
642 CreateAndConnectWithExtensions(
643 "permessage-deflate; client_no_context_takeover; "
644 "server_max_window_bits=11; client_max_window_bits=13; "
645 "server_no_context_takeover");
646 EXPECT_TRUE(stream_
);
647 EXPECT_FALSE(has_failed());
650 // Verify that incoming messages are actually decompressed with
651 // permessage-deflate enabled.
652 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateInflates
) {
653 CreateAndConnectCustomResponse(
654 "ws://localhost/testing_path",
659 WebSocketStandardResponse(
660 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
662 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
663 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
667 ASSERT_TRUE(stream_
);
668 ScopedVector
<WebSocketFrame
> frames
;
669 CompletionCallback callback
;
670 ASSERT_EQ(OK
, stream_
->ReadFrames(&frames
, callback
));
671 ASSERT_EQ(1U, frames
.size());
672 ASSERT_EQ(5U, frames
[0]->header
.payload_length
);
673 EXPECT_EQ("Hello", std::string(frames
[0]->data
->data(), 5));
676 // Unknown extension in the response is rejected
677 TEST_F(WebSocketStreamCreateExtensionTest
, UnknownExtension
) {
678 CreateAndConnectWithExtensions("x-unknown-extension");
679 EXPECT_FALSE(stream_
);
680 EXPECT_TRUE(has_failed());
681 EXPECT_EQ("Error during WebSocket handshake: "
682 "Found an unsupported extension 'x-unknown-extension' "
683 "in 'Sec-WebSocket-Extensions' header",
687 // Malformed extensions are rejected (this file does not cover all possible
688 // parse failures, as the parser is covered thoroughly by its own unit tests).
689 TEST_F(WebSocketStreamCreateExtensionTest
, MalformedExtension
) {
690 CreateAndConnectWithExtensions(";");
691 EXPECT_FALSE(stream_
);
692 EXPECT_TRUE(has_failed());
694 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
695 "value is rejected by the parser: ;",
699 // The permessage-deflate extension may only be specified once.
700 TEST_F(WebSocketStreamCreateExtensionTest
, OnlyOnePerMessageDeflateAllowed
) {
701 CreateAndConnectWithExtensions(
702 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
703 EXPECT_FALSE(stream_
);
704 EXPECT_TRUE(has_failed());
706 "Error during WebSocket handshake: "
707 "Received duplicate permessage-deflate response",
711 // permessage-deflate parameters may not be duplicated.
712 TEST_F(WebSocketStreamCreateExtensionTest
, NoDuplicateParameters
) {
713 CreateAndConnectWithExtensions(
714 "permessage-deflate; client_no_context_takeover; "
715 "client_no_context_takeover");
716 EXPECT_FALSE(stream_
);
717 EXPECT_TRUE(has_failed());
719 "Error during WebSocket handshake: Error in permessage-deflate: "
720 "Received duplicate permessage-deflate extension parameter "
721 "client_no_context_takeover",
725 // permessage-deflate parameters must start with "client_" or "server_"
726 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterPrefix
) {
727 CreateAndConnectWithExtensions(
728 "permessage-deflate; absurd_no_context_takeover");
729 EXPECT_FALSE(stream_
);
730 EXPECT_TRUE(has_failed());
732 "Error during WebSocket handshake: Error in permessage-deflate: "
733 "Received an unexpected permessage-deflate extension parameter",
737 // permessage-deflate parameters must be either *_no_context_takeover or
739 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterSuffix
) {
740 CreateAndConnectWithExtensions(
741 "permessage-deflate; client_max_content_bits=5");
742 EXPECT_FALSE(stream_
);
743 EXPECT_TRUE(has_failed());
745 "Error during WebSocket handshake: Error in permessage-deflate: "
746 "Received an unexpected permessage-deflate extension parameter",
750 // *_no_context_takeover parameters must not have an argument
751 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterValue
) {
752 CreateAndConnectWithExtensions(
753 "permessage-deflate; client_no_context_takeover=true");
754 EXPECT_FALSE(stream_
);
755 EXPECT_TRUE(has_failed());
757 "Error during WebSocket handshake: Error in permessage-deflate: "
758 "Received invalid client_no_context_takeover parameter",
762 // *_max_window_bits must have an argument
763 TEST_F(WebSocketStreamCreateExtensionTest
, NoMaxWindowBitsArgument
) {
764 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
765 EXPECT_FALSE(stream_
);
766 EXPECT_TRUE(has_failed());
768 "Error during WebSocket handshake: Error in permessage-deflate: "
769 "client_max_window_bits must have value",
773 // *_max_window_bits must be an integer
774 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueInteger
) {
775 CreateAndConnectWithExtensions(
776 "permessage-deflate; server_max_window_bits=banana");
777 EXPECT_FALSE(stream_
);
778 EXPECT_TRUE(has_failed());
780 "Error during WebSocket handshake: Error in permessage-deflate: "
781 "Received invalid server_max_window_bits parameter",
785 // *_max_window_bits must be >= 8
786 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueTooSmall
) {
787 CreateAndConnectWithExtensions(
788 "permessage-deflate; server_max_window_bits=7");
789 EXPECT_FALSE(stream_
);
790 EXPECT_TRUE(has_failed());
792 "Error during WebSocket handshake: Error in permessage-deflate: "
793 "Received invalid server_max_window_bits parameter",
797 // *_max_window_bits must be <= 15
798 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueTooBig
) {
799 CreateAndConnectWithExtensions(
800 "permessage-deflate; client_max_window_bits=16");
801 EXPECT_FALSE(stream_
);
802 EXPECT_TRUE(has_failed());
804 "Error during WebSocket handshake: Error in permessage-deflate: "
805 "Received invalid client_max_window_bits parameter",
809 // *_max_window_bits must not start with 0
810 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueStartsWithZero
) {
811 CreateAndConnectWithExtensions(
812 "permessage-deflate; client_max_window_bits=08");
813 EXPECT_FALSE(stream_
);
814 EXPECT_TRUE(has_failed());
816 "Error during WebSocket handshake: Error in permessage-deflate: "
817 "Received invalid client_max_window_bits parameter",
821 // *_max_window_bits must not start with +
822 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueStartsWithPlus
) {
823 CreateAndConnectWithExtensions(
824 "permessage-deflate; server_max_window_bits=+9");
825 EXPECT_FALSE(stream_
);
826 EXPECT_TRUE(has_failed());
828 "Error during WebSocket handshake: Error in permessage-deflate: "
829 "Received invalid server_max_window_bits parameter",
833 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
834 // arguments from the server. This is difficult because the data written to the
835 // socket is randomly masked.
837 // Additional Sec-WebSocket-Accept headers should be rejected.
838 TEST_F(WebSocketStreamCreateTest
, DoubleAccept
) {
839 CreateAndConnectStandard(
845 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
847 EXPECT_FALSE(stream_
);
848 EXPECT_TRUE(has_failed());
849 EXPECT_EQ("Error during WebSocket handshake: "
850 "'Sec-WebSocket-Accept' header must not appear "
851 "more than once in a response",
855 // Response code 200 must be rejected.
856 TEST_F(WebSocketStreamCreateTest
, InvalidStatusCode
) {
857 static const char kInvalidStatusCodeResponse
[] =
858 "HTTP/1.1 200 OK\r\n"
859 "Upgrade: websocket\r\n"
860 "Connection: Upgrade\r\n"
861 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
863 CreateAndConnectCustomResponse("ws://localhost/",
868 kInvalidStatusCodeResponse
);
870 EXPECT_TRUE(has_failed());
871 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
875 // Redirects are not followed (according to the WHATWG WebSocket API, which
876 // overrides RFC6455 for browser applications).
877 TEST_F(WebSocketStreamCreateTest
, RedirectsRejected
) {
878 static const char kRedirectResponse
[] =
879 "HTTP/1.1 302 Moved Temporarily\r\n"
880 "Content-Type: text/html\r\n"
881 "Content-Length: 34\r\n"
882 "Connection: keep-alive\r\n"
883 "Location: ws://localhost/other\r\n"
885 "<title>Moved</title><h1>Moved</h1>";
886 CreateAndConnectCustomResponse("ws://localhost/",
893 EXPECT_TRUE(has_failed());
894 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
898 // Malformed responses should be rejected. HttpStreamParser will accept just
899 // about any garbage in the middle of the headers. To make it give up, the junk
900 // has to be at the start of the response. Even then, it just gets treated as an
901 // HTTP/0.9 response.
902 TEST_F(WebSocketStreamCreateTest
, MalformedResponse
) {
903 static const char kMalformedResponse
[] =
904 "220 mx.google.com ESMTP\r\n"
905 "HTTP/1.1 101 OK\r\n"
906 "Upgrade: websocket\r\n"
907 "Connection: Upgrade\r\n"
908 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
910 CreateAndConnectCustomResponse("ws://localhost/",
917 EXPECT_TRUE(has_failed());
918 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
922 // Upgrade header must be present.
923 TEST_F(WebSocketStreamCreateTest
, MissingUpgradeHeader
) {
924 static const char kMissingUpgradeResponse
[] =
925 "HTTP/1.1 101 Switching Protocols\r\n"
926 "Connection: Upgrade\r\n"
927 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
929 CreateAndConnectCustomResponse("ws://localhost/",
934 kMissingUpgradeResponse
);
936 EXPECT_TRUE(has_failed());
937 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
941 // There must only be one upgrade header.
942 TEST_F(WebSocketStreamCreateTest
, DoubleUpgradeHeader
) {
943 CreateAndConnectStandard(
948 "", "Upgrade: HTTP/2.0\r\n");
950 EXPECT_TRUE(has_failed());
951 EXPECT_EQ("Error during WebSocket handshake: "
952 "'Upgrade' header must not appear more than once in a response",
956 // There must only be one correct upgrade header.
957 TEST_F(WebSocketStreamCreateTest
, IncorrectUpgradeHeader
) {
958 static const char kMissingUpgradeResponse
[] =
959 "HTTP/1.1 101 Switching Protocols\r\n"
960 "Connection: Upgrade\r\n"
961 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
962 "Upgrade: hogefuga\r\n"
964 CreateAndConnectCustomResponse("ws://localhost/",
969 kMissingUpgradeResponse
);
971 EXPECT_TRUE(has_failed());
972 EXPECT_EQ("Error during WebSocket handshake: "
973 "'Upgrade' header value is not 'WebSocket': hogefuga",
977 // Connection header must be present.
978 TEST_F(WebSocketStreamCreateTest
, MissingConnectionHeader
) {
979 static const char kMissingConnectionResponse
[] =
980 "HTTP/1.1 101 Switching Protocols\r\n"
981 "Upgrade: websocket\r\n"
982 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
984 CreateAndConnectCustomResponse("ws://localhost/",
989 kMissingConnectionResponse
);
991 EXPECT_TRUE(has_failed());
992 EXPECT_EQ("Error during WebSocket handshake: "
993 "'Connection' header is missing",
997 // Connection header must contain "Upgrade".
998 TEST_F(WebSocketStreamCreateTest
, IncorrectConnectionHeader
) {
999 static const char kMissingConnectionResponse
[] =
1000 "HTTP/1.1 101 Switching Protocols\r\n"
1001 "Upgrade: websocket\r\n"
1002 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1003 "Connection: hogefuga\r\n"
1005 CreateAndConnectCustomResponse("ws://localhost/",
1010 kMissingConnectionResponse
);
1012 EXPECT_TRUE(has_failed());
1013 EXPECT_EQ("Error during WebSocket handshake: "
1014 "'Connection' header value must contain 'Upgrade'",
1018 // Connection header is permitted to contain other tokens.
1019 TEST_F(WebSocketStreamCreateTest
, AdditionalTokenInConnectionHeader
) {
1020 static const char kAdditionalConnectionTokenResponse
[] =
1021 "HTTP/1.1 101 Switching Protocols\r\n"
1022 "Upgrade: websocket\r\n"
1023 "Connection: Upgrade, Keep-Alive\r\n"
1024 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1026 CreateAndConnectCustomResponse("ws://localhost/",
1031 kAdditionalConnectionTokenResponse
);
1033 EXPECT_FALSE(has_failed());
1034 EXPECT_TRUE(stream_
);
1037 // Sec-WebSocket-Accept header must be present.
1038 TEST_F(WebSocketStreamCreateTest
, MissingSecWebSocketAccept
) {
1039 static const char kMissingAcceptResponse
[] =
1040 "HTTP/1.1 101 Switching Protocols\r\n"
1041 "Upgrade: websocket\r\n"
1042 "Connection: Upgrade\r\n"
1044 CreateAndConnectCustomResponse("ws://localhost/",
1049 kMissingAcceptResponse
);
1051 EXPECT_TRUE(has_failed());
1052 EXPECT_EQ("Error during WebSocket handshake: "
1053 "'Sec-WebSocket-Accept' header is missing",
1057 // Sec-WebSocket-Accept header must match the key that was sent.
1058 TEST_F(WebSocketStreamCreateTest
, WrongSecWebSocketAccept
) {
1059 static const char kIncorrectAcceptResponse
[] =
1060 "HTTP/1.1 101 Switching Protocols\r\n"
1061 "Upgrade: websocket\r\n"
1062 "Connection: Upgrade\r\n"
1063 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
1065 CreateAndConnectCustomResponse("ws://localhost/",
1070 kIncorrectAcceptResponse
);
1072 EXPECT_TRUE(has_failed());
1073 EXPECT_EQ("Error during WebSocket handshake: "
1074 "Incorrect 'Sec-WebSocket-Accept' header value",
1078 // Cancellation works.
1079 TEST_F(WebSocketStreamCreateTest
, Cancellation
) {
1080 CreateAndConnectStandard(
1081 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
1082 stream_request_
.reset();
1084 EXPECT_FALSE(has_failed());
1085 EXPECT_FALSE(stream_
);
1086 EXPECT_FALSE(request_info_
);
1087 EXPECT_FALSE(response_info_
);
1090 // Connect failure must look just like negotiation failure.
1091 TEST_F(WebSocketStreamCreateTest
, ConnectionFailure
) {
1092 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1093 socket_data
->set_connect_data(
1094 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
1095 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1096 "http://localhost", socket_data
.Pass());
1098 EXPECT_TRUE(has_failed());
1099 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1101 EXPECT_FALSE(request_info_
);
1102 EXPECT_FALSE(response_info_
);
1105 // Connect timeout must look just like any other failure.
1106 TEST_F(WebSocketStreamCreateTest
, ConnectionTimeout
) {
1107 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1108 socket_data
->set_connect_data(
1109 MockConnect(ASYNC
, ERR_CONNECTION_TIMED_OUT
));
1110 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1111 "http://localhost", socket_data
.Pass());
1113 EXPECT_TRUE(has_failed());
1114 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
1118 // The server doesn't respond to the opening handshake.
1119 TEST_F(WebSocketStreamCreateTest
, HandshakeTimeout
) {
1120 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1121 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
1122 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
1123 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
1124 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1125 "http://localhost", socket_data
.Pass(),
1126 timer
.PassAs
<base::Timer
>());
1127 EXPECT_FALSE(has_failed());
1128 ASSERT_TRUE(weak_timer
.get());
1129 EXPECT_TRUE(weak_timer
->IsRunning());
1134 EXPECT_TRUE(has_failed());
1135 EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
1136 ASSERT_TRUE(weak_timer
.get());
1137 EXPECT_FALSE(weak_timer
->IsRunning());
1140 // When the connection establishes the timer should be stopped.
1141 TEST_F(WebSocketStreamCreateTest
, HandshakeTimerOnSuccess
) {
1142 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
1143 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
1145 CreateAndConnectStandard(
1146 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "",
1147 timer
.PassAs
<base::Timer
>());
1148 ASSERT_TRUE(weak_timer
);
1149 EXPECT_TRUE(weak_timer
->IsRunning());
1152 EXPECT_FALSE(has_failed());
1153 EXPECT_TRUE(stream_
);
1154 ASSERT_TRUE(weak_timer
);
1155 EXPECT_FALSE(weak_timer
->IsRunning());
1158 // When the connection fails the timer should be stopped.
1159 TEST_F(WebSocketStreamCreateTest
, HandshakeTimerOnFailure
) {
1160 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1161 socket_data
->set_connect_data(
1162 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
1163 scoped_ptr
<MockWeakTimer
> timer(new MockWeakTimer(false, false));
1164 base::WeakPtr
<MockWeakTimer
> weak_timer
= timer
->AsWeakPtr();
1165 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1166 "http://localhost", socket_data
.Pass(),
1167 timer
.PassAs
<base::Timer
>());
1168 ASSERT_TRUE(weak_timer
.get());
1169 EXPECT_TRUE(weak_timer
->IsRunning());
1172 EXPECT_TRUE(has_failed());
1173 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1175 ASSERT_TRUE(weak_timer
.get());
1176 EXPECT_FALSE(weak_timer
->IsRunning());
1179 // Cancellation during connect works.
1180 TEST_F(WebSocketStreamCreateTest
, CancellationDuringConnect
) {
1181 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1182 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
1183 CreateAndConnectRawExpectations("ws://localhost/",
1186 socket_data
.Pass());
1187 stream_request_
.reset();
1189 EXPECT_FALSE(has_failed());
1190 EXPECT_FALSE(stream_
);
1193 // Cancellation during write of the request headers works.
1194 TEST_F(WebSocketStreamCreateTest
, CancellationDuringWrite
) {
1195 // We seem to need at least two operations in order to use SetStop().
1196 MockWrite writes
[] = {MockWrite(ASYNC
, 0, "GET / HTTP/"),
1197 MockWrite(ASYNC
, 1, "1.1\r\n")};
1198 // We keep a copy of the pointer so that we can call RunFor() on it later.
1199 DeterministicSocketData
* socket_data(
1200 new DeterministicSocketData(NULL
, 0, writes
, arraysize(writes
)));
1201 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
1202 socket_data
->SetStop(1);
1203 CreateAndConnectRawExpectations("ws://localhost/",
1206 make_scoped_ptr(socket_data
));
1208 stream_request_
.reset();
1210 EXPECT_FALSE(has_failed());
1211 EXPECT_FALSE(stream_
);
1212 EXPECT_TRUE(request_info_
);
1213 EXPECT_FALSE(response_info_
);
1216 // Cancellation during read of the response headers works.
1217 TEST_F(WebSocketStreamCreateTest
, CancellationDuringRead
) {
1218 std::string request
= WebSocketStandardRequest("/", "http://localhost", "");
1219 MockWrite writes
[] = {MockWrite(ASYNC
, 0, request
.c_str())};
1220 MockRead reads
[] = {
1221 MockRead(ASYNC
, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
1223 scoped_ptr
<DeterministicSocketData
> socket_data(
1224 BuildSocketData(reads
, writes
));
1225 socket_data
->SetStop(1);
1226 DeterministicSocketData
* socket_data_raw_ptr
= socket_data
.get();
1227 CreateAndConnectRawExpectations("ws://localhost/",
1230 socket_data
.Pass());
1231 socket_data_raw_ptr
->Run();
1232 stream_request_
.reset();
1234 EXPECT_FALSE(has_failed());
1235 EXPECT_FALSE(stream_
);
1236 EXPECT_TRUE(request_info_
);
1237 EXPECT_FALSE(response_info_
);
1240 // Over-size response headers (> 256KB) should not cause a crash. This is a
1241 // regression test for crbug.com/339456. It is based on the layout test
1242 // "cookie-flood.html".
1243 TEST_F(WebSocketStreamCreateTest
, VeryLargeResponseHeaders
) {
1244 std::string set_cookie_headers
;
1245 set_cookie_headers
.reserve(45 * 10000);
1246 for (int i
= 0; i
< 10000; ++i
) {
1247 set_cookie_headers
+=
1248 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i
);
1250 CreateAndConnectStandard("ws://localhost/", "/", NoSubProtocols(),
1251 "http://localhost", "", set_cookie_headers
);
1253 EXPECT_TRUE(has_failed());
1254 EXPECT_FALSE(response_info_
);
1257 // If the remote host closes the connection without sending headers, we should
1258 // log the console message "Connection closed before receiving a handshake
1260 TEST_F(WebSocketStreamCreateTest
, NoResponse
) {
1261 std::string request
= WebSocketStandardRequest("/", "http://localhost", "");
1262 MockWrite writes
[] = {MockWrite(ASYNC
, request
.data(), request
.size(), 0)};
1263 MockRead reads
[] = {MockRead(ASYNC
, 0, 1)};
1264 scoped_ptr
<DeterministicSocketData
> socket_data(
1265 BuildSocketData(reads
, writes
));
1266 DeterministicSocketData
* socket_data_raw_ptr
= socket_data
.get();
1267 CreateAndConnectRawExpectations("ws://localhost/",
1270 socket_data
.Pass());
1271 socket_data_raw_ptr
->RunFor(2);
1272 EXPECT_TRUE(has_failed());
1273 EXPECT_FALSE(stream_
);
1274 EXPECT_FALSE(response_info_
);
1275 EXPECT_EQ("Connection closed before receiving a handshake response",
1279 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateFailure
) {
1280 ssl_data_
.push_back(
1281 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1282 ssl_data_
[0]->cert
=
1283 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1284 ASSERT_TRUE(ssl_data_
[0]->cert
.get());
1285 scoped_ptr
<DeterministicSocketData
> raw_socket_data(BuildNullSocketData());
1286 CreateAndConnectRawExpectations("wss://localhost/",
1289 raw_socket_data
.Pass());
1291 EXPECT_FALSE(has_failed());
1292 ASSERT_TRUE(ssl_error_callbacks_
);
1293 ssl_error_callbacks_
->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID
,
1296 EXPECT_TRUE(has_failed());
1299 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateSuccess
) {
1300 scoped_ptr
<SSLSocketDataProvider
> ssl_data(
1301 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1303 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1304 ASSERT_TRUE(ssl_data
->cert
.get());
1305 ssl_data_
.push_back(ssl_data
.release());
1306 ssl_data
.reset(new SSLSocketDataProvider(ASYNC
, OK
));
1307 ssl_data_
.push_back(ssl_data
.release());
1308 url_request_context_host_
.AddRawExpectations(BuildNullSocketData());
1309 CreateAndConnectStandard(
1310 "wss://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
1312 ASSERT_TRUE(ssl_error_callbacks_
);
1313 ssl_error_callbacks_
->ContinueSSLRequest();
1315 EXPECT_FALSE(has_failed());
1316 EXPECT_TRUE(stream_
);
1319 // If the server requests authorisation, but we have no credentials, the
1320 // connection should fail cleanly.
1321 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureNoCredentials
) {
1322 CreateAndConnectCustomResponse("ws://localhost/",
1327 kUnauthorizedResponse
);
1329 EXPECT_TRUE(has_failed());
1330 EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1332 EXPECT_TRUE(response_info_
);
1335 TEST_F(WebSocketStreamCreateBasicAuthTest
, SuccessPasswordInUrl
) {
1336 CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
1338 WebSocketStandardResponse(std::string()));
1340 EXPECT_FALSE(has_failed());
1341 EXPECT_TRUE(stream_
);
1342 ASSERT_TRUE(response_info_
);
1343 EXPECT_EQ(101, response_info_
->status_code
);
1346 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureIncorrectPasswordInUrl
) {
1347 CreateAndConnectAuthHandshake(
1348 "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse
);
1350 EXPECT_TRUE(has_failed());
1351 EXPECT_TRUE(response_info_
);
1354 // Digest auth has the same connection semantics as Basic auth, so we can
1355 // generally assume that whatever works for Basic auth will also work for
1356 // Digest. There's just one test here, to confirm that it works at all.
1357 TEST_F(WebSocketStreamCreateDigestAuthTest
, DigestPasswordInUrl
) {
1358 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
1360 CreateAndConnectRawExpectations(
1361 "ws://FooBar:pass@localhost/",
1364 helper_
.BuildSocketData2(kAuthorizedRequest
,
1365 WebSocketStandardResponse(std::string())));
1367 EXPECT_FALSE(has_failed());
1368 EXPECT_TRUE(stream_
);
1369 ASSERT_TRUE(response_info_
);
1370 EXPECT_EQ(101, response_info_
->status_code
);
1373 TEST_F(WebSocketStreamCreateUMATest
, Incomplete
) {
1374 const std::string
name("Net.WebSocket.HandshakeResult");
1375 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1378 StreamCreation creation
;
1379 creation
.CreateAndConnectStandard("ws://localhost/",
1381 creation
.NoSubProtocols(),
1387 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1388 ASSERT_TRUE(samples
);
1390 samples
->Subtract(*original
); // Cancel the original values.
1392 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1393 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1394 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1397 TEST_F(WebSocketStreamCreateUMATest
, Connected
) {
1398 const std::string
name("Net.WebSocket.HandshakeResult");
1399 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1402 StreamCreation creation
;
1403 creation
.CreateAndConnectStandard("ws://localhost/",
1405 creation
.NoSubProtocols(),
1409 creation
.RunUntilIdle();
1412 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1413 ASSERT_TRUE(samples
);
1415 samples
->Subtract(*original
); // Cancel the original values.
1417 EXPECT_EQ(0, samples
->GetCount(INCOMPLETE
));
1418 EXPECT_EQ(1, samples
->GetCount(CONNECTED
));
1419 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1422 TEST_F(WebSocketStreamCreateUMATest
, Failed
) {
1423 const std::string
name("Net.WebSocket.HandshakeResult");
1424 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1427 StreamCreation creation
;
1428 static const char kInvalidStatusCodeResponse
[] =
1429 "HTTP/1.1 200 OK\r\n"
1430 "Upgrade: websocket\r\n"
1431 "Connection: Upgrade\r\n"
1432 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1434 creation
.CreateAndConnectCustomResponse("ws://localhost/",
1436 creation
.NoSubProtocols(),
1439 kInvalidStatusCodeResponse
);
1440 creation
.RunUntilIdle();
1443 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1444 ASSERT_TRUE(samples
);
1446 samples
->Subtract(*original
); // Cancel the original values.
1448 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1449 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1450 EXPECT_EQ(0, samples
->GetCount(FAILED
));