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