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 "net/base/net_errors.h"
20 #include "net/base/test_data_directory.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/socket/client_socket_handle.h"
24 #include "net/socket/socket_test_util.h"
25 #include "net/test/cert_test_util.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "net/websockets/websocket_basic_handshake_stream.h"
28 #include "net/websockets/websocket_frame.h"
29 #include "net/websockets/websocket_handshake_request_info.h"
30 #include "net/websockets/websocket_handshake_response_info.h"
31 #include "net/websockets/websocket_handshake_stream_create_helper.h"
32 #include "net/websockets/websocket_test_util.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 #include "url/origin.h"
40 typedef std::pair
<std::string
, std::string
> HeaderKeyValuePair
;
42 std::vector
<HeaderKeyValuePair
> ToVector(const HttpRequestHeaders
& headers
) {
43 HttpRequestHeaders::Iterator
it(headers
);
44 std::vector
<HeaderKeyValuePair
> result
;
46 result
.push_back(HeaderKeyValuePair(it
.name(), it
.value()));
50 std::vector
<HeaderKeyValuePair
> ToVector(const HttpResponseHeaders
& headers
) {
52 std::string name
, value
;
53 std::vector
<HeaderKeyValuePair
> result
;
54 while (headers
.EnumerateHeaderLines(&iter
, &name
, &value
))
55 result
.push_back(HeaderKeyValuePair(name
, value
));
59 // Simple builder for a DeterministicSocketData object to save repetitive code.
60 // It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
61 // be used in tests where the connect fails. In practice, those tests never have
62 // any read/write data and so can't benefit from it anyway. The arrays are not
63 // copied. It is up to the caller to ensure they stay in scope until the test
65 template <size_t reads_count
, size_t writes_count
>
66 scoped_ptr
<DeterministicSocketData
> BuildSocketData(
67 MockRead (&reads
)[reads_count
],
68 MockWrite (&writes
)[writes_count
]) {
69 scoped_ptr
<DeterministicSocketData
> socket_data(
70 new DeterministicSocketData(reads
, reads_count
, writes
, writes_count
));
71 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
72 socket_data
->SetStop(reads_count
+ writes_count
);
73 return socket_data
.Pass();
76 // Builder for a DeterministicSocketData that expects nothing. This does not
77 // set the connect data, so the calling code must do that explicitly.
78 scoped_ptr
<DeterministicSocketData
> BuildNullSocketData() {
79 return make_scoped_ptr(new DeterministicSocketData(NULL
, 0, NULL
, 0));
82 // A sub-class of WebSocketHandshakeStreamCreateHelper which always sets a
83 // deterministic key to use in the WebSocket handshake.
84 class DeterministicKeyWebSocketHandshakeStreamCreateHelper
85 : public WebSocketHandshakeStreamCreateHelper
{
87 DeterministicKeyWebSocketHandshakeStreamCreateHelper(
88 WebSocketStream::ConnectDelegate
* connect_delegate
,
89 const std::vector
<std::string
>& requested_subprotocols
)
90 : WebSocketHandshakeStreamCreateHelper(connect_delegate
,
91 requested_subprotocols
) {}
93 virtual void OnStreamCreated(WebSocketBasicHandshakeStream
* stream
) OVERRIDE
{
94 stream
->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
98 class WebSocketStreamCreateTest
: public ::testing::Test
{
100 WebSocketStreamCreateTest() : has_failed_(false), ssl_fatal_(false) {}
102 void CreateAndConnectCustomResponse(
103 const std::string
& socket_url
,
104 const std::string
& socket_path
,
105 const std::vector
<std::string
>& sub_protocols
,
106 const std::string
& origin
,
107 const std::string
& extra_request_headers
,
108 const std::string
& response_body
) {
109 url_request_context_host_
.SetExpectations(
110 WebSocketStandardRequest(socket_path
, origin
, extra_request_headers
),
112 CreateAndConnectStream(socket_url
, sub_protocols
, origin
);
115 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
116 // errors like "Unable to perform synchronous IO while stopped" will occur.
117 void CreateAndConnectStandard(const std::string
& socket_url
,
118 const std::string
& socket_path
,
119 const std::vector
<std::string
>& sub_protocols
,
120 const std::string
& origin
,
121 const std::string
& extra_request_headers
,
122 const std::string
& extra_response_headers
) {
123 CreateAndConnectCustomResponse(
128 extra_request_headers
,
129 WebSocketStandardResponse(extra_response_headers
));
132 void CreateAndConnectRawExpectations(
133 const std::string
& socket_url
,
134 const std::vector
<std::string
>& sub_protocols
,
135 const std::string
& origin
,
136 scoped_ptr
<DeterministicSocketData
> socket_data
) {
137 AddRawExpectations(socket_data
.Pass());
138 CreateAndConnectStream(socket_url
, sub_protocols
, origin
);
141 // Add additional raw expectations for sockets created before the final one.
142 void AddRawExpectations(scoped_ptr
<DeterministicSocketData
> socket_data
) {
143 url_request_context_host_
.AddRawExpectations(socket_data
.Pass());
146 // A wrapper for CreateAndConnectStreamForTesting that knows about our default
148 void CreateAndConnectStream(const std::string
& socket_url
,
149 const std::vector
<std::string
>& sub_protocols
,
150 const std::string
& origin
) {
151 for (size_t i
= 0; i
< ssl_data_
.size(); ++i
) {
152 scoped_ptr
<SSLSocketDataProvider
> ssl_data(ssl_data_
[i
]);
154 url_request_context_host_
.AddSSLSocketDataProvider(ssl_data
.Pass());
157 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate(
158 new TestConnectDelegate(this));
159 WebSocketStream::ConnectDelegate
* delegate
= connect_delegate
.get();
160 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper(
161 new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
162 delegate
, sub_protocols
));
163 stream_request_
= ::net::CreateAndConnectStreamForTesting(
165 create_helper
.Pass(),
167 url_request_context_host_
.GetURLRequestContext(),
169 connect_delegate
.Pass());
172 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
174 // A simple function to make the tests more readable. Creates an empty vector.
175 static std::vector
<std::string
> NoSubProtocols() {
176 return std::vector
<std::string
>();
179 const std::string
& failure_message() const { return failure_message_
; }
180 bool has_failed() const { return has_failed_
; }
182 class TestConnectDelegate
: public WebSocketStream::ConnectDelegate
{
184 explicit TestConnectDelegate(WebSocketStreamCreateTest
* owner
)
187 virtual void OnSuccess(scoped_ptr
<WebSocketStream
> stream
) OVERRIDE
{
188 stream
.swap(owner_
->stream_
);
191 virtual void OnFailure(const std::string
& message
) OVERRIDE
{
192 owner_
->has_failed_
= true;
193 owner_
->failure_message_
= message
;
196 virtual void OnStartOpeningHandshake(
197 scoped_ptr
<WebSocketHandshakeRequestInfo
> request
) OVERRIDE
{
198 // Can be called multiple times (in the case of HTTP auth). Last call
200 owner_
->request_info_
= request
.Pass();
202 virtual void OnFinishOpeningHandshake(
203 scoped_ptr
<WebSocketHandshakeResponseInfo
> response
) OVERRIDE
{
204 if (owner_
->response_info_
)
206 owner_
->response_info_
= response
.Pass();
208 virtual void OnSSLCertificateError(
209 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
>
211 const SSLInfo
& ssl_info
,
212 bool fatal
) OVERRIDE
{
213 owner_
->ssl_error_callbacks_
= ssl_error_callbacks
.Pass();
214 owner_
->ssl_info_
= ssl_info
;
215 owner_
->ssl_fatal_
= fatal
;
219 WebSocketStreamCreateTest
* owner_
;
222 WebSocketTestURLRequestContextHost url_request_context_host_
;
223 scoped_ptr
<WebSocketStreamRequest
> stream_request_
;
224 // Only set if the connection succeeded.
225 scoped_ptr
<WebSocketStream
> stream_
;
226 // Only set if the connection failed.
227 std::string failure_message_
;
229 scoped_ptr
<WebSocketHandshakeRequestInfo
> request_info_
;
230 scoped_ptr
<WebSocketHandshakeResponseInfo
> response_info_
;
231 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
> ssl_error_callbacks_
;
234 ScopedVector
<SSLSocketDataProvider
> ssl_data_
;
237 // There are enough tests of the Sec-WebSocket-Extensions header that they
238 // deserve their own test fixture.
239 class WebSocketStreamCreateExtensionTest
: public WebSocketStreamCreateTest
{
241 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
242 // header in the response set to |extensions_header_value|. Runs the event
243 // loop to allow the connect to complete.
244 void CreateAndConnectWithExtensions(
245 const std::string
& extensions_header_value
) {
246 CreateAndConnectStandard(
247 "ws://localhost/testing_path",
252 "Sec-WebSocket-Extensions: " + extensions_header_value
+ "\r\n");
257 // Common code to construct expectations for authentication tests that receive
258 // the auth challenge on one connection and then create a second connection to
259 // send the authenticated request on.
260 class CommonAuthTestHelper
{
262 CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
264 scoped_ptr
<DeterministicSocketData
> BuildSocketData1(
265 const std::string
& response
) {
266 request1_
= WebSocketStandardRequest("/", "http://localhost", "");
267 writes1_
[0] = MockWrite(SYNCHRONOUS
, 0, request1_
.c_str());
268 response1_
= response
;
269 reads1_
[0] = MockRead(SYNCHRONOUS
, 1, response1_
.c_str());
270 reads1_
[1] = MockRead(SYNCHRONOUS
, OK
, 2); // Close connection
272 return BuildSocketData(reads1_
, writes1_
);
275 scoped_ptr
<DeterministicSocketData
> BuildSocketData2(
276 const std::string
& request
,
277 const std::string
& response
) {
279 response2_
= response
;
280 writes2_
[0] = MockWrite(SYNCHRONOUS
, 0, request2_
.c_str());
281 reads2_
[0] = MockRead(SYNCHRONOUS
, 1, response2_
.c_str());
282 return BuildSocketData(reads2_
, writes2_
);
286 // These need to be object-scoped since they have to remain valid until all
287 // socket operations in the test are complete.
288 std::string request1_
;
289 std::string request2_
;
290 std::string response1_
;
291 std::string response2_
;
293 MockWrite writes1_
[1];
295 MockWrite writes2_
[1];
297 DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper
);
300 // Data and methods for BasicAuth tests.
301 class WebSocketStreamCreateBasicAuthTest
: public WebSocketStreamCreateTest
{
303 void CreateAndConnectAuthHandshake(const std::string
& url
,
304 const std::string
& base64_user_pass
,
305 const std::string
& response2
) {
306 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
308 static const char request2format
[] =
310 "Host: localhost\r\n"
311 "Connection: Upgrade\r\n"
312 "Pragma: no-cache\r\n"
313 "Cache-Control: no-cache\r\n"
314 "Authorization: Basic %s\r\n"
315 "Upgrade: websocket\r\n"
316 "Origin: http://localhost\r\n"
317 "Sec-WebSocket-Version: 13\r\n"
319 "Accept-Encoding: gzip,deflate\r\n"
320 "Accept-Language: en-us,fr\r\n"
321 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
322 "Sec-WebSocket-Extensions: permessage-deflate; "
323 "client_max_window_bits\r\n"
325 const std::string request
=
326 base::StringPrintf(request2format
, base64_user_pass
.c_str());
327 CreateAndConnectRawExpectations(
331 helper_
.BuildSocketData2(request
, response2
));
334 static const char kUnauthorizedResponse
[];
336 CommonAuthTestHelper helper_
;
339 class WebSocketStreamCreateDigestAuthTest
: public WebSocketStreamCreateTest
{
341 static const char kUnauthorizedResponse
[];
342 static const char kAuthorizedRequest
[];
344 CommonAuthTestHelper helper_
;
347 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse
[] =
348 "HTTP/1.1 401 Unauthorized\r\n"
349 "Content-Length: 0\r\n"
350 "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
353 // These negotiation values are borrowed from
354 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
355 // you are bored. Only the weakest (no qop) variants of Digest authentication
356 // can be tested by this method, because the others involve random input.
357 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse
[] =
358 "HTTP/1.1 401 Unauthorized\r\n"
359 "Content-Length: 0\r\n"
360 "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
363 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest
[] =
365 "Host: localhost\r\n"
366 "Connection: Upgrade\r\n"
367 "Pragma: no-cache\r\n"
368 "Cache-Control: no-cache\r\n"
369 "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
370 "nonce=\"nonce-value\", uri=\"/\", "
371 "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
372 "Upgrade: websocket\r\n"
373 "Origin: http://localhost\r\n"
374 "Sec-WebSocket-Version: 13\r\n"
376 "Accept-Encoding: gzip,deflate\r\n"
377 "Accept-Language: en-us,fr\r\n"
378 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
379 "Sec-WebSocket-Extensions: permessage-deflate; "
380 "client_max_window_bits\r\n"
383 class WebSocketStreamCreateUMATest
: public ::testing::Test
{
385 // This enum should match with the enum in Delegate in websocket_stream.cc.
386 enum HandshakeResult
{
390 NUM_HANDSHAKE_RESULT_TYPES
,
393 class StreamCreation
: public WebSocketStreamCreateTest
{
394 virtual void TestBody() OVERRIDE
{}
397 scoped_ptr
<base::HistogramSamples
> GetSamples(const std::string
& name
) {
398 base::HistogramBase
* histogram
=
399 base::StatisticsRecorder::FindHistogram(name
);
400 return histogram
? histogram
->SnapshotSamples()
401 : scoped_ptr
<base::HistogramSamples
>();
405 // Confirm that the basic case works as expected.
406 TEST_F(WebSocketStreamCreateTest
, SimpleSuccess
) {
407 CreateAndConnectStandard(
408 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
409 EXPECT_FALSE(request_info_
);
410 EXPECT_FALSE(response_info_
);
412 EXPECT_FALSE(has_failed());
413 EXPECT_TRUE(stream_
);
414 EXPECT_TRUE(request_info_
);
415 EXPECT_TRUE(response_info_
);
418 TEST_F(WebSocketStreamCreateTest
, HandshakeInfo
) {
419 static const char kResponse
[] =
420 "HTTP/1.1 101 Switching Protocols\r\n"
421 "Upgrade: websocket\r\n"
422 "Connection: Upgrade\r\n"
423 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
429 CreateAndConnectCustomResponse(
436 EXPECT_FALSE(request_info_
);
437 EXPECT_FALSE(response_info_
);
439 EXPECT_TRUE(stream_
);
440 ASSERT_TRUE(request_info_
);
441 ASSERT_TRUE(response_info_
);
442 std::vector
<HeaderKeyValuePair
> request_headers
=
443 ToVector(request_info_
->headers
);
444 // We examine the contents of request_info_ and response_info_
445 // mainly only in this test case.
446 EXPECT_EQ(GURL("ws://localhost/"), request_info_
->url
);
447 EXPECT_EQ(GURL("ws://localhost/"), response_info_
->url
);
448 EXPECT_EQ(101, response_info_
->status_code
);
449 EXPECT_EQ("Switching Protocols", response_info_
->status_text
);
450 ASSERT_EQ(12u, request_headers
.size());
451 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers
[0]);
452 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers
[1]);
453 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers
[2]);
454 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
456 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers
[4]);
457 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
459 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
461 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers
[7]);
462 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip,deflate"),
464 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
466 EXPECT_EQ("Sec-WebSocket-Key", request_headers
[10].first
);
467 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
468 "permessage-deflate; client_max_window_bits"),
469 request_headers
[11]);
471 std::vector
<HeaderKeyValuePair
> response_headers
=
472 ToVector(*response_info_
->headers
.get());
473 ASSERT_EQ(6u, response_headers
.size());
474 // Sort the headers for ease of verification.
475 std::sort(response_headers
.begin(), response_headers
.end());
477 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers
[0]);
478 EXPECT_EQ("Sec-WebSocket-Accept", response_headers
[1].first
);
479 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers
[2]);
480 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers
[3]);
481 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers
[4]);
482 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers
[5]);
485 // Confirm that the stream isn't established until the message loop runs.
486 TEST_F(WebSocketStreamCreateTest
, NeedsToRunLoop
) {
487 CreateAndConnectStandard(
488 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
489 EXPECT_FALSE(has_failed());
490 EXPECT_FALSE(stream_
);
493 // Check the path is used.
494 TEST_F(WebSocketStreamCreateTest
, PathIsUsed
) {
495 CreateAndConnectStandard("ws://localhost/testing_path",
502 EXPECT_FALSE(has_failed());
503 EXPECT_TRUE(stream_
);
506 // Check that the origin is used.
507 TEST_F(WebSocketStreamCreateTest
, OriginIsUsed
) {
508 CreateAndConnectStandard("ws://localhost/testing_path",
515 EXPECT_FALSE(has_failed());
516 EXPECT_TRUE(stream_
);
519 // Check that sub-protocols are sent and parsed.
520 TEST_F(WebSocketStreamCreateTest
, SubProtocolIsUsed
) {
521 std::vector
<std::string
> sub_protocols
;
522 sub_protocols
.push_back("chatv11.chromium.org");
523 sub_protocols
.push_back("chatv20.chromium.org");
524 CreateAndConnectStandard("ws://localhost/testing_path",
528 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
529 "chatv20.chromium.org\r\n",
530 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
532 EXPECT_TRUE(stream_
);
533 EXPECT_FALSE(has_failed());
534 EXPECT_EQ("chatv20.chromium.org", stream_
->GetSubProtocol());
537 // Unsolicited sub-protocols are rejected.
538 TEST_F(WebSocketStreamCreateTest
, UnsolicitedSubProtocol
) {
539 CreateAndConnectStandard("ws://localhost/testing_path",
544 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
546 EXPECT_FALSE(stream_
);
547 EXPECT_TRUE(has_failed());
548 EXPECT_EQ("Error during WebSocket handshake: "
549 "Response must not include 'Sec-WebSocket-Protocol' header "
550 "if not present in request: chatv20.chromium.org",
554 // Missing sub-protocol response is rejected.
555 TEST_F(WebSocketStreamCreateTest
, UnacceptedSubProtocol
) {
556 std::vector
<std::string
> sub_protocols
;
557 sub_protocols
.push_back("chat.example.com");
558 CreateAndConnectStandard("ws://localhost/testing_path",
562 "Sec-WebSocket-Protocol: chat.example.com\r\n",
565 EXPECT_FALSE(stream_
);
566 EXPECT_TRUE(has_failed());
567 EXPECT_EQ("Error during WebSocket handshake: "
568 "Sent non-empty 'Sec-WebSocket-Protocol' header "
569 "but no response was received",
573 // Only one sub-protocol can be accepted.
574 TEST_F(WebSocketStreamCreateTest
, MultipleSubProtocolsInResponse
) {
575 std::vector
<std::string
> sub_protocols
;
576 sub_protocols
.push_back("chatv11.chromium.org");
577 sub_protocols
.push_back("chatv20.chromium.org");
578 CreateAndConnectStandard("ws://localhost/testing_path",
582 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
583 "chatv20.chromium.org\r\n",
584 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
585 "chatv20.chromium.org\r\n");
587 EXPECT_FALSE(stream_
);
588 EXPECT_TRUE(has_failed());
589 EXPECT_EQ("Error during WebSocket handshake: "
590 "'Sec-WebSocket-Protocol' header must not appear "
591 "more than once in a response",
595 // Unmatched sub-protocol should be rejected.
596 TEST_F(WebSocketStreamCreateTest
, UnmatchedSubProtocolInResponse
) {
597 std::vector
<std::string
> sub_protocols
;
598 sub_protocols
.push_back("chatv11.chromium.org");
599 sub_protocols
.push_back("chatv20.chromium.org");
600 CreateAndConnectStandard("ws://localhost/testing_path",
604 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
605 "chatv20.chromium.org\r\n",
606 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
608 EXPECT_FALSE(stream_
);
609 EXPECT_TRUE(has_failed());
610 EXPECT_EQ("Error during WebSocket handshake: "
611 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
612 "in response does not match any of sent values",
616 // permessage-deflate extension basic success case.
617 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateSuccess
) {
618 CreateAndConnectWithExtensions("permessage-deflate");
619 EXPECT_TRUE(stream_
);
620 EXPECT_FALSE(has_failed());
623 // permessage-deflate extensions success with all parameters.
624 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateParamsSuccess
) {
625 CreateAndConnectWithExtensions(
626 "permessage-deflate; client_no_context_takeover; "
627 "server_max_window_bits=11; client_max_window_bits=13; "
628 "server_no_context_takeover");
629 EXPECT_TRUE(stream_
);
630 EXPECT_FALSE(has_failed());
633 // Verify that incoming messages are actually decompressed with
634 // permessage-deflate enabled.
635 TEST_F(WebSocketStreamCreateExtensionTest
, PerMessageDeflateInflates
) {
636 CreateAndConnectCustomResponse(
637 "ws://localhost/testing_path",
642 WebSocketStandardResponse(
643 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
645 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
646 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
650 ASSERT_TRUE(stream_
);
651 ScopedVector
<WebSocketFrame
> frames
;
652 CompletionCallback callback
;
653 ASSERT_EQ(OK
, stream_
->ReadFrames(&frames
, callback
));
654 ASSERT_EQ(1U, frames
.size());
655 ASSERT_EQ(5U, frames
[0]->header
.payload_length
);
656 EXPECT_EQ("Hello", std::string(frames
[0]->data
->data(), 5));
659 // Unknown extension in the response is rejected
660 TEST_F(WebSocketStreamCreateExtensionTest
, UnknownExtension
) {
661 CreateAndConnectWithExtensions("x-unknown-extension");
662 EXPECT_FALSE(stream_
);
663 EXPECT_TRUE(has_failed());
664 EXPECT_EQ("Error during WebSocket handshake: "
665 "Found an unsupported extension 'x-unknown-extension' "
666 "in 'Sec-WebSocket-Extensions' header",
670 // Malformed extensions are rejected (this file does not cover all possible
671 // parse failures, as the parser is covered thoroughly by its own unit tests).
672 TEST_F(WebSocketStreamCreateExtensionTest
, MalformedExtension
) {
673 CreateAndConnectWithExtensions(";");
674 EXPECT_FALSE(stream_
);
675 EXPECT_TRUE(has_failed());
677 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
678 "value is rejected by the parser: ;",
682 // The permessage-deflate extension may only be specified once.
683 TEST_F(WebSocketStreamCreateExtensionTest
, OnlyOnePerMessageDeflateAllowed
) {
684 CreateAndConnectWithExtensions(
685 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
686 EXPECT_FALSE(stream_
);
687 EXPECT_TRUE(has_failed());
689 "Error during WebSocket handshake: "
690 "Received duplicate permessage-deflate response",
694 // permessage-deflate parameters may not be duplicated.
695 TEST_F(WebSocketStreamCreateExtensionTest
, NoDuplicateParameters
) {
696 CreateAndConnectWithExtensions(
697 "permessage-deflate; client_no_context_takeover; "
698 "client_no_context_takeover");
699 EXPECT_FALSE(stream_
);
700 EXPECT_TRUE(has_failed());
702 "Error during WebSocket handshake: Error in permessage-deflate: "
703 "Received duplicate permessage-deflate extension parameter "
704 "client_no_context_takeover",
708 // permessage-deflate parameters must start with "client_" or "server_"
709 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterPrefix
) {
710 CreateAndConnectWithExtensions(
711 "permessage-deflate; absurd_no_context_takeover");
712 EXPECT_FALSE(stream_
);
713 EXPECT_TRUE(has_failed());
715 "Error during WebSocket handshake: Error in permessage-deflate: "
716 "Received an unexpected permessage-deflate extension parameter",
720 // permessage-deflate parameters must be either *_no_context_takeover or
722 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterSuffix
) {
723 CreateAndConnectWithExtensions(
724 "permessage-deflate; client_max_content_bits=5");
725 EXPECT_FALSE(stream_
);
726 EXPECT_TRUE(has_failed());
728 "Error during WebSocket handshake: Error in permessage-deflate: "
729 "Received an unexpected permessage-deflate extension parameter",
733 // *_no_context_takeover parameters must not have an argument
734 TEST_F(WebSocketStreamCreateExtensionTest
, BadParameterValue
) {
735 CreateAndConnectWithExtensions(
736 "permessage-deflate; client_no_context_takeover=true");
737 EXPECT_FALSE(stream_
);
738 EXPECT_TRUE(has_failed());
740 "Error during WebSocket handshake: Error in permessage-deflate: "
741 "Received invalid client_no_context_takeover parameter",
745 // *_max_window_bits must have an argument
746 TEST_F(WebSocketStreamCreateExtensionTest
, NoMaxWindowBitsArgument
) {
747 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
748 EXPECT_FALSE(stream_
);
749 EXPECT_TRUE(has_failed());
751 "Error during WebSocket handshake: Error in permessage-deflate: "
752 "client_max_window_bits must have value",
756 // *_max_window_bits must be an integer
757 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueInteger
) {
758 CreateAndConnectWithExtensions(
759 "permessage-deflate; server_max_window_bits=banana");
760 EXPECT_FALSE(stream_
);
761 EXPECT_TRUE(has_failed());
763 "Error during WebSocket handshake: Error in permessage-deflate: "
764 "Received invalid server_max_window_bits parameter",
768 // *_max_window_bits must be >= 8
769 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueTooSmall
) {
770 CreateAndConnectWithExtensions(
771 "permessage-deflate; server_max_window_bits=7");
772 EXPECT_FALSE(stream_
);
773 EXPECT_TRUE(has_failed());
775 "Error during WebSocket handshake: Error in permessage-deflate: "
776 "Received invalid server_max_window_bits parameter",
780 // *_max_window_bits must be <= 15
781 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueTooBig
) {
782 CreateAndConnectWithExtensions(
783 "permessage-deflate; client_max_window_bits=16");
784 EXPECT_FALSE(stream_
);
785 EXPECT_TRUE(has_failed());
787 "Error during WebSocket handshake: Error in permessage-deflate: "
788 "Received invalid client_max_window_bits parameter",
792 // *_max_window_bits must not start with 0
793 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueStartsWithZero
) {
794 CreateAndConnectWithExtensions(
795 "permessage-deflate; client_max_window_bits=08");
796 EXPECT_FALSE(stream_
);
797 EXPECT_TRUE(has_failed());
799 "Error during WebSocket handshake: Error in permessage-deflate: "
800 "Received invalid client_max_window_bits parameter",
804 // *_max_window_bits must not start with +
805 TEST_F(WebSocketStreamCreateExtensionTest
, MaxWindowBitsValueStartsWithPlus
) {
806 CreateAndConnectWithExtensions(
807 "permessage-deflate; server_max_window_bits=+9");
808 EXPECT_FALSE(stream_
);
809 EXPECT_TRUE(has_failed());
811 "Error during WebSocket handshake: Error in permessage-deflate: "
812 "Received invalid server_max_window_bits parameter",
816 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
817 // arguments from the server. This is difficult because the data written to the
818 // socket is randomly masked.
820 // Additional Sec-WebSocket-Accept headers should be rejected.
821 TEST_F(WebSocketStreamCreateTest
, DoubleAccept
) {
822 CreateAndConnectStandard(
828 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
830 EXPECT_FALSE(stream_
);
831 EXPECT_TRUE(has_failed());
832 EXPECT_EQ("Error during WebSocket handshake: "
833 "'Sec-WebSocket-Accept' header must not appear "
834 "more than once in a response",
838 // Response code 200 must be rejected.
839 TEST_F(WebSocketStreamCreateTest
, InvalidStatusCode
) {
840 static const char kInvalidStatusCodeResponse
[] =
841 "HTTP/1.1 200 OK\r\n"
842 "Upgrade: websocket\r\n"
843 "Connection: Upgrade\r\n"
844 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
846 CreateAndConnectCustomResponse("ws://localhost/",
851 kInvalidStatusCodeResponse
);
853 EXPECT_TRUE(has_failed());
854 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
858 // Redirects are not followed (according to the WHATWG WebSocket API, which
859 // overrides RFC6455 for browser applications).
860 TEST_F(WebSocketStreamCreateTest
, RedirectsRejected
) {
861 static const char kRedirectResponse
[] =
862 "HTTP/1.1 302 Moved Temporarily\r\n"
863 "Content-Type: text/html\r\n"
864 "Content-Length: 34\r\n"
865 "Connection: keep-alive\r\n"
866 "Location: ws://localhost/other\r\n"
868 "<title>Moved</title><h1>Moved</h1>";
869 CreateAndConnectCustomResponse("ws://localhost/",
876 EXPECT_TRUE(has_failed());
877 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
881 // Malformed responses should be rejected. HttpStreamParser will accept just
882 // about any garbage in the middle of the headers. To make it give up, the junk
883 // has to be at the start of the response. Even then, it just gets treated as an
884 // HTTP/0.9 response.
885 TEST_F(WebSocketStreamCreateTest
, MalformedResponse
) {
886 static const char kMalformedResponse
[] =
887 "220 mx.google.com ESMTP\r\n"
888 "HTTP/1.1 101 OK\r\n"
889 "Upgrade: websocket\r\n"
890 "Connection: Upgrade\r\n"
891 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
893 CreateAndConnectCustomResponse("ws://localhost/",
900 EXPECT_TRUE(has_failed());
901 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
905 // Upgrade header must be present.
906 TEST_F(WebSocketStreamCreateTest
, MissingUpgradeHeader
) {
907 static const char kMissingUpgradeResponse
[] =
908 "HTTP/1.1 101 Switching Protocols\r\n"
909 "Connection: Upgrade\r\n"
910 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
912 CreateAndConnectCustomResponse("ws://localhost/",
917 kMissingUpgradeResponse
);
919 EXPECT_TRUE(has_failed());
920 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
924 // There must only be one upgrade header.
925 TEST_F(WebSocketStreamCreateTest
, DoubleUpgradeHeader
) {
926 CreateAndConnectStandard(
931 "", "Upgrade: HTTP/2.0\r\n");
933 EXPECT_TRUE(has_failed());
934 EXPECT_EQ("Error during WebSocket handshake: "
935 "'Upgrade' header must not appear more than once in a response",
939 // There must only be one correct upgrade header.
940 TEST_F(WebSocketStreamCreateTest
, IncorrectUpgradeHeader
) {
941 static const char kMissingUpgradeResponse
[] =
942 "HTTP/1.1 101 Switching Protocols\r\n"
943 "Connection: Upgrade\r\n"
944 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
945 "Upgrade: hogefuga\r\n"
947 CreateAndConnectCustomResponse("ws://localhost/",
952 kMissingUpgradeResponse
);
954 EXPECT_TRUE(has_failed());
955 EXPECT_EQ("Error during WebSocket handshake: "
956 "'Upgrade' header value is not 'WebSocket': hogefuga",
960 // Connection header must be present.
961 TEST_F(WebSocketStreamCreateTest
, MissingConnectionHeader
) {
962 static const char kMissingConnectionResponse
[] =
963 "HTTP/1.1 101 Switching Protocols\r\n"
964 "Upgrade: websocket\r\n"
965 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
967 CreateAndConnectCustomResponse("ws://localhost/",
972 kMissingConnectionResponse
);
974 EXPECT_TRUE(has_failed());
975 EXPECT_EQ("Error during WebSocket handshake: "
976 "'Connection' header is missing",
980 // Connection header must contain "Upgrade".
981 TEST_F(WebSocketStreamCreateTest
, IncorrectConnectionHeader
) {
982 static const char kMissingConnectionResponse
[] =
983 "HTTP/1.1 101 Switching Protocols\r\n"
984 "Upgrade: websocket\r\n"
985 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
986 "Connection: hogefuga\r\n"
988 CreateAndConnectCustomResponse("ws://localhost/",
993 kMissingConnectionResponse
);
995 EXPECT_TRUE(has_failed());
996 EXPECT_EQ("Error during WebSocket handshake: "
997 "'Connection' header value must contain 'Upgrade'",
1001 // Connection header is permitted to contain other tokens.
1002 TEST_F(WebSocketStreamCreateTest
, AdditionalTokenInConnectionHeader
) {
1003 static const char kAdditionalConnectionTokenResponse
[] =
1004 "HTTP/1.1 101 Switching Protocols\r\n"
1005 "Upgrade: websocket\r\n"
1006 "Connection: Upgrade, Keep-Alive\r\n"
1007 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1009 CreateAndConnectCustomResponse("ws://localhost/",
1014 kAdditionalConnectionTokenResponse
);
1016 EXPECT_FALSE(has_failed());
1017 EXPECT_TRUE(stream_
);
1020 // Sec-WebSocket-Accept header must be present.
1021 TEST_F(WebSocketStreamCreateTest
, MissingSecWebSocketAccept
) {
1022 static const char kMissingAcceptResponse
[] =
1023 "HTTP/1.1 101 Switching Protocols\r\n"
1024 "Upgrade: websocket\r\n"
1025 "Connection: Upgrade\r\n"
1027 CreateAndConnectCustomResponse("ws://localhost/",
1032 kMissingAcceptResponse
);
1034 EXPECT_TRUE(has_failed());
1035 EXPECT_EQ("Error during WebSocket handshake: "
1036 "'Sec-WebSocket-Accept' header is missing",
1040 // Sec-WebSocket-Accept header must match the key that was sent.
1041 TEST_F(WebSocketStreamCreateTest
, WrongSecWebSocketAccept
) {
1042 static const char kIncorrectAcceptResponse
[] =
1043 "HTTP/1.1 101 Switching Protocols\r\n"
1044 "Upgrade: websocket\r\n"
1045 "Connection: Upgrade\r\n"
1046 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
1048 CreateAndConnectCustomResponse("ws://localhost/",
1053 kIncorrectAcceptResponse
);
1055 EXPECT_TRUE(has_failed());
1056 EXPECT_EQ("Error during WebSocket handshake: "
1057 "Incorrect 'Sec-WebSocket-Accept' header value",
1061 // Cancellation works.
1062 TEST_F(WebSocketStreamCreateTest
, Cancellation
) {
1063 CreateAndConnectStandard(
1064 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
1065 stream_request_
.reset();
1067 EXPECT_FALSE(has_failed());
1068 EXPECT_FALSE(stream_
);
1069 EXPECT_FALSE(request_info_
);
1070 EXPECT_FALSE(response_info_
);
1073 // Connect failure must look just like negotiation failure.
1074 TEST_F(WebSocketStreamCreateTest
, ConnectionFailure
) {
1075 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1076 socket_data
->set_connect_data(
1077 MockConnect(SYNCHRONOUS
, ERR_CONNECTION_REFUSED
));
1078 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1079 "http://localhost", socket_data
.Pass());
1081 EXPECT_TRUE(has_failed());
1082 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1084 EXPECT_FALSE(request_info_
);
1085 EXPECT_FALSE(response_info_
);
1088 // Connect timeout must look just like any other failure.
1089 TEST_F(WebSocketStreamCreateTest
, ConnectionTimeout
) {
1090 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1091 socket_data
->set_connect_data(
1092 MockConnect(ASYNC
, ERR_CONNECTION_TIMED_OUT
));
1093 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1094 "http://localhost", socket_data
.Pass());
1096 EXPECT_TRUE(has_failed());
1097 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
1101 // Cancellation during connect works.
1102 TEST_F(WebSocketStreamCreateTest
, CancellationDuringConnect
) {
1103 scoped_ptr
<DeterministicSocketData
> socket_data(BuildNullSocketData());
1104 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, ERR_IO_PENDING
));
1105 CreateAndConnectRawExpectations("ws://localhost/",
1108 socket_data
.Pass());
1109 stream_request_
.reset();
1111 EXPECT_FALSE(has_failed());
1112 EXPECT_FALSE(stream_
);
1115 // Cancellation during write of the request headers works.
1116 TEST_F(WebSocketStreamCreateTest
, CancellationDuringWrite
) {
1117 // We seem to need at least two operations in order to use SetStop().
1118 MockWrite writes
[] = {MockWrite(ASYNC
, 0, "GET / HTTP/"),
1119 MockWrite(ASYNC
, 1, "1.1\r\n")};
1120 // We keep a copy of the pointer so that we can call RunFor() on it later.
1121 DeterministicSocketData
* socket_data(
1122 new DeterministicSocketData(NULL
, 0, writes
, arraysize(writes
)));
1123 socket_data
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
1124 socket_data
->SetStop(1);
1125 CreateAndConnectRawExpectations("ws://localhost/",
1128 make_scoped_ptr(socket_data
));
1130 stream_request_
.reset();
1132 EXPECT_FALSE(has_failed());
1133 EXPECT_FALSE(stream_
);
1134 EXPECT_TRUE(request_info_
);
1135 EXPECT_FALSE(response_info_
);
1138 // Cancellation during read of the response headers works.
1139 TEST_F(WebSocketStreamCreateTest
, CancellationDuringRead
) {
1140 std::string request
= WebSocketStandardRequest("/", "http://localhost", "");
1141 MockWrite writes
[] = {MockWrite(ASYNC
, 0, request
.c_str())};
1142 MockRead reads
[] = {
1143 MockRead(ASYNC
, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
1145 scoped_ptr
<DeterministicSocketData
> socket_data(
1146 BuildSocketData(reads
, writes
));
1147 socket_data
->SetStop(1);
1148 DeterministicSocketData
* socket_data_raw_ptr
= socket_data
.get();
1149 CreateAndConnectRawExpectations("ws://localhost/",
1152 socket_data
.Pass());
1153 socket_data_raw_ptr
->Run();
1154 stream_request_
.reset();
1156 EXPECT_FALSE(has_failed());
1157 EXPECT_FALSE(stream_
);
1158 EXPECT_TRUE(request_info_
);
1159 EXPECT_FALSE(response_info_
);
1162 // Over-size response headers (> 256KB) should not cause a crash. This is a
1163 // regression test for crbug.com/339456. It is based on the layout test
1164 // "cookie-flood.html".
1165 TEST_F(WebSocketStreamCreateTest
, VeryLargeResponseHeaders
) {
1166 std::string set_cookie_headers
;
1167 set_cookie_headers
.reserve(45 * 10000);
1168 for (int i
= 0; i
< 10000; ++i
) {
1169 set_cookie_headers
+=
1170 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i
);
1172 CreateAndConnectStandard("ws://localhost/", "/", NoSubProtocols(),
1173 "http://localhost", "", set_cookie_headers
);
1175 EXPECT_TRUE(has_failed());
1176 EXPECT_FALSE(response_info_
);
1179 // If the remote host closes the connection without sending headers, we should
1180 // log the console message "Connection closed before receiving a handshake
1182 TEST_F(WebSocketStreamCreateTest
, NoResponse
) {
1183 std::string request
= WebSocketStandardRequest("/", "http://localhost", "");
1184 MockWrite writes
[] = {MockWrite(ASYNC
, request
.data(), request
.size(), 0)};
1185 MockRead reads
[] = {MockRead(ASYNC
, 0, 1)};
1186 scoped_ptr
<DeterministicSocketData
> socket_data(
1187 BuildSocketData(reads
, writes
));
1188 DeterministicSocketData
* socket_data_raw_ptr
= socket_data
.get();
1189 CreateAndConnectRawExpectations("ws://localhost/",
1192 socket_data
.Pass());
1193 socket_data_raw_ptr
->RunFor(2);
1194 EXPECT_TRUE(has_failed());
1195 EXPECT_FALSE(stream_
);
1196 EXPECT_FALSE(response_info_
);
1197 EXPECT_EQ("Connection closed before receiving a handshake response",
1201 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateFailure
) {
1202 ssl_data_
.push_back(
1203 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1204 ssl_data_
[0]->cert
=
1205 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1206 ASSERT_TRUE(ssl_data_
[0]->cert
.get());
1207 scoped_ptr
<DeterministicSocketData
> raw_socket_data(BuildNullSocketData());
1208 CreateAndConnectRawExpectations("wss://localhost/",
1211 raw_socket_data
.Pass());
1213 EXPECT_FALSE(has_failed());
1214 ASSERT_TRUE(ssl_error_callbacks_
);
1215 ssl_error_callbacks_
->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID
,
1218 EXPECT_TRUE(has_failed());
1221 TEST_F(WebSocketStreamCreateTest
, SelfSignedCertificateSuccess
) {
1222 scoped_ptr
<SSLSocketDataProvider
> ssl_data(
1223 new SSLSocketDataProvider(ASYNC
, ERR_CERT_AUTHORITY_INVALID
));
1225 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1226 ASSERT_TRUE(ssl_data
->cert
.get());
1227 ssl_data_
.push_back(ssl_data
.release());
1228 ssl_data
.reset(new SSLSocketDataProvider(ASYNC
, OK
));
1229 ssl_data_
.push_back(ssl_data
.release());
1230 url_request_context_host_
.AddRawExpectations(BuildNullSocketData());
1231 CreateAndConnectStandard(
1232 "wss://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
1234 ASSERT_TRUE(ssl_error_callbacks_
);
1235 ssl_error_callbacks_
->ContinueSSLRequest();
1237 EXPECT_FALSE(has_failed());
1238 EXPECT_TRUE(stream_
);
1241 // If the server requests authorisation, but we have no credentials, the
1242 // connection should fail cleanly.
1243 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureNoCredentials
) {
1244 CreateAndConnectCustomResponse("ws://localhost/",
1249 kUnauthorizedResponse
);
1251 EXPECT_TRUE(has_failed());
1252 EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1254 EXPECT_TRUE(response_info_
);
1257 TEST_F(WebSocketStreamCreateBasicAuthTest
, SuccessPasswordInUrl
) {
1258 CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
1260 WebSocketStandardResponse(std::string()));
1262 EXPECT_FALSE(has_failed());
1263 EXPECT_TRUE(stream_
);
1264 ASSERT_TRUE(response_info_
);
1265 EXPECT_EQ(101, response_info_
->status_code
);
1268 TEST_F(WebSocketStreamCreateBasicAuthTest
, FailureIncorrectPasswordInUrl
) {
1269 CreateAndConnectAuthHandshake(
1270 "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse
);
1272 EXPECT_TRUE(has_failed());
1273 EXPECT_TRUE(response_info_
);
1276 // Digest auth has the same connection semantics as Basic auth, so we can
1277 // generally assume that whatever works for Basic auth will also work for
1278 // Digest. There's just one test here, to confirm that it works at all.
1279 TEST_F(WebSocketStreamCreateDigestAuthTest
, DigestPasswordInUrl
) {
1280 AddRawExpectations(helper_
.BuildSocketData1(kUnauthorizedResponse
));
1282 CreateAndConnectRawExpectations(
1283 "ws://FooBar:pass@localhost/",
1286 helper_
.BuildSocketData2(kAuthorizedRequest
,
1287 WebSocketStandardResponse(std::string())));
1289 EXPECT_FALSE(has_failed());
1290 EXPECT_TRUE(stream_
);
1291 ASSERT_TRUE(response_info_
);
1292 EXPECT_EQ(101, response_info_
->status_code
);
1295 TEST_F(WebSocketStreamCreateUMATest
, Incomplete
) {
1296 const std::string
name("Net.WebSocket.HandshakeResult");
1297 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1300 StreamCreation creation
;
1301 creation
.CreateAndConnectStandard("ws://localhost/",
1303 creation
.NoSubProtocols(),
1309 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1310 ASSERT_TRUE(samples
);
1312 samples
->Subtract(*original
); // Cancel the original values.
1314 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1315 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1316 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1319 TEST_F(WebSocketStreamCreateUMATest
, Connected
) {
1320 const std::string
name("Net.WebSocket.HandshakeResult");
1321 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1324 StreamCreation creation
;
1325 creation
.CreateAndConnectStandard("ws://localhost/",
1327 creation
.NoSubProtocols(),
1331 creation
.RunUntilIdle();
1334 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1335 ASSERT_TRUE(samples
);
1337 samples
->Subtract(*original
); // Cancel the original values.
1339 EXPECT_EQ(0, samples
->GetCount(INCOMPLETE
));
1340 EXPECT_EQ(1, samples
->GetCount(CONNECTED
));
1341 EXPECT_EQ(0, samples
->GetCount(FAILED
));
1344 TEST_F(WebSocketStreamCreateUMATest
, Failed
) {
1345 const std::string
name("Net.WebSocket.HandshakeResult");
1346 scoped_ptr
<base::HistogramSamples
> original(GetSamples(name
));
1349 StreamCreation creation
;
1350 static const char kInvalidStatusCodeResponse
[] =
1351 "HTTP/1.1 200 OK\r\n"
1352 "Upgrade: websocket\r\n"
1353 "Connection: Upgrade\r\n"
1354 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1356 creation
.CreateAndConnectCustomResponse("ws://localhost/",
1358 creation
.NoSubProtocols(),
1361 kInvalidStatusCodeResponse
);
1362 creation
.RunUntilIdle();
1365 scoped_ptr
<base::HistogramSamples
> samples(GetSamples(name
));
1366 ASSERT_TRUE(samples
);
1368 samples
->Subtract(*original
); // Cancel the original values.
1370 EXPECT_EQ(1, samples
->GetCount(INCOMPLETE
));
1371 EXPECT_EQ(0, samples
->GetCount(CONNECTED
));
1372 EXPECT_EQ(0, samples
->GetCount(FAILED
));