Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / net / websockets / websocket_stream_test.cc
blob03a593de0e9a4b7c5f98f4fed36b65ed793a7554
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_host,
114 const std::string& socket_path,
115 const std::vector<std::string>& sub_protocols,
116 const std::string& origin,
117 const std::string& extra_request_headers,
118 const std::string& response_body,
119 scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
120 url_request_context_host_.SetExpectations(
121 WebSocketStandardRequest(socket_path, socket_host, origin,
122 extra_request_headers),
123 response_body);
124 CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
127 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
128 // errors like "Unable to perform synchronous IO while stopped" will occur.
129 void CreateAndConnectStandard(
130 const std::string& socket_url,
131 const std::string& socket_host,
132 const std::string& socket_path,
133 const std::vector<std::string>& sub_protocols,
134 const std::string& origin,
135 const std::string& extra_request_headers,
136 const std::string& extra_response_headers,
137 scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
138 CreateAndConnectCustomResponse(
139 socket_url, socket_host, socket_path, sub_protocols, origin,
140 extra_request_headers,
141 WebSocketStandardResponse(extra_response_headers), timer.Pass());
144 void CreateAndConnectRawExpectations(
145 const std::string& socket_url,
146 const std::vector<std::string>& sub_protocols,
147 const std::string& origin,
148 scoped_ptr<DeterministicSocketData> socket_data,
149 scoped_ptr<base::Timer> timer = scoped_ptr<base::Timer>()) {
150 AddRawExpectations(socket_data.Pass());
151 CreateAndConnectStream(socket_url, sub_protocols, origin, timer.Pass());
154 // Add additional raw expectations for sockets created before the final one.
155 void AddRawExpectations(scoped_ptr<DeterministicSocketData> socket_data) {
156 url_request_context_host_.AddRawExpectations(socket_data.Pass());
159 // A wrapper for CreateAndConnectStreamForTesting that knows about our default
160 // parameters.
161 void CreateAndConnectStream(const std::string& socket_url,
162 const std::vector<std::string>& sub_protocols,
163 const std::string& origin,
164 scoped_ptr<base::Timer> timer) {
165 for (size_t i = 0; i < ssl_data_.size(); ++i) {
166 scoped_ptr<SSLSocketDataProvider> ssl_data(ssl_data_[i]);
167 ssl_data_[i] = NULL;
168 url_request_context_host_.AddSSLSocketDataProvider(ssl_data.Pass());
170 ssl_data_.clear();
171 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
172 new TestConnectDelegate(this));
173 WebSocketStream::ConnectDelegate* delegate = connect_delegate.get();
174 scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper(
175 new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
176 delegate, sub_protocols));
177 stream_request_ = ::net::CreateAndConnectStreamForTesting(
178 GURL(socket_url),
179 create_helper.Pass(),
180 url::Origin(origin),
181 url_request_context_host_.GetURLRequestContext(),
182 BoundNetLog(),
183 connect_delegate.Pass(),
184 timer ? timer.Pass() : scoped_ptr<base::Timer>(
185 new base::Timer(false, false)));
188 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
190 // A simple function to make the tests more readable. Creates an empty vector.
191 static std::vector<std::string> NoSubProtocols() {
192 return std::vector<std::string>();
195 const std::string& failure_message() const { return failure_message_; }
196 bool has_failed() const { return has_failed_; }
198 class TestConnectDelegate : public WebSocketStream::ConnectDelegate {
199 public:
200 explicit TestConnectDelegate(WebSocketStreamCreateTest* owner)
201 : owner_(owner) {}
203 void OnSuccess(scoped_ptr<WebSocketStream> stream) override {
204 stream.swap(owner_->stream_);
207 void OnFailure(const std::string& message) override {
208 owner_->has_failed_ = true;
209 owner_->failure_message_ = message;
212 void OnStartOpeningHandshake(
213 scoped_ptr<WebSocketHandshakeRequestInfo> request) override {
214 // Can be called multiple times (in the case of HTTP auth). Last call
215 // wins.
216 owner_->request_info_ = request.Pass();
218 void OnFinishOpeningHandshake(
219 scoped_ptr<WebSocketHandshakeResponseInfo> response) override {
220 if (owner_->response_info_)
221 ADD_FAILURE();
222 owner_->response_info_ = response.Pass();
224 void OnSSLCertificateError(
225 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
226 ssl_error_callbacks,
227 const SSLInfo& ssl_info,
228 bool fatal) override {
229 owner_->ssl_error_callbacks_ = ssl_error_callbacks.Pass();
230 owner_->ssl_info_ = ssl_info;
231 owner_->ssl_fatal_ = fatal;
234 private:
235 WebSocketStreamCreateTest* owner_;
238 WebSocketTestURLRequestContextHost url_request_context_host_;
239 scoped_ptr<WebSocketStreamRequest> stream_request_;
240 // Only set if the connection succeeded.
241 scoped_ptr<WebSocketStream> stream_;
242 // Only set if the connection failed.
243 std::string failure_message_;
244 bool has_failed_;
245 scoped_ptr<WebSocketHandshakeRequestInfo> request_info_;
246 scoped_ptr<WebSocketHandshakeResponseInfo> response_info_;
247 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks_;
248 SSLInfo ssl_info_;
249 bool ssl_fatal_;
250 ScopedVector<SSLSocketDataProvider> ssl_data_;
253 // There are enough tests of the Sec-WebSocket-Extensions header that they
254 // deserve their own test fixture.
255 class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest {
256 public:
257 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
258 // header in the response set to |extensions_header_value|. Runs the event
259 // loop to allow the connect to complete.
260 void CreateAndConnectWithExtensions(
261 const std::string& extensions_header_value) {
262 CreateAndConnectStandard(
263 "ws://localhost/testing_path", "localhost", "/testing_path",
264 NoSubProtocols(), "http://localhost", "",
265 "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n");
266 RunUntilIdle();
270 // Common code to construct expectations for authentication tests that receive
271 // the auth challenge on one connection and then create a second connection to
272 // send the authenticated request on.
273 class CommonAuthTestHelper {
274 public:
275 CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
277 scoped_ptr<DeterministicSocketData> BuildSocketData1(
278 const std::string& response) {
279 request1_ =
280 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
281 writes1_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
282 response1_ = response;
283 reads1_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
284 reads1_[1] = MockRead(SYNCHRONOUS, OK, 2); // Close connection
286 return BuildSocketData(reads1_, writes1_);
289 scoped_ptr<DeterministicSocketData> BuildSocketData2(
290 const std::string& request,
291 const std::string& response) {
292 request2_ = request;
293 response2_ = response;
294 writes2_[0] = MockWrite(SYNCHRONOUS, 0, request2_.c_str());
295 reads2_[0] = MockRead(SYNCHRONOUS, 1, response2_.c_str());
296 return BuildSocketData(reads2_, writes2_);
299 private:
300 // These need to be object-scoped since they have to remain valid until all
301 // socket operations in the test are complete.
302 std::string request1_;
303 std::string request2_;
304 std::string response1_;
305 std::string response2_;
306 MockRead reads1_[2];
307 MockWrite writes1_[1];
308 MockRead reads2_[1];
309 MockWrite writes2_[1];
311 DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper);
314 // Data and methods for BasicAuth tests.
315 class WebSocketStreamCreateBasicAuthTest : public WebSocketStreamCreateTest {
316 protected:
317 void CreateAndConnectAuthHandshake(const std::string& url,
318 const std::string& base64_user_pass,
319 const std::string& response2) {
320 AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
322 static const char request2format[] =
323 "GET / HTTP/1.1\r\n"
324 "Host: localhost\r\n"
325 "Connection: Upgrade\r\n"
326 "Pragma: no-cache\r\n"
327 "Cache-Control: no-cache\r\n"
328 "Authorization: Basic %s\r\n"
329 "Upgrade: websocket\r\n"
330 "Origin: http://localhost\r\n"
331 "Sec-WebSocket-Version: 13\r\n"
332 "User-Agent:\r\n"
333 "Accept-Encoding: gzip, deflate\r\n"
334 "Accept-Language: en-us,fr\r\n"
335 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
336 "Sec-WebSocket-Extensions: permessage-deflate; "
337 "client_max_window_bits\r\n"
338 "\r\n";
339 const std::string request =
340 base::StringPrintf(request2format, base64_user_pass.c_str());
341 CreateAndConnectRawExpectations(
342 url,
343 NoSubProtocols(),
344 "http://localhost",
345 helper_.BuildSocketData2(request, response2));
348 static const char kUnauthorizedResponse[];
350 CommonAuthTestHelper helper_;
353 class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {
354 protected:
355 static const char kUnauthorizedResponse[];
356 static const char kAuthorizedRequest[];
358 CommonAuthTestHelper helper_;
361 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse[] =
362 "HTTP/1.1 401 Unauthorized\r\n"
363 "Content-Length: 0\r\n"
364 "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
365 "\r\n";
367 // These negotiation values are borrowed from
368 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
369 // you are bored. Only the weakest (no qop) variants of Digest authentication
370 // can be tested by this method, because the others involve random input.
371 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse[] =
372 "HTTP/1.1 401 Unauthorized\r\n"
373 "Content-Length: 0\r\n"
374 "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
375 "\r\n";
377 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest[] =
378 "GET / HTTP/1.1\r\n"
379 "Host: localhost\r\n"
380 "Connection: Upgrade\r\n"
381 "Pragma: no-cache\r\n"
382 "Cache-Control: no-cache\r\n"
383 "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
384 "nonce=\"nonce-value\", uri=\"/\", "
385 "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
386 "Upgrade: websocket\r\n"
387 "Origin: http://localhost\r\n"
388 "Sec-WebSocket-Version: 13\r\n"
389 "User-Agent:\r\n"
390 "Accept-Encoding: gzip, deflate\r\n"
391 "Accept-Language: en-us,fr\r\n"
392 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
393 "Sec-WebSocket-Extensions: permessage-deflate; "
394 "client_max_window_bits\r\n"
395 "\r\n";
397 class WebSocketStreamCreateUMATest : public ::testing::Test {
398 public:
399 // This enum should match with the enum in Delegate in websocket_stream.cc.
400 enum HandshakeResult {
401 INCOMPLETE,
402 CONNECTED,
403 FAILED,
404 NUM_HANDSHAKE_RESULT_TYPES,
407 class StreamCreation : public WebSocketStreamCreateTest {
408 void TestBody() override {}
411 scoped_ptr<base::HistogramSamples> GetSamples(const std::string& name) {
412 base::HistogramBase* histogram =
413 base::StatisticsRecorder::FindHistogram(name);
414 return histogram ? histogram->SnapshotSamples()
415 : scoped_ptr<base::HistogramSamples>();
419 // Confirm that the basic case works as expected.
420 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
421 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
422 NoSubProtocols(), "http://localhost", "", "");
423 EXPECT_FALSE(request_info_);
424 EXPECT_FALSE(response_info_);
425 RunUntilIdle();
426 EXPECT_FALSE(has_failed());
427 EXPECT_TRUE(stream_);
428 EXPECT_TRUE(request_info_);
429 EXPECT_TRUE(response_info_);
432 TEST_F(WebSocketStreamCreateTest, HandshakeInfo) {
433 static const char kResponse[] =
434 "HTTP/1.1 101 Switching Protocols\r\n"
435 "Upgrade: websocket\r\n"
436 "Connection: Upgrade\r\n"
437 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
438 "foo: bar, baz\r\n"
439 "hoge: fuga\r\n"
440 "hoge: piyo\r\n"
441 "\r\n";
443 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
444 NoSubProtocols(), "http://localhost", "",
445 kResponse);
446 EXPECT_FALSE(request_info_);
447 EXPECT_FALSE(response_info_);
448 RunUntilIdle();
449 EXPECT_TRUE(stream_);
450 ASSERT_TRUE(request_info_);
451 ASSERT_TRUE(response_info_);
452 std::vector<HeaderKeyValuePair> request_headers =
453 ToVector(request_info_->headers);
454 // We examine the contents of request_info_ and response_info_
455 // mainly only in this test case.
456 EXPECT_EQ(GURL("ws://localhost/"), request_info_->url);
457 EXPECT_EQ(GURL("ws://localhost/"), response_info_->url);
458 EXPECT_EQ(101, response_info_->status_code);
459 EXPECT_EQ("Switching Protocols", response_info_->status_text);
460 ASSERT_EQ(12u, request_headers.size());
461 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]);
462 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
463 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
464 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
465 request_headers[3]);
466 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
467 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
468 request_headers[5]);
469 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
470 request_headers[6]);
471 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
472 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
473 request_headers[8]);
474 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
475 request_headers[9]);
476 EXPECT_EQ("Sec-WebSocket-Key", request_headers[10].first);
477 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
478 "permessage-deflate; client_max_window_bits"),
479 request_headers[11]);
481 std::vector<HeaderKeyValuePair> response_headers =
482 ToVector(*response_info_->headers.get());
483 ASSERT_EQ(6u, response_headers.size());
484 // Sort the headers for ease of verification.
485 std::sort(response_headers.begin(), response_headers.end());
487 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
488 EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
489 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
490 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
491 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
492 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
495 // Confirm that the stream isn't established until the message loop runs.
496 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
497 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
498 NoSubProtocols(), "http://localhost", "", "");
499 EXPECT_FALSE(has_failed());
500 EXPECT_FALSE(stream_);
503 // Check the path is used.
504 TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
505 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
506 "/testing_path", NoSubProtocols(),
507 "http://localhost", "", "");
508 RunUntilIdle();
509 EXPECT_FALSE(has_failed());
510 EXPECT_TRUE(stream_);
513 // Check that the origin is used.
514 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
515 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
516 "/testing_path", NoSubProtocols(),
517 "http://google.com", "", "");
518 RunUntilIdle();
519 EXPECT_FALSE(has_failed());
520 EXPECT_TRUE(stream_);
523 // Check that sub-protocols are sent and parsed.
524 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
525 std::vector<std::string> sub_protocols;
526 sub_protocols.push_back("chatv11.chromium.org");
527 sub_protocols.push_back("chatv20.chromium.org");
528 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
529 "/testing_path", sub_protocols, "http://google.com",
530 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
531 "chatv20.chromium.org\r\n",
532 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
533 RunUntilIdle();
534 EXPECT_TRUE(stream_);
535 EXPECT_FALSE(has_failed());
536 EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
539 // Unsolicited sub-protocols are rejected.
540 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
541 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
542 "/testing_path", NoSubProtocols(),
543 "http://google.com", "",
544 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
545 RunUntilIdle();
546 EXPECT_FALSE(stream_);
547 EXPECT_TRUE(has_failed());
548 EXPECT_EQ("Error during WebSocket handshake: "
549 "Response must not include 'Sec-WebSocket-Protocol' header "
550 "if not present in request: chatv20.chromium.org",
551 failure_message());
554 // Missing sub-protocol response is rejected.
555 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
556 std::vector<std::string> sub_protocols;
557 sub_protocols.push_back("chat.example.com");
558 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
559 "/testing_path", sub_protocols, "http://localhost",
560 "Sec-WebSocket-Protocol: chat.example.com\r\n", "");
561 RunUntilIdle();
562 EXPECT_FALSE(stream_);
563 EXPECT_TRUE(has_failed());
564 EXPECT_EQ("Error during WebSocket handshake: "
565 "Sent non-empty 'Sec-WebSocket-Protocol' header "
566 "but no response was received",
567 failure_message());
570 // Only one sub-protocol can be accepted.
571 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
572 std::vector<std::string> sub_protocols;
573 sub_protocols.push_back("chatv11.chromium.org");
574 sub_protocols.push_back("chatv20.chromium.org");
575 CreateAndConnectStandard("ws://localhost/testing_path", "localhost",
576 "/testing_path", sub_protocols, "http://google.com",
577 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
578 "chatv20.chromium.org\r\n",
579 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
580 "chatv20.chromium.org\r\n");
581 RunUntilIdle();
582 EXPECT_FALSE(stream_);
583 EXPECT_TRUE(has_failed());
584 EXPECT_EQ("Error during WebSocket handshake: "
585 "'Sec-WebSocket-Protocol' header must not appear "
586 "more than once in a response",
587 failure_message());
590 // Unmatched sub-protocol should be rejected.
591 TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) {
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", "localhost",
596 "/testing_path", sub_protocols, "http://google.com",
597 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
598 "chatv20.chromium.org\r\n",
599 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
600 RunUntilIdle();
601 EXPECT_FALSE(stream_);
602 EXPECT_TRUE(has_failed());
603 EXPECT_EQ("Error during WebSocket handshake: "
604 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
605 "in response does not match any of sent values",
606 failure_message());
609 // permessage-deflate extension basic success case.
610 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
611 CreateAndConnectWithExtensions("permessage-deflate");
612 EXPECT_TRUE(stream_);
613 EXPECT_FALSE(has_failed());
616 // permessage-deflate extensions success with all parameters.
617 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
618 CreateAndConnectWithExtensions(
619 "permessage-deflate; client_no_context_takeover; "
620 "server_max_window_bits=11; client_max_window_bits=13; "
621 "server_no_context_takeover");
622 EXPECT_TRUE(stream_);
623 EXPECT_FALSE(has_failed());
626 // Verify that incoming messages are actually decompressed with
627 // permessage-deflate enabled.
628 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
629 CreateAndConnectCustomResponse(
630 "ws://localhost/testing_path", "localhost", "/testing_path",
631 NoSubProtocols(), "http://localhost", "",
632 WebSocketStandardResponse(
633 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
634 std::string(
635 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
636 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
637 9));
638 RunUntilIdle();
640 ASSERT_TRUE(stream_);
641 ScopedVector<WebSocketFrame> frames;
642 CompletionCallback callback;
643 ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback));
644 ASSERT_EQ(1U, frames.size());
645 ASSERT_EQ(5U, frames[0]->header.payload_length);
646 EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5));
649 // Unknown extension in the response is rejected
650 TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) {
651 CreateAndConnectWithExtensions("x-unknown-extension");
652 EXPECT_FALSE(stream_);
653 EXPECT_TRUE(has_failed());
654 EXPECT_EQ("Error during WebSocket handshake: "
655 "Found an unsupported extension 'x-unknown-extension' "
656 "in 'Sec-WebSocket-Extensions' header",
657 failure_message());
660 // Malformed extensions are rejected (this file does not cover all possible
661 // parse failures, as the parser is covered thoroughly by its own unit tests).
662 TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) {
663 CreateAndConnectWithExtensions(";");
664 EXPECT_FALSE(stream_);
665 EXPECT_TRUE(has_failed());
666 EXPECT_EQ(
667 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
668 "value is rejected by the parser: ;",
669 failure_message());
672 // The permessage-deflate extension may only be specified once.
673 TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
674 CreateAndConnectWithExtensions(
675 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
676 EXPECT_FALSE(stream_);
677 EXPECT_TRUE(has_failed());
678 EXPECT_EQ(
679 "Error during WebSocket handshake: "
680 "Received duplicate permessage-deflate response",
681 failure_message());
684 // permessage-deflate parameters may not be duplicated.
685 TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) {
686 CreateAndConnectWithExtensions(
687 "permessage-deflate; client_no_context_takeover; "
688 "client_no_context_takeover");
689 EXPECT_FALSE(stream_);
690 EXPECT_TRUE(has_failed());
691 EXPECT_EQ(
692 "Error during WebSocket handshake: Error in permessage-deflate: "
693 "Received duplicate permessage-deflate extension parameter "
694 "client_no_context_takeover",
695 failure_message());
698 // permessage-deflate parameters must start with "client_" or "server_"
699 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) {
700 CreateAndConnectWithExtensions(
701 "permessage-deflate; absurd_no_context_takeover");
702 EXPECT_FALSE(stream_);
703 EXPECT_TRUE(has_failed());
704 EXPECT_EQ(
705 "Error during WebSocket handshake: Error in permessage-deflate: "
706 "Received an unexpected permessage-deflate extension parameter",
707 failure_message());
710 // permessage-deflate parameters must be either *_no_context_takeover or
711 // *_max_window_bits
712 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) {
713 CreateAndConnectWithExtensions(
714 "permessage-deflate; client_max_content_bits=5");
715 EXPECT_FALSE(stream_);
716 EXPECT_TRUE(has_failed());
717 EXPECT_EQ(
718 "Error during WebSocket handshake: Error in permessage-deflate: "
719 "Received an unexpected permessage-deflate extension parameter",
720 failure_message());
723 // *_no_context_takeover parameters must not have an argument
724 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) {
725 CreateAndConnectWithExtensions(
726 "permessage-deflate; client_no_context_takeover=true");
727 EXPECT_FALSE(stream_);
728 EXPECT_TRUE(has_failed());
729 EXPECT_EQ(
730 "Error during WebSocket handshake: Error in permessage-deflate: "
731 "Received invalid client_no_context_takeover parameter",
732 failure_message());
735 // *_max_window_bits must have an argument
736 TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
737 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
738 EXPECT_FALSE(stream_);
739 EXPECT_TRUE(has_failed());
740 EXPECT_EQ(
741 "Error during WebSocket handshake: Error in permessage-deflate: "
742 "client_max_window_bits must have value",
743 failure_message());
746 // *_max_window_bits must be an integer
747 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) {
748 CreateAndConnectWithExtensions(
749 "permessage-deflate; server_max_window_bits=banana");
750 EXPECT_FALSE(stream_);
751 EXPECT_TRUE(has_failed());
752 EXPECT_EQ(
753 "Error during WebSocket handshake: Error in permessage-deflate: "
754 "Received invalid server_max_window_bits parameter",
755 failure_message());
758 // *_max_window_bits must be >= 8
759 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) {
760 CreateAndConnectWithExtensions(
761 "permessage-deflate; server_max_window_bits=7");
762 EXPECT_FALSE(stream_);
763 EXPECT_TRUE(has_failed());
764 EXPECT_EQ(
765 "Error during WebSocket handshake: Error in permessage-deflate: "
766 "Received invalid server_max_window_bits parameter",
767 failure_message());
770 // *_max_window_bits must be <= 15
771 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) {
772 CreateAndConnectWithExtensions(
773 "permessage-deflate; client_max_window_bits=16");
774 EXPECT_FALSE(stream_);
775 EXPECT_TRUE(has_failed());
776 EXPECT_EQ(
777 "Error during WebSocket handshake: Error in permessage-deflate: "
778 "Received invalid client_max_window_bits parameter",
779 failure_message());
782 // *_max_window_bits must not start with 0
783 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) {
784 CreateAndConnectWithExtensions(
785 "permessage-deflate; client_max_window_bits=08");
786 EXPECT_FALSE(stream_);
787 EXPECT_TRUE(has_failed());
788 EXPECT_EQ(
789 "Error during WebSocket handshake: Error in permessage-deflate: "
790 "Received invalid client_max_window_bits parameter",
791 failure_message());
794 // *_max_window_bits must not start with +
795 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) {
796 CreateAndConnectWithExtensions(
797 "permessage-deflate; server_max_window_bits=+9");
798 EXPECT_FALSE(stream_);
799 EXPECT_TRUE(has_failed());
800 EXPECT_EQ(
801 "Error during WebSocket handshake: Error in permessage-deflate: "
802 "Received invalid server_max_window_bits parameter",
803 failure_message());
806 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
807 // arguments from the server. This is difficult because the data written to the
808 // socket is randomly masked.
810 // Additional Sec-WebSocket-Accept headers should be rejected.
811 TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
812 CreateAndConnectStandard(
813 "ws://localhost/", "localhost", "/", NoSubProtocols(), "http://localhost",
814 "", "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
815 RunUntilIdle();
816 EXPECT_FALSE(stream_);
817 EXPECT_TRUE(has_failed());
818 EXPECT_EQ("Error during WebSocket handshake: "
819 "'Sec-WebSocket-Accept' header must not appear "
820 "more than once in a response",
821 failure_message());
824 // Response code 200 must be rejected.
825 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
826 static const char kInvalidStatusCodeResponse[] =
827 "HTTP/1.1 200 OK\r\n"
828 "Upgrade: websocket\r\n"
829 "Connection: Upgrade\r\n"
830 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
831 "\r\n";
832 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
833 NoSubProtocols(), "http://localhost", "",
834 kInvalidStatusCodeResponse);
835 RunUntilIdle();
836 EXPECT_TRUE(has_failed());
837 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
838 failure_message());
841 // Redirects are not followed (according to the WHATWG WebSocket API, which
842 // overrides RFC6455 for browser applications).
843 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
844 static const char kRedirectResponse[] =
845 "HTTP/1.1 302 Moved Temporarily\r\n"
846 "Content-Type: text/html\r\n"
847 "Content-Length: 34\r\n"
848 "Connection: keep-alive\r\n"
849 "Location: ws://localhost/other\r\n"
850 "\r\n"
851 "<title>Moved</title><h1>Moved</h1>";
852 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
853 NoSubProtocols(), "http://localhost", "",
854 kRedirectResponse);
855 RunUntilIdle();
856 EXPECT_TRUE(has_failed());
857 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
858 failure_message());
861 // Malformed responses should be rejected. HttpStreamParser will accept just
862 // about any garbage in the middle of the headers. To make it give up, the junk
863 // has to be at the start of the response. Even then, it just gets treated as an
864 // HTTP/0.9 response.
865 TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
866 static const char kMalformedResponse[] =
867 "220 mx.google.com ESMTP\r\n"
868 "HTTP/1.1 101 OK\r\n"
869 "Upgrade: websocket\r\n"
870 "Connection: Upgrade\r\n"
871 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
872 "\r\n";
873 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
874 NoSubProtocols(), "http://localhost", "",
875 kMalformedResponse);
876 RunUntilIdle();
877 EXPECT_TRUE(has_failed());
878 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
879 failure_message());
882 // Upgrade header must be present.
883 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
884 static const char kMissingUpgradeResponse[] =
885 "HTTP/1.1 101 Switching Protocols\r\n"
886 "Connection: Upgrade\r\n"
887 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
888 "\r\n";
889 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
890 NoSubProtocols(), "http://localhost", "",
891 kMissingUpgradeResponse);
892 RunUntilIdle();
893 EXPECT_TRUE(has_failed());
894 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
895 failure_message());
898 // There must only be one upgrade header.
899 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
900 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
901 NoSubProtocols(), "http://localhost", "",
902 "Upgrade: HTTP/2.0\r\n");
903 RunUntilIdle();
904 EXPECT_TRUE(has_failed());
905 EXPECT_EQ("Error during WebSocket handshake: "
906 "'Upgrade' header must not appear more than once in a response",
907 failure_message());
910 // There must only be one correct upgrade header.
911 TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
912 static const char kMissingUpgradeResponse[] =
913 "HTTP/1.1 101 Switching Protocols\r\n"
914 "Connection: Upgrade\r\n"
915 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
916 "Upgrade: hogefuga\r\n"
917 "\r\n";
918 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
919 NoSubProtocols(), "http://localhost", "",
920 kMissingUpgradeResponse);
921 RunUntilIdle();
922 EXPECT_TRUE(has_failed());
923 EXPECT_EQ("Error during WebSocket handshake: "
924 "'Upgrade' header value is not 'WebSocket': hogefuga",
925 failure_message());
928 // Connection header must be present.
929 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
930 static const char kMissingConnectionResponse[] =
931 "HTTP/1.1 101 Switching Protocols\r\n"
932 "Upgrade: websocket\r\n"
933 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
934 "\r\n";
935 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
936 NoSubProtocols(), "http://localhost", "",
937 kMissingConnectionResponse);
938 RunUntilIdle();
939 EXPECT_TRUE(has_failed());
940 EXPECT_EQ("Error during WebSocket handshake: "
941 "'Connection' header is missing",
942 failure_message());
945 // Connection header must contain "Upgrade".
946 TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
947 static const char kMissingConnectionResponse[] =
948 "HTTP/1.1 101 Switching Protocols\r\n"
949 "Upgrade: websocket\r\n"
950 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
951 "Connection: hogefuga\r\n"
952 "\r\n";
953 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
954 NoSubProtocols(), "http://localhost", "",
955 kMissingConnectionResponse);
956 RunUntilIdle();
957 EXPECT_TRUE(has_failed());
958 EXPECT_EQ("Error during WebSocket handshake: "
959 "'Connection' header value must contain 'Upgrade'",
960 failure_message());
963 // Connection header is permitted to contain other tokens.
964 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
965 static const char kAdditionalConnectionTokenResponse[] =
966 "HTTP/1.1 101 Switching Protocols\r\n"
967 "Upgrade: websocket\r\n"
968 "Connection: Upgrade, Keep-Alive\r\n"
969 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
970 "\r\n";
971 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
972 NoSubProtocols(), "http://localhost", "",
973 kAdditionalConnectionTokenResponse);
974 RunUntilIdle();
975 EXPECT_FALSE(has_failed());
976 EXPECT_TRUE(stream_);
979 // Sec-WebSocket-Accept header must be present.
980 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
981 static const char kMissingAcceptResponse[] =
982 "HTTP/1.1 101 Switching Protocols\r\n"
983 "Upgrade: websocket\r\n"
984 "Connection: Upgrade\r\n"
985 "\r\n";
986 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
987 NoSubProtocols(), "http://localhost", "",
988 kMissingAcceptResponse);
989 RunUntilIdle();
990 EXPECT_TRUE(has_failed());
991 EXPECT_EQ("Error during WebSocket handshake: "
992 "'Sec-WebSocket-Accept' header is missing",
993 failure_message());
996 // Sec-WebSocket-Accept header must match the key that was sent.
997 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
998 static const char kIncorrectAcceptResponse[] =
999 "HTTP/1.1 101 Switching Protocols\r\n"
1000 "Upgrade: websocket\r\n"
1001 "Connection: Upgrade\r\n"
1002 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
1003 "\r\n";
1004 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
1005 NoSubProtocols(), "http://localhost", "",
1006 kIncorrectAcceptResponse);
1007 RunUntilIdle();
1008 EXPECT_TRUE(has_failed());
1009 EXPECT_EQ("Error during WebSocket handshake: "
1010 "Incorrect 'Sec-WebSocket-Accept' header value",
1011 failure_message());
1014 // Cancellation works.
1015 TEST_F(WebSocketStreamCreateTest, Cancellation) {
1016 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1017 NoSubProtocols(), "http://localhost", "", "");
1018 stream_request_.reset();
1019 RunUntilIdle();
1020 EXPECT_FALSE(has_failed());
1021 EXPECT_FALSE(stream_);
1022 EXPECT_FALSE(request_info_);
1023 EXPECT_FALSE(response_info_);
1026 // Connect failure must look just like negotiation failure.
1027 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
1028 scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
1029 socket_data->set_connect_data(
1030 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
1031 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1032 "http://localhost", socket_data.Pass());
1033 RunUntilIdle();
1034 EXPECT_TRUE(has_failed());
1035 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1036 failure_message());
1037 EXPECT_FALSE(request_info_);
1038 EXPECT_FALSE(response_info_);
1041 // Connect timeout must look just like any other failure.
1042 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
1043 scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
1044 socket_data->set_connect_data(
1045 MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
1046 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
1047 "http://localhost", socket_data.Pass());
1048 RunUntilIdle();
1049 EXPECT_TRUE(has_failed());
1050 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
1051 failure_message());
1054 // The server doesn't respond to the opening handshake.
1055 TEST_F(WebSocketStreamCreateTest, HandshakeTimeout) {
1056 scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
1057 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
1058 scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
1059 base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
1060 CreateAndConnectRawExpectations("ws://localhost/",
1061 NoSubProtocols(),
1062 "http://localhost",
1063 socket_data.Pass(),
1064 timer.Pass());
1065 EXPECT_FALSE(has_failed());
1066 ASSERT_TRUE(weak_timer.get());
1067 EXPECT_TRUE(weak_timer->IsRunning());
1069 weak_timer->Fire();
1070 RunUntilIdle();
1072 EXPECT_TRUE(has_failed());
1073 EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
1074 ASSERT_TRUE(weak_timer.get());
1075 EXPECT_FALSE(weak_timer->IsRunning());
1078 // When the connection establishes the timer should be stopped.
1079 TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnSuccess) {
1080 scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
1081 base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
1083 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1084 NoSubProtocols(), "http://localhost", "", "",
1085 timer.Pass());
1086 ASSERT_TRUE(weak_timer);
1087 EXPECT_TRUE(weak_timer->IsRunning());
1089 RunUntilIdle();
1090 EXPECT_FALSE(has_failed());
1091 EXPECT_TRUE(stream_);
1092 ASSERT_TRUE(weak_timer);
1093 EXPECT_FALSE(weak_timer->IsRunning());
1096 // When the connection fails the timer should be stopped.
1097 TEST_F(WebSocketStreamCreateTest, HandshakeTimerOnFailure) {
1098 scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
1099 socket_data->set_connect_data(
1100 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
1101 scoped_ptr<MockWeakTimer> timer(new MockWeakTimer(false, false));
1102 base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
1103 CreateAndConnectRawExpectations("ws://localhost/",
1104 NoSubProtocols(),
1105 "http://localhost",
1106 socket_data.Pass(),
1107 timer.Pass());
1108 ASSERT_TRUE(weak_timer.get());
1109 EXPECT_TRUE(weak_timer->IsRunning());
1111 RunUntilIdle();
1112 EXPECT_TRUE(has_failed());
1113 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1114 failure_message());
1115 ASSERT_TRUE(weak_timer.get());
1116 EXPECT_FALSE(weak_timer->IsRunning());
1119 // Cancellation during connect works.
1120 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
1121 scoped_ptr<DeterministicSocketData> socket_data(BuildNullSocketData());
1122 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
1123 CreateAndConnectRawExpectations("ws://localhost/",
1124 NoSubProtocols(),
1125 "http://localhost",
1126 socket_data.Pass());
1127 stream_request_.reset();
1128 RunUntilIdle();
1129 EXPECT_FALSE(has_failed());
1130 EXPECT_FALSE(stream_);
1133 // Cancellation during write of the request headers works.
1134 TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) {
1135 // We seem to need at least two operations in order to use SetStop().
1136 MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"),
1137 MockWrite(ASYNC, 1, "1.1\r\n")};
1138 // We keep a copy of the pointer so that we can call RunFor() on it later.
1139 DeterministicSocketData* socket_data(
1140 new DeterministicSocketData(NULL, 0, writes, arraysize(writes)));
1141 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
1142 socket_data->SetStop(1);
1143 CreateAndConnectRawExpectations("ws://localhost/",
1144 NoSubProtocols(),
1145 "http://localhost",
1146 make_scoped_ptr(socket_data));
1147 socket_data->Run();
1148 stream_request_.reset();
1149 RunUntilIdle();
1150 EXPECT_FALSE(has_failed());
1151 EXPECT_FALSE(stream_);
1152 EXPECT_TRUE(request_info_);
1153 EXPECT_FALSE(response_info_);
1156 // Cancellation during read of the response headers works.
1157 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
1158 std::string request =
1159 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
1160 MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
1161 MockRead reads[] = {
1162 MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
1164 scoped_ptr<DeterministicSocketData> socket_data(
1165 BuildSocketData(reads, writes));
1166 socket_data->SetStop(1);
1167 DeterministicSocketData* socket_data_raw_ptr = socket_data.get();
1168 CreateAndConnectRawExpectations("ws://localhost/",
1169 NoSubProtocols(),
1170 "http://localhost",
1171 socket_data.Pass());
1172 socket_data_raw_ptr->Run();
1173 stream_request_.reset();
1174 RunUntilIdle();
1175 EXPECT_FALSE(has_failed());
1176 EXPECT_FALSE(stream_);
1177 EXPECT_TRUE(request_info_);
1178 EXPECT_FALSE(response_info_);
1181 // Over-size response headers (> 256KB) should not cause a crash. This is a
1182 // regression test for crbug.com/339456. It is based on the layout test
1183 // "cookie-flood.html".
1184 TEST_F(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
1185 std::string set_cookie_headers;
1186 set_cookie_headers.reserve(45 * 10000);
1187 for (int i = 0; i < 10000; ++i) {
1188 set_cookie_headers +=
1189 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i);
1191 CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1192 NoSubProtocols(), "http://localhost", "",
1193 set_cookie_headers);
1194 RunUntilIdle();
1195 EXPECT_TRUE(has_failed());
1196 EXPECT_FALSE(response_info_);
1199 // If the remote host closes the connection without sending headers, we should
1200 // log the console message "Connection closed before receiving a handshake
1201 // response".
1202 TEST_F(WebSocketStreamCreateTest, NoResponse) {
1203 std::string request =
1204 WebSocketStandardRequest("/", "localhost", "http://localhost", "");
1205 MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
1206 MockRead reads[] = {MockRead(ASYNC, 0, 1)};
1207 scoped_ptr<DeterministicSocketData> socket_data(
1208 BuildSocketData(reads, writes));
1209 DeterministicSocketData* socket_data_raw_ptr = socket_data.get();
1210 CreateAndConnectRawExpectations("ws://localhost/",
1211 NoSubProtocols(),
1212 "http://localhost",
1213 socket_data.Pass());
1214 socket_data_raw_ptr->RunFor(2);
1215 EXPECT_TRUE(has_failed());
1216 EXPECT_FALSE(stream_);
1217 EXPECT_FALSE(response_info_);
1218 EXPECT_EQ("Connection closed before receiving a handshake response",
1219 failure_message());
1222 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
1223 ssl_data_.push_back(
1224 new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
1225 ssl_data_[0]->cert =
1226 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1227 ASSERT_TRUE(ssl_data_[0]->cert.get());
1228 scoped_ptr<DeterministicSocketData> raw_socket_data(BuildNullSocketData());
1229 CreateAndConnectRawExpectations("wss://localhost/",
1230 NoSubProtocols(),
1231 "http://localhost",
1232 raw_socket_data.Pass());
1233 RunUntilIdle();
1234 EXPECT_FALSE(has_failed());
1235 ASSERT_TRUE(ssl_error_callbacks_);
1236 ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
1237 &ssl_info_);
1238 RunUntilIdle();
1239 EXPECT_TRUE(has_failed());
1242 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
1243 scoped_ptr<SSLSocketDataProvider> ssl_data(
1244 new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
1245 ssl_data->cert =
1246 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1247 ASSERT_TRUE(ssl_data->cert.get());
1248 ssl_data_.push_back(ssl_data.release());
1249 ssl_data.reset(new SSLSocketDataProvider(ASYNC, OK));
1250 ssl_data_.push_back(ssl_data.release());
1251 url_request_context_host_.AddRawExpectations(BuildNullSocketData());
1252 CreateAndConnectStandard("wss://localhost/", "localhost", "/",
1253 NoSubProtocols(), "http://localhost", "", "");
1254 RunUntilIdle();
1255 ASSERT_TRUE(ssl_error_callbacks_);
1256 ssl_error_callbacks_->ContinueSSLRequest();
1257 RunUntilIdle();
1258 EXPECT_FALSE(has_failed());
1259 EXPECT_TRUE(stream_);
1262 // If the server requests authorisation, but we have no credentials, the
1263 // connection should fail cleanly.
1264 TEST_F(WebSocketStreamCreateBasicAuthTest, FailureNoCredentials) {
1265 CreateAndConnectCustomResponse("ws://localhost/", "localhost", "/",
1266 NoSubProtocols(), "http://localhost", "",
1267 kUnauthorizedResponse);
1268 RunUntilIdle();
1269 EXPECT_TRUE(has_failed());
1270 EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1271 failure_message());
1272 EXPECT_TRUE(response_info_);
1275 TEST_F(WebSocketStreamCreateBasicAuthTest, SuccessPasswordInUrl) {
1276 CreateAndConnectAuthHandshake("ws://foo:bar@localhost/",
1277 "Zm9vOmJhcg==",
1278 WebSocketStandardResponse(std::string()));
1279 RunUntilIdle();
1280 EXPECT_FALSE(has_failed());
1281 EXPECT_TRUE(stream_);
1282 ASSERT_TRUE(response_info_);
1283 EXPECT_EQ(101, response_info_->status_code);
1286 TEST_F(WebSocketStreamCreateBasicAuthTest, FailureIncorrectPasswordInUrl) {
1287 CreateAndConnectAuthHandshake(
1288 "ws://foo:baz@localhost/", "Zm9vOmJheg==", kUnauthorizedResponse);
1289 RunUntilIdle();
1290 EXPECT_TRUE(has_failed());
1291 EXPECT_TRUE(response_info_);
1294 // Digest auth has the same connection semantics as Basic auth, so we can
1295 // generally assume that whatever works for Basic auth will also work for
1296 // Digest. There's just one test here, to confirm that it works at all.
1297 TEST_F(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {
1298 AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
1300 CreateAndConnectRawExpectations(
1301 "ws://FooBar:pass@localhost/",
1302 NoSubProtocols(),
1303 "http://localhost",
1304 helper_.BuildSocketData2(kAuthorizedRequest,
1305 WebSocketStandardResponse(std::string())));
1306 RunUntilIdle();
1307 EXPECT_FALSE(has_failed());
1308 EXPECT_TRUE(stream_);
1309 ASSERT_TRUE(response_info_);
1310 EXPECT_EQ(101, response_info_->status_code);
1313 TEST_F(WebSocketStreamCreateUMATest, Incomplete) {
1314 const std::string name("Net.WebSocket.HandshakeResult");
1315 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1318 StreamCreation creation;
1319 creation.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1320 creation.NoSubProtocols(),
1321 "http://localhost", "", "");
1324 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1325 ASSERT_TRUE(samples);
1326 if (original) {
1327 samples->Subtract(*original); // Cancel the original values.
1329 EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
1330 EXPECT_EQ(0, samples->GetCount(CONNECTED));
1331 EXPECT_EQ(0, samples->GetCount(FAILED));
1334 TEST_F(WebSocketStreamCreateUMATest, Connected) {
1335 const std::string name("Net.WebSocket.HandshakeResult");
1336 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1339 StreamCreation creation;
1340 creation.CreateAndConnectStandard("ws://localhost/", "localhost", "/",
1341 creation.NoSubProtocols(),
1342 "http://localhost", "", "");
1343 creation.RunUntilIdle();
1346 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1347 ASSERT_TRUE(samples);
1348 if (original) {
1349 samples->Subtract(*original); // Cancel the original values.
1351 EXPECT_EQ(0, samples->GetCount(INCOMPLETE));
1352 EXPECT_EQ(1, samples->GetCount(CONNECTED));
1353 EXPECT_EQ(0, samples->GetCount(FAILED));
1356 TEST_F(WebSocketStreamCreateUMATest, Failed) {
1357 const std::string name("Net.WebSocket.HandshakeResult");
1358 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1361 StreamCreation creation;
1362 static const char kInvalidStatusCodeResponse[] =
1363 "HTTP/1.1 200 OK\r\n"
1364 "Upgrade: websocket\r\n"
1365 "Connection: Upgrade\r\n"
1366 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1367 "\r\n";
1368 creation.CreateAndConnectCustomResponse(
1369 "ws://localhost/", "localhost", "/", creation.NoSubProtocols(),
1370 "http://localhost", "", kInvalidStatusCodeResponse);
1371 creation.RunUntilIdle();
1374 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1375 ASSERT_TRUE(samples);
1376 if (original) {
1377 samples->Subtract(*original); // Cancel the original values.
1379 EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
1380 EXPECT_EQ(0, samples->GetCount(CONNECTED));
1381 EXPECT_EQ(0, samples->GetCount(FAILED));
1384 } // namespace
1385 } // namespace net