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