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