Roll src/third_party/WebKit 3aa9dd9:183b2b0 (svn 201777:201778)
[chromium-blink-merge.git] / net / websockets / websocket_stream_test.cc
blob195002aa0b800754c89bcb171c83759cd0b3328f
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 static url::Origin LocalhostOrigin() {
71 return url::Origin(GURL("http://localhost/"));
74 static url::Origin GoogleOrigin() {
75 return url::Origin(GURL("http://google.com/"));
78 class WebSocketStreamCreateTest : public ::testing::Test,
79 public WebSocketStreamCreateTestBase {
80 public:
81 ~WebSocketStreamCreateTest() override {
82 // Permit any endpoint locks to be released.
83 stream_request_.reset();
84 stream_.reset();
85 base::RunLoop().RunUntilIdle();
88 void CreateAndConnectCustomResponse(
89 const std::string& socket_url,
90 const std::string& socket_host,
91 const std::string& socket_path,
92 const std::vector<std::string>& sub_protocols,
93 const url::Origin& origin,
94 const std::string& extra_request_headers,
95 const std::string& response_body,
96 scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
97 url_request_context_host_.SetExpectations(
98 WebSocketStandardRequest(socket_path, socket_host, origin,
99 extra_request_headers),
100 response_body);
101 CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
104 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
105 // errors like "Unable to perform synchronous IO while stopped" will occur.
106 void CreateAndConnectStandard(
107 const std::string& socket_url,
108 const std::string& socket_host,
109 const std::string& socket_path,
110 const std::vector<std::string>& sub_protocols,
111 const url::Origin& origin,
112 const std::string& extra_request_headers,
113 const std::string& extra_response_headers,
114 scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
115 CreateAndConnectCustomResponse(
116 socket_url, socket_host, socket_path, sub_protocols, origin,
117 extra_request_headers,
118 WebSocketStandardResponse(extra_response_headers), timer.Pass());
121 void CreateAndConnectRawExpectations(
122 const std::string& socket_url,
123 const std::vector<std::string>& sub_protocols,
124 const url::Origin& origin,
125 scoped_ptr<SequencedSocketData> socket_data,
126 scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
127 AddRawExpectations(socket_data.Pass());
128 CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
131 // Add additional raw expectations for sockets created before the final one.
132 void AddRawExpectations(scoped_ptr<SequencedSocketData> socket_data) {
133 url_request_context_host_.AddRawExpectations(socket_data.Pass());
137 // There are enough tests of the Sec-WebSocket-Extensions header that they
138 // deserve their own test fixture.
139 class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest {
140 public:
141 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
142 // header in the response set to |extensions_header_value|. Runs the event
143 // loop to allow the connect to complete.
144 void CreateAndConnectWithExtensions(
145 const std::string& extensions_header_value) {
146 CreateAndConnectStandard(
147 "ws://localhost/testing_path", "localhost", "/testing_path",
148 NoSubProtocols(), LocalhostOrigin(), "",
149 "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n");
150 WaitUntilConnectDone();
154 // Common code to construct expectations for authentication tests that receive
155 // the auth challenge on one connection and then create a second connection to
156 // send the authenticated request on.
157 class CommonAuthTestHelper {
158 public:
159 CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
161 scoped_ptr<SequencedSocketData> BuildSocketData1(
162 const std::string& response) {
163 request1_ =
164 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
165 writes1_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
166 response1_ = response;
167 reads1_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
168 reads1_[1] = MockRead(SYNCHRONOUS, OK, 2); // Close connection
170 return BuildSocketData(reads1_, writes1_);
173 scoped_ptr<SequencedSocketData> BuildSocketData2(
174 const std::string& request,
175 const std::string& response) {
176 request2_ = request;
177 response2_ = response;
178 writes2_[0] = MockWrite(SYNCHRONOUS, 0, request2_.c_str());
179 reads2_[0] = MockRead(SYNCHRONOUS, 1, response2_.c_str());
180 return BuildSocketData(reads2_, writes2_);
183 private:
184 // These need to be object-scoped since they have to remain valid until all
185 // socket operations in the test are complete.
186 std::string request1_;
187 std::string request2_;
188 std::string response1_;
189 std::string response2_;
190 MockRead reads1_[2];
191 MockWrite writes1_[1];
192 MockRead reads2_[1];
193 MockWrite writes2_[1];
195 DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper);
198 // Data and methods for BasicAuth tests.
199 class WebSocketStreamCreateBasicAuthTest : public WebSocketStreamCreateTest {
200 protected:
201 void CreateAndConnectAuthHandshake(const std::string& url,
202 const std::string& base64_user_pass,
203 const std::string& response2) {
204 AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
206 static const char request2format[] =
207 "GET / HTTP/1.1\r\n"
208 "Host: localhost\r\n"
209 "Connection: Upgrade\r\n"
210 "Pragma: no-cache\r\n"
211 "Cache-Control: no-cache\r\n"
212 "Authorization: Basic %s\r\n"
213 "Upgrade: websocket\r\n"
214 "Origin: http://localhost\r\n"
215 "Sec-WebSocket-Version: 13\r\n"
216 "User-Agent:\r\n"
217 "Accept-Encoding: gzip, deflate\r\n"
218 "Accept-Language: en-us,fr\r\n"
219 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
220 "Sec-WebSocket-Extensions: permessage-deflate; "
221 "client_max_window_bits\r\n"
222 "\r\n";
223 const std::string request =
224 base::StringPrintf(request2format, base64_user_pass.c_str());
225 CreateAndConnectRawExpectations(
226 url, NoSubProtocols(), LocalhostOrigin(),
227 helper_.BuildSocketData2(request, response2));
230 static const char kUnauthorizedResponse[];
232 CommonAuthTestHelper helper_;
235 class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {
236 protected:
237 static const char kUnauthorizedResponse[];
238 static const char kAuthorizedRequest[];
240 CommonAuthTestHelper helper_;
243 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse[] =
244 "HTTP/1.1 401 Unauthorized\r\n"
245 "Content-Length: 0\r\n"
246 "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
247 "\r\n";
249 // These negotiation values are borrowed from
250 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
251 // you are bored. Only the weakest (no qop) variants of Digest authentication
252 // can be tested by this method, because the others involve random input.
253 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse[] =
254 "HTTP/1.1 401 Unauthorized\r\n"
255 "Content-Length: 0\r\n"
256 "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
257 "\r\n";
259 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest[] =
260 "GET / HTTP/1.1\r\n"
261 "Host: localhost\r\n"
262 "Connection: Upgrade\r\n"
263 "Pragma: no-cache\r\n"
264 "Cache-Control: no-cache\r\n"
265 "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
266 "nonce=\"nonce-value\", uri=\"/\", "
267 "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
268 "Upgrade: websocket\r\n"
269 "Origin: http://localhost\r\n"
270 "Sec-WebSocket-Version: 13\r\n"
271 "User-Agent:\r\n"
272 "Accept-Encoding: gzip, deflate\r\n"
273 "Accept-Language: en-us,fr\r\n"
274 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
275 "Sec-WebSocket-Extensions: permessage-deflate; "
276 "client_max_window_bits\r\n"
277 "\r\n";
279 class WebSocketStreamCreateUMATest : public ::testing::Test {
280 public:
281 // This enum should match with the enum in Delegate in websocket_stream.cc.
282 enum HandshakeResult {
283 INCOMPLETE,
284 CONNECTED,
285 FAILED,
286 NUM_HANDSHAKE_RESULT_TYPES,
289 class StreamCreation : public WebSocketStreamCreateTest {
290 void TestBody() override {}
293 scoped_ptr<base::HistogramSamples> GetSamples(const std::string& name) {
294 base::HistogramBase* histogram =
295 base::StatisticsRecorder::FindHistogram(name);
296 return histogram ? histogram->SnapshotSamples()
297 : scoped_ptr<base::HistogramSamples>();
301 // Confirm that the basic case works as expected.
302 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
303 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
304 NoSubProtocols(), LocalhostOrigin(), "", "");
305 EXPECT_FALSE(request_info_);
306 EXPECT_FALSE(response_info_);
307 WaitUntilConnectDone();
308 EXPECT_FALSE(has_failed());
309 EXPECT_TRUE(stream_);
310 EXPECT_TRUE(request_info_);
311 EXPECT_TRUE(response_info_);
314 TEST_F(WebSocketStreamCreateTest, HandshakeInfo) {
315 static const char kResponse[] =
316 "HTTP/1.1 101 Switching Protocols\r\n"
317 "Upgrade: websocket\r\n"
318 "Connection: Upgrade\r\n"
319 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
320 "foo: bar, baz\r\n"
321 "hoge: fuga\r\n"
322 "hoge: piyo\r\n"
323 "\r\n";
325 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
326 NoSubProtocols(), LocalhostOrigin(), "",
327 kResponse);
328 EXPECT_FALSE(request_info_);
329 EXPECT_FALSE(response_info_);
330 WaitUntilConnectDone();
331 EXPECT_TRUE(stream_);
332 ASSERT_TRUE(request_info_);
333 ASSERT_TRUE(response_info_);
334 std::vector<HeaderKeyValuePair> request_headers =
335 RequestHeadersToVector(request_info_->headers);
336 // We examine the contents of request_info_ and response_info_
337 // mainly only in this test case.
338 EXPECT_EQ(GURL("ws://localhost/"), request_info_->url);
339 EXPECT_EQ(GURL("ws://localhost/"), response_info_->url);
340 EXPECT_EQ(101, response_info_->status_code);
341 EXPECT_EQ("Switching Protocols", response_info_->status_text);
342 ASSERT_EQ(12u, request_headers.size());
343 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]);
344 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
345 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
346 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
347 request_headers[3]);
348 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
349 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
350 request_headers[5]);
351 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
352 request_headers[6]);
353 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
354 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
355 request_headers[8]);
356 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
357 request_headers[9]);
358 EXPECT_EQ("Sec-WebSocket-Key", request_headers[10].first);
359 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
360 "permessage-deflate; client_max_window_bits"),
361 request_headers[11]);
363 std::vector<HeaderKeyValuePair> response_headers =
364 ResponseHeadersToVector(*response_info_->headers.get());
365 ASSERT_EQ(6u, response_headers.size());
366 // Sort the headers for ease of verification.
367 std::sort(response_headers.begin(), response_headers.end());
369 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
370 EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
371 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
372 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
373 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
374 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
377 // Confirm that the stream isn't established until the message loop runs.
378 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
379 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
380 NoSubProtocols(), LocalhostOrigin(), "", "");
381 EXPECT_FALSE(has_failed());
382 EXPECT_FALSE(stream_);
385 // Check the path is used.
386 TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
387 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
388 "/testing_path", NoSubProtocols(), LocalhostOrigin(),
389 "", "");
390 WaitUntilConnectDone();
391 EXPECT_FALSE(has_failed());
392 EXPECT_TRUE(stream_);
395 // Check that the origin is used.
396 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
397 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
398 "/testing_path", NoSubProtocols(), GoogleOrigin(),
399 "", "");
400 WaitUntilConnectDone();
401 EXPECT_FALSE(has_failed());
402 EXPECT_TRUE(stream_);
405 // Check that sub-protocols are sent and parsed.
406 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
407 std::vector<std::string> sub_protocols;
408 sub_protocols.push_back("chatv11.chromium.org");
409 sub_protocols.push_back("chatv20.chromium.org");
410 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
411 "/testing_path", sub_protocols, GoogleOrigin(),
412 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
413 "chatv20.chromium.org\r\n",
414 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
415 WaitUntilConnectDone();
416 EXPECT_TRUE(stream_);
417 EXPECT_FALSE(has_failed());
418 EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
421 // Unsolicited sub-protocols are rejected.
422 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
423 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
424 "/testing_path", NoSubProtocols(), GoogleOrigin(),
426 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
427 WaitUntilConnectDone();
428 EXPECT_FALSE(stream_);
429 EXPECT_TRUE(has_failed());
430 EXPECT_EQ("Error during WebSocket handshake: "
431 "Response must not include 'Sec-WebSocket-Protocol' header "
432 "if not present in request: chatv20.chromium.org",
433 failure_message());
436 // Missing sub-protocol response is rejected.
437 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
438 std::vector<std::string> sub_protocols;
439 sub_protocols.push_back("chat.example.com");
440 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
441 "/testing_path", sub_protocols, LocalhostOrigin(),
442 "Sec-WebSocket-Protocol: chat.example.com\r\n", "");
443 WaitUntilConnectDone();
444 EXPECT_FALSE(stream_);
445 EXPECT_TRUE(has_failed());
446 EXPECT_EQ("Error during WebSocket handshake: "
447 "Sent non-empty 'Sec-WebSocket-Protocol' header "
448 "but no response was received",
449 failure_message());
452 // Only one sub-protocol can be accepted.
453 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
454 std::vector<std::string> sub_protocols;
455 sub_protocols.push_back("chatv11.chromium.org");
456 sub_protocols.push_back("chatv20.chromium.org");
457 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
458 "/testing_path", sub_protocols, GoogleOrigin(),
459 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
460 "chatv20.chromium.org\r\n",
461 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
462 "chatv20.chromium.org\r\n");
463 WaitUntilConnectDone();
464 EXPECT_FALSE(stream_);
465 EXPECT_TRUE(has_failed());
466 EXPECT_EQ("Error during WebSocket handshake: "
467 "'Sec-WebSocket-Protocol' header must not appear "
468 "more than once in a response",
469 failure_message());
472 // Unmatched sub-protocol should be rejected.
473 TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) {
474 std::vector<std::string> sub_protocols;
475 sub_protocols.push_back("chatv11.chromium.org");
476 sub_protocols.push_back("chatv20.chromium.org");
477 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
478 "/testing_path", sub_protocols, GoogleOrigin(),
479 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
480 "chatv20.chromium.org\r\n",
481 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
482 WaitUntilConnectDone();
483 EXPECT_FALSE(stream_);
484 EXPECT_TRUE(has_failed());
485 EXPECT_EQ("Error during WebSocket handshake: "
486 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
487 "in response does not match any of sent values",
488 failure_message());
491 // permessage-deflate extension basic success case.
492 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
493 CreateAndConnectWithExtensions("permessage-deflate");
494 EXPECT_TRUE(stream_);
495 EXPECT_FALSE(has_failed());
498 // permessage-deflate extensions success with all parameters.
499 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
500 CreateAndConnectWithExtensions(
501 "permessage-deflate; client_no_context_takeover; "
502 "server_max_window_bits=11; client_max_window_bits=13; "
503 "server_no_context_takeover");
504 EXPECT_TRUE(stream_);
505 EXPECT_FALSE(has_failed());
508 // Verify that incoming messages are actually decompressed with
509 // permessage-deflate enabled.
510 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
511 CreateAndConnectCustomResponse(
512 "ws://localhost/testing_path", "localhost", "/testing_path",
513 NoSubProtocols(), LocalhostOrigin(), "",
514 WebSocketStandardResponse(
515 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
516 std::string(
517 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
518 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
519 9));
520 WaitUntilConnectDone();
522 ASSERT_TRUE(stream_);
523 ScopedVector<WebSocketFrame> frames;
524 CompletionCallback callback;
525 ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback));
526 ASSERT_EQ(1U, frames.size());
527 ASSERT_EQ(5U, frames[0]->header.payload_length);
528 EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5));
531 // Unknown extension in the response is rejected
532 TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) {
533 CreateAndConnectWithExtensions("x-unknown-extension");
534 EXPECT_FALSE(stream_);
535 EXPECT_TRUE(has_failed());
536 EXPECT_EQ("Error during WebSocket handshake: "
537 "Found an unsupported extension 'x-unknown-extension' "
538 "in 'Sec-WebSocket-Extensions' header",
539 failure_message());
542 // Malformed extensions are rejected (this file does not cover all possible
543 // parse failures, as the parser is covered thoroughly by its own unit tests).
544 TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) {
545 CreateAndConnectWithExtensions(";");
546 EXPECT_FALSE(stream_);
547 EXPECT_TRUE(has_failed());
548 EXPECT_EQ(
549 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
550 "value is rejected by the parser: ;",
551 failure_message());
554 // The permessage-deflate extension may only be specified once.
555 TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
556 CreateAndConnectWithExtensions(
557 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
558 EXPECT_FALSE(stream_);
559 EXPECT_TRUE(has_failed());
560 EXPECT_EQ(
561 "Error during WebSocket handshake: "
562 "Received duplicate permessage-deflate response",
563 failure_message());
566 // permessage-deflate parameters may not be duplicated.
567 TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) {
568 CreateAndConnectWithExtensions(
569 "permessage-deflate; client_no_context_takeover; "
570 "client_no_context_takeover");
571 EXPECT_FALSE(stream_);
572 EXPECT_TRUE(has_failed());
573 EXPECT_EQ(
574 "Error during WebSocket handshake: Error in permessage-deflate: "
575 "Received duplicate permessage-deflate extension parameter "
576 "client_no_context_takeover",
577 failure_message());
580 // permessage-deflate parameters must start with "client_" or "server_"
581 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) {
582 CreateAndConnectWithExtensions(
583 "permessage-deflate; absurd_no_context_takeover");
584 EXPECT_FALSE(stream_);
585 EXPECT_TRUE(has_failed());
586 EXPECT_EQ(
587 "Error during WebSocket handshake: Error in permessage-deflate: "
588 "Received an unexpected permessage-deflate extension parameter",
589 failure_message());
592 // permessage-deflate parameters must be either *_no_context_takeover or
593 // *_max_window_bits
594 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) {
595 CreateAndConnectWithExtensions(
596 "permessage-deflate; client_max_content_bits=5");
597 EXPECT_FALSE(stream_);
598 EXPECT_TRUE(has_failed());
599 EXPECT_EQ(
600 "Error during WebSocket handshake: Error in permessage-deflate: "
601 "Received an unexpected permessage-deflate extension parameter",
602 failure_message());
605 // *_no_context_takeover parameters must not have an argument
606 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) {
607 CreateAndConnectWithExtensions(
608 "permessage-deflate; client_no_context_takeover=true");
609 EXPECT_FALSE(stream_);
610 EXPECT_TRUE(has_failed());
611 EXPECT_EQ(
612 "Error during WebSocket handshake: Error in permessage-deflate: "
613 "Received invalid client_no_context_takeover parameter",
614 failure_message());
617 // *_max_window_bits must have an argument
618 TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
619 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
620 EXPECT_FALSE(stream_);
621 EXPECT_TRUE(has_failed());
622 EXPECT_EQ(
623 "Error during WebSocket handshake: Error in permessage-deflate: "
624 "client_max_window_bits must have value",
625 failure_message());
628 // *_max_window_bits must be an integer
629 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) {
630 CreateAndConnectWithExtensions(
631 "permessage-deflate; server_max_window_bits=banana");
632 EXPECT_FALSE(stream_);
633 EXPECT_TRUE(has_failed());
634 EXPECT_EQ(
635 "Error during WebSocket handshake: Error in permessage-deflate: "
636 "Received invalid server_max_window_bits parameter",
637 failure_message());
640 // *_max_window_bits must be >= 8
641 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) {
642 CreateAndConnectWithExtensions(
643 "permessage-deflate; server_max_window_bits=7");
644 EXPECT_FALSE(stream_);
645 EXPECT_TRUE(has_failed());
646 EXPECT_EQ(
647 "Error during WebSocket handshake: Error in permessage-deflate: "
648 "Received invalid server_max_window_bits parameter",
649 failure_message());
652 // *_max_window_bits must be <= 15
653 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) {
654 CreateAndConnectWithExtensions(
655 "permessage-deflate; client_max_window_bits=16");
656 EXPECT_FALSE(stream_);
657 EXPECT_TRUE(has_failed());
658 EXPECT_EQ(
659 "Error during WebSocket handshake: Error in permessage-deflate: "
660 "Received invalid client_max_window_bits parameter",
661 failure_message());
664 // *_max_window_bits must not start with 0
665 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) {
666 CreateAndConnectWithExtensions(
667 "permessage-deflate; client_max_window_bits=08");
668 EXPECT_FALSE(stream_);
669 EXPECT_TRUE(has_failed());
670 EXPECT_EQ(
671 "Error during WebSocket handshake: Error in permessage-deflate: "
672 "Received invalid client_max_window_bits parameter",
673 failure_message());
676 // *_max_window_bits must not start with +
677 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) {
678 CreateAndConnectWithExtensions(
679 "permessage-deflate; server_max_window_bits=+9");
680 EXPECT_FALSE(stream_);
681 EXPECT_TRUE(has_failed());
682 EXPECT_EQ(
683 "Error during WebSocket handshake: Error in permessage-deflate: "
684 "Received invalid server_max_window_bits parameter",
685 failure_message());
688 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
689 // arguments from the server. This is difficult because the data written to the
690 // socket is randomly masked.
692 // Additional Sec-WebSocket-Accept headers should be rejected.
693 TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
694 CreateAndConnectStandard(
695 "ws://localhost/", "localhost", "/", NoSubProtocols(), LocalhostOrigin(),
696 "", "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
697 WaitUntilConnectDone();
698 EXPECT_FALSE(stream_);
699 EXPECT_TRUE(has_failed());
700 EXPECT_EQ("Error during WebSocket handshake: "
701 "'Sec-WebSocket-Accept' header must not appear "
702 "more than once in a response",
703 failure_message());
706 // Response code 200 must be rejected.
707 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
708 static const char kInvalidStatusCodeResponse[] =
709 "HTTP/1.1 200 OK\r\n"
710 "Upgrade: websocket\r\n"
711 "Connection: Upgrade\r\n"
712 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
713 "\r\n";
714 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
715 NoSubProtocols(), LocalhostOrigin(), "",
716 kInvalidStatusCodeResponse);
717 WaitUntilConnectDone();
718 EXPECT_TRUE(has_failed());
719 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
720 failure_message());
723 // Redirects are not followed (according to the WHATWG WebSocket API, which
724 // overrides RFC6455 for browser applications).
725 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
726 static const char kRedirectResponse[] =
727 "HTTP/1.1 302 Moved Temporarily\r\n"
728 "Content-Type: text/html\r\n"
729 "Content-Length: 34\r\n"
730 "Connection: keep-alive\r\n"
731 "Location: ws://localhost/other\r\n"
732 "\r\n"
733 "<title>Moved</title><h1>Moved</h1>";
734 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
735 NoSubProtocols(), LocalhostOrigin(), "",
736 kRedirectResponse);
737 WaitUntilConnectDone();
738 EXPECT_TRUE(has_failed());
739 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
740 failure_message());
743 // Malformed responses should be rejected. HttpStreamParser will accept just
744 // about any garbage in the middle of the headers. To make it give up, the junk
745 // has to be at the start of the response. Even then, it just gets treated as an
746 // HTTP/0.9 response.
747 TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
748 static const char kMalformedResponse[] =
749 "220 mx.google.com ESMTP\r\n"
750 "HTTP/1.1 101 OK\r\n"
751 "Upgrade: websocket\r\n"
752 "Connection: Upgrade\r\n"
753 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
754 "\r\n";
755 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
756 NoSubProtocols(), LocalhostOrigin(), "",
757 kMalformedResponse);
758 WaitUntilConnectDone();
759 EXPECT_TRUE(has_failed());
760 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
761 failure_message());
764 // Upgrade header must be present.
765 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
766 static const char kMissingUpgradeResponse[] =
767 "HTTP/1.1 101 Switching Protocols\r\n"
768 "Connection: Upgrade\r\n"
769 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
770 "\r\n";
771 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
772 NoSubProtocols(), LocalhostOrigin(), "",
773 kMissingUpgradeResponse);
774 WaitUntilConnectDone();
775 EXPECT_TRUE(has_failed());
776 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
777 failure_message());
780 // There must only be one upgrade header.
781 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
782 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
783 NoSubProtocols(), LocalhostOrigin(), "",
784 "Upgrade: HTTP/2.0\r\n");
785 WaitUntilConnectDone();
786 EXPECT_TRUE(has_failed());
787 EXPECT_EQ("Error during WebSocket handshake: "
788 "'Upgrade' header must not appear more than once in a response",
789 failure_message());
792 // There must only be one correct upgrade header.
793 TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
794 static const char kMissingUpgradeResponse[] =
795 "HTTP/1.1 101 Switching Protocols\r\n"
796 "Connection: Upgrade\r\n"
797 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
798 "Upgrade: hogefuga\r\n"
799 "\r\n";
800 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
801 NoSubProtocols(), LocalhostOrigin(), "",
802 kMissingUpgradeResponse);
803 WaitUntilConnectDone();
804 EXPECT_TRUE(has_failed());
805 EXPECT_EQ("Error during WebSocket handshake: "
806 "'Upgrade' header value is not 'WebSocket': hogefuga",
807 failure_message());
810 // Connection header must be present.
811 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
812 static const char kMissingConnectionResponse[] =
813 "HTTP/1.1 101 Switching Protocols\r\n"
814 "Upgrade: websocket\r\n"
815 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
816 "\r\n";
817 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
818 NoSubProtocols(), LocalhostOrigin(), "",
819 kMissingConnectionResponse);
820 WaitUntilConnectDone();
821 EXPECT_TRUE(has_failed());
822 EXPECT_EQ("Error during WebSocket handshake: "
823 "'Connection' header is missing",
824 failure_message());
827 // Connection header must contain "Upgrade".
828 TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
829 static const char kMissingConnectionResponse[] =
830 "HTTP/1.1 101 Switching Protocols\r\n"
831 "Upgrade: websocket\r\n"
832 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
833 "Connection: hogefuga\r\n"
834 "\r\n";
835 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
836 NoSubProtocols(), LocalhostOrigin(), "",
837 kMissingConnectionResponse);
838 WaitUntilConnectDone();
839 EXPECT_TRUE(has_failed());
840 EXPECT_EQ("Error during WebSocket handshake: "
841 "'Connection' header value must contain 'Upgrade'",
842 failure_message());
845 // Connection header is permitted to contain other tokens.
846 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
847 static const char kAdditionalConnectionTokenResponse[] =
848 "HTTP/1.1 101 Switching Protocols\r\n"
849 "Upgrade: websocket\r\n"
850 "Connection: Upgrade, Keep-Alive\r\n"
851 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
852 "\r\n";
853 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
854 NoSubProtocols(), LocalhostOrigin(), "",
855 kAdditionalConnectionTokenResponse);
856 WaitUntilConnectDone();
857 EXPECT_FALSE(has_failed());
858 EXPECT_TRUE(stream_);
861 // Sec-WebSocket-Accept header must be present.
862 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
863 static const char kMissingAcceptResponse[] =
864 "HTTP/1.1 101 Switching Protocols\r\n"
865 "Upgrade: websocket\r\n"
866 "Connection: Upgrade\r\n"
867 "\r\n";
868 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
869 NoSubProtocols(), LocalhostOrigin(), "",
870 kMissingAcceptResponse);
871 WaitUntilConnectDone();
872 EXPECT_TRUE(has_failed());
873 EXPECT_EQ("Error during WebSocket handshake: "
874 "'Sec-WebSocket-Accept' header is missing",
875 failure_message());
878 // Sec-WebSocket-Accept header must match the key that was sent.
879 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
880 static const char kIncorrectAcceptResponse[] =
881 "HTTP/1.1 101 Switching Protocols\r\n"
882 "Upgrade: websocket\r\n"
883 "Connection: Upgrade\r\n"
884 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
885 "\r\n";
886 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
887 NoSubProtocols(), LocalhostOrigin(), "",
888 kIncorrectAcceptResponse);
889 WaitUntilConnectDone();
890 EXPECT_TRUE(has_failed());
891 EXPECT_EQ("Error during WebSocket handshake: "
892 "Incorrect 'Sec-WebSocket-Accept' header value",
893 failure_message());
896 // Cancellation works.
897 TEST_F(WebSocketStreamCreateTest, Cancellation) {
898 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
899 NoSubProtocols(), LocalhostOrigin(), "", "");
900 stream_request_.reset();
901 // WaitUntilConnectDone doesn't work in this case.
902 base::RunLoop().RunUntilIdle();
903 EXPECT_FALSE(has_failed());
904 EXPECT_FALSE(stream_);
905 EXPECT_FALSE(request_info_);
906 EXPECT_FALSE(response_info_);
909 // Connect failure must look just like negotiation failure.
910 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
911 scoped_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
912 socket_data->set_connect_data(
913 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
914 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
915 LocalhostOrigin(), socket_data.Pass());
916 WaitUntilConnectDone();
917 EXPECT_TRUE(has_failed());
918 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
919 failure_message());
920 EXPECT_FALSE(request_info_);
921 EXPECT_FALSE(response_info_);
924 // Connect timeout must look just like any other failure.
925 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
926 scoped_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
927 socket_data->set_connect_data(
928 MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
929 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
930 LocalhostOrigin(), socket_data.Pass());
931 WaitUntilConnectDone();
932 EXPECT_TRUE(has_failed());
933 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
934 failure_message());
937 // The server doesn't respond to the opening handshake.
938 TEST_F(WebSocketStreamCreateTest, HandshakeTimeout) {
939 scoped_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
940 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
941 scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
942 base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
943 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
944 LocalhostOrigin(), socket_data.Pass(),
945 timer.Pass());
946 EXPECT_FALSE(has_failed());
947 ASSERT_TRUE(weak_timer.get());
948 EXPECT_TRUE(weak_timer->IsRunning());
950 weak_timer->Fire();
951 WaitUntilConnectDone();
953 EXPECT_TRUE(has_failed());
954 EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
955 ASSERT_TRUE(weak_timer.get());
956 EXPECT_FALSE(weak_timer->IsRunning());
959 // When the connection establishes the timer should be stopped.
960 TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnSuccess) {
961 scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
962 base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
964 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
965 NoSubProtocols(), LocalhostOrigin(), "", "",
966 timer.Pass());
967 ASSERT_TRUE(weak_timer);
968 EXPECT_TRUE(weak_timer->IsRunning());
970 WaitUntilConnectDone();
971 EXPECT_FALSE(has_failed());
972 EXPECT_TRUE(stream_);
973 ASSERT_TRUE(weak_timer);
974 EXPECT_FALSE(weak_timer->IsRunning());
977 // When the connection fails the timer should be stopped.
978 TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnFailure) {
979 scoped_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
980 socket_data->set_connect_data(
981 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
982 scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
983 base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
984 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
985 LocalhostOrigin(), socket_data.Pass(),
986 timer.Pass());
987 ASSERT_TRUE(weak_timer.get());
988 EXPECT_TRUE(weak_timer->IsRunning());
990 WaitUntilConnectDone();
991 EXPECT_TRUE(has_failed());
992 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
993 failure_message());
994 ASSERT_TRUE(weak_timer.get());
995 EXPECT_FALSE(weak_timer->IsRunning());
998 // Cancellation during connect works.
999 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
1000 scoped_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
1001 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
1002 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1003 LocalhostOrigin(), 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/", NoSubProtocols(),
1019 LocalhostOrigin(),
1020 make_scoped_ptr(socket_data));
1021 base::RunLoop().RunUntilIdle();
1022 EXPECT_TRUE(socket_data->AllWriteDataConsumed());
1023 stream_request_.reset();
1024 // WaitUntilConnectDone doesn't work in this case.
1025 base::RunLoop().RunUntilIdle();
1026 EXPECT_FALSE(has_failed());
1027 EXPECT_FALSE(stream_);
1028 EXPECT_TRUE(request_info_);
1029 EXPECT_FALSE(response_info_);
1032 // Cancellation during read of the response headers works.
1033 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
1034 std::string request =
1035 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
1036 MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
1037 MockRead reads[] = {
1038 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 1),
1040 scoped_ptr<SequencedSocketData> socket_data(BuildSocketData(reads, writes));
1041 SequencedSocketData* socket_data_raw_ptr = socket_data.get();
1042 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1043 LocalhostOrigin(), socket_data.Pass());
1044 base::RunLoop().RunUntilIdle();
1045 EXPECT_TRUE(socket_data_raw_ptr->AllReadDataConsumed());
1046 stream_request_.reset();
1047 // WaitUntilConnectDone doesn't work in this case.
1048 base::RunLoop().RunUntilIdle();
1049 EXPECT_FALSE(has_failed());
1050 EXPECT_FALSE(stream_);
1051 EXPECT_TRUE(request_info_);
1052 EXPECT_FALSE(response_info_);
1055 // Over-size response headers (> 256KB) should not cause a crash. This is a
1056 // regression test for crbug.com/339456. It is based on the layout test
1057 // "cookie-flood.html".
1058 TEST_F(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
1059 std::string set_cookie_headers;
1060 set_cookie_headers.reserve(45 * 10000);
1061 for (int i = 0; i < 10000; ++i) {
1062 set_cookie_headers +=
1063 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i);
1065 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1066 NoSubProtocols(), LocalhostOrigin(), "",
1067 set_cookie_headers);
1068 WaitUntilConnectDone();
1069 EXPECT_TRUE(has_failed());
1070 EXPECT_FALSE(response_info_);
1073 // If the remote host closes the connection without sending headers, we should
1074 // log the console message "Connection closed before receiving a handshake
1075 // response".
1076 TEST_F(WebSocketStreamCreateTest, NoResponse) {
1077 std::string request =
1078 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
1079 MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
1080 MockRead reads[] = {MockRead(ASYNC, 0, 1)};
1081 scoped_ptr<SequencedSocketData> socket_data(BuildSocketData(reads, writes));
1082 SequencedSocketData* socket_data_raw_ptr = socket_data.get();
1083 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1084 LocalhostOrigin(), socket_data.Pass());
1085 base::RunLoop().RunUntilIdle();
1086 EXPECT_TRUE(socket_data_raw_ptr->AllReadDataConsumed());
1087 EXPECT_TRUE(has_failed());
1088 EXPECT_FALSE(stream_);
1089 EXPECT_FALSE(response_info_);
1090 EXPECT_EQ("Connection closed before receiving a handshake response",
1091 failure_message());
1094 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
1095 ssl_data_.push_back(
1096 new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
1097 ssl_data_[0]->cert =
1098 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1099 ASSERT_TRUE(ssl_data_[0]->cert.get());
1100 scoped_ptr<SequencedSocketData> raw_socket_data(BuildNullSocketData());
1101 CreateAndConnectRawExpectations("wss://localhost/", NoSubProtocols(),
1102 LocalhostOrigin(), raw_socket_data.Pass());
1103 // WaitUntilConnectDone doesn't work in this case.
1104 base::RunLoop().RunUntilIdle();
1105 EXPECT_FALSE(has_failed());
1106 ASSERT_TRUE(ssl_error_callbacks_);
1107 ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
1108 &ssl_info_);
1109 WaitUntilConnectDone();
1110 EXPECT_TRUE(has_failed());
1113 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
1114 scoped_ptr<SSLSocketDataProvider> ssl_data(
1115 new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
1116 ssl_data->cert =
1117 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1118 ASSERT_TRUE(ssl_data->cert.get());
1119 ssl_data_.push_back(ssl_data.Pass());
1120 ssl_data.reset(new SSLSocketDataProvider(ASYNC, OK));
1121 ssl_data_.push_back(ssl_data.Pass());
1122 url_request_context_host_.AddRawExpectations(BuildNullSocketData());
1123 CreateAndConnectStandard("wss://localhost/", "localhost", "/",
1124 NoSubProtocols(), LocalhostOrigin(), "", "");
1125 // WaitUntilConnectDone doesn't work in this case.
1126 base::RunLoop().RunUntilIdle();
1127 ASSERT_TRUE(ssl_error_callbacks_);
1128 ssl_error_callbacks_->ContinueSSLRequest();
1129 WaitUntilConnectDone();
1130 EXPECT_FALSE(has_failed());
1131 EXPECT_TRUE(stream_);
1134 // If the server requests authorisation, but we have no credentials, the
1135 // connection should fail cleanly.
1136 TEST_F(WebSocketStreamCreateBasicAuthTest, FailureNoCredentials) {
1137 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
1138 NoSubProtocols(), LocalhostOrigin(), "",
1139 kUnauthorizedResponse);
1140 WaitUntilConnectDone();
1141 EXPECT_TRUE(has_failed());
1142 EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1143 failure_message());
1144 EXPECT_TRUE(response_info_);
1147 TEST_F(WebSocketStreamCreateBasicAuthTest, SuccessPasswordInUrl) {
1148 CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
1149 "Zm9vOmJhcg==",
1150 WebSocketStandardResponse(std::string()));
1151 WaitUntilConnectDone();
1152 EXPECT_FALSE(has_failed());
1153 EXPECT_TRUE(stream_);
1154 ASSERT_TRUE(response_info_);
1155 EXPECT_EQ(101, response_info_->status_code);
1158 TEST_F(WebSocketStreamCreateBasicAuthTest, FailureIncorrectPasswordInUrl) {
1159 CreateAndConnectAuthHandshake(
1160 "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse);
1161 WaitUntilConnectDone();
1162 EXPECT_TRUE(has_failed());
1163 EXPECT_TRUE(response_info_);
1166 // Digest auth has the same connection semantics as Basic auth, so we can
1167 // generally assume that whatever works for Basic auth will also work for
1168 // Digest. There's just one test here, to confirm that it works at all.
1169 TEST_F(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {
1170 AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
1172 CreateAndConnectRawExpectations(
1173 "ws://FooBar:pass@localhost/", NoSubProtocols(), LocalhostOrigin(),
1174 helper_.BuildSocketData2(kAuthorizedRequest,
1175 WebSocketStandardResponse(std::string())));
1176 WaitUntilConnectDone();
1177 EXPECT_FALSE(has_failed());
1178 EXPECT_TRUE(stream_);
1179 ASSERT_TRUE(response_info_);
1180 EXPECT_EQ(101, response_info_->status_code);
1183 TEST_F(WebSocketStreamCreateUMATest, Incomplete) {
1184 const std::string name("Net.WebSocket.HandshakeResult");
1185 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1188 StreamCreation creation;
1189 creation.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1190 creation.NoSubProtocols(),
1191 LocalhostOrigin(), "", "");
1194 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1195 ASSERT_TRUE(samples);
1196 if (original) {
1197 samples->Subtract(*original); // Cancel the original values.
1199 EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
1200 EXPECT_EQ(0, samples->GetCount(CONNECTED));
1201 EXPECT_EQ(0, samples->GetCount(FAILED));
1204 TEST_F(WebSocketStreamCreateUMATest, Connected) {
1205 const std::string name("Net.WebSocket.HandshakeResult");
1206 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1209 StreamCreation creation;
1210 creation.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1211 creation.NoSubProtocols(),
1212 LocalhostOrigin(), "", "");
1213 creation.WaitUntilConnectDone();
1216 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1217 ASSERT_TRUE(samples);
1218 if (original) {
1219 samples->Subtract(*original); // Cancel the original values.
1221 EXPECT_EQ(0, samples->GetCount(INCOMPLETE));
1222 EXPECT_EQ(1, samples->GetCount(CONNECTED));
1223 EXPECT_EQ(0, samples->GetCount(FAILED));
1226 TEST_F(WebSocketStreamCreateUMATest, Failed) {
1227 const std::string name("Net.WebSocket.HandshakeResult");
1228 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1231 StreamCreation creation;
1232 static const char kInvalidStatusCodeResponse[] =
1233 "HTTP/1.1 200 OK\r\n"
1234 "Upgrade: websocket\r\n"
1235 "Connection: Upgrade\r\n"
1236 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1237 "\r\n";
1238 creation.CreateAndConnectCustomResponse(
1239 "ws://localhost/", "localhost", "/", creation.NoSubProtocols(),
1240 LocalhostOrigin(), "", kInvalidStatusCodeResponse);
1241 creation.WaitUntilConnectDone();
1244 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1245 ASSERT_TRUE(samples);
1246 if (original) {
1247 samples->Subtract(*original); // Cancel the original values.
1249 EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
1250 EXPECT_EQ(0, samples->GetCount(CONNECTED));
1251 EXPECT_EQ(0, samples->GetCount(FAILED));
1254 TEST_F(WebSocketStreamCreateTest, HandleErrConnectionClosed) {
1255 static const char kTruncatedResponse[] =
1256 "HTTP/1.1 101 Switching Protocols\r\n"
1257 "Upgrade: websocket\r\n"
1258 "Connection: Upgrade\r\n"
1259 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1260 "Cache-Control: no-sto";
1262 std::string request =
1263 WebSocketStandardRequest("/", "localhost", LocalhostOrigin(), "");
1264 MockRead reads[] = {
1265 MockRead(SYNCHRONOUS, 1, kTruncatedResponse),
1266 MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED, 2),
1268 MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, request.c_str())};
1269 scoped_ptr<SequencedSocketData> socket_data(BuildSocketData(reads, writes));
1270 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
1271 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1272 LocalhostOrigin(), socket_data.Pass());
1273 WaitUntilConnectDone();
1274 EXPECT_TRUE(has_failed());
1277 TEST_F(WebSocketStreamCreateTest, HandleErrTunnelConnectionFailed) {
1278 static const char kConnectRequest[] =
1279 "CONNECT localhost:80 HTTP/1.1\r\n"
1280 "Host: localhost:80\r\n"
1281 "Proxy-Connection: keep-alive\r\n"
1282 "\r\n";
1284 static const char kProxyResponse[] =
1285 "HTTP/1.1 403 Forbidden\r\n"
1286 "Content-Type: text/html\r\n"
1287 "Content-Length: 9\r\n"
1288 "Connection: keep-alive\r\n"
1289 "\r\n"
1290 "Forbidden";
1292 MockRead reads[] = {MockRead(SYNCHRONOUS, 1, kProxyResponse)};
1293 MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, kConnectRequest)};
1294 scoped_ptr<SequencedSocketData> socket_data(BuildSocketData(reads, writes));
1295 url_request_context_host_.SetProxyConfig("https=proxy:8000");
1296 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1297 LocalhostOrigin(), socket_data.Pass());
1298 WaitUntilConnectDone();
1299 EXPECT_TRUE(has_failed());
1300 EXPECT_EQ("Establishing a tunnel via proxy server failed.",
1301 failure_message());
1304 } // namespace
1305 } // namespace net