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