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_job.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/callback.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/strings/string_split.h" 15 #include "base/strings/string_util.h" 16 #include "net/base/completion_callback.h" 17 #include "net/base/net_errors.h" 18 #include "net/base/test_completion_callback.h" 19 #include "net/cookies/cookie_store.h" 20 #include "net/cookies/cookie_store_test_helpers.h" 21 #include "net/dns/mock_host_resolver.h" 22 #include "net/http/http_transaction_factory.h" 23 #include "net/http/transport_security_state.h" 24 #include "net/proxy/proxy_service.h" 25 #include "net/socket/next_proto.h" 26 #include "net/socket/socket_test_util.h" 27 #include "net/socket_stream/socket_stream.h" 28 #include "net/spdy/spdy_session.h" 29 #include "net/spdy/spdy_websocket_test_util.h" 30 #include "net/ssl/ssl_config_service.h" 31 #include "net/url_request/url_request_context.h" 32 #include "net/websockets/websocket_throttle.h" 33 #include "testing/gmock/include/gmock/gmock.h" 34 #include "testing/gtest/include/gtest/gtest.h" 35 #include "testing/platform_test.h" 36 #include "url/gurl.h" 37 38 namespace net { 39 40 namespace { 41 42 class MockSocketStream : public SocketStream { 43 public: 44 MockSocketStream(const GURL& url, SocketStream::Delegate* delegate) 45 : SocketStream(url, delegate) {} 46 47 virtual void Connect() OVERRIDE {} 48 virtual bool SendData(const char* data, int len) OVERRIDE { 49 sent_data_ += std::string(data, len); 50 return true; 51 } 52 53 virtual void Close() OVERRIDE {} 54 virtual void RestartWithAuth( 55 const AuthCredentials& credentials) OVERRIDE { 56 } 57 58 virtual void DetachDelegate() OVERRIDE { 59 delegate_ = NULL; 60 } 61 62 const std::string& sent_data() const { 63 return sent_data_; 64 } 65 66 protected: 67 virtual ~MockSocketStream() {} 68 69 private: 70 std::string sent_data_; 71 }; 72 73 class MockSocketStreamDelegate : public SocketStream::Delegate { 74 public: 75 MockSocketStreamDelegate() 76 : amount_sent_(0), allow_all_cookies_(true) {} 77 void set_allow_all_cookies(bool allow_all_cookies) { 78 allow_all_cookies_ = allow_all_cookies; 79 } 80 virtual ~MockSocketStreamDelegate() {} 81 82 void SetOnStartOpenConnection(const base::Closure& callback) { 83 on_start_open_connection_ = callback; 84 } 85 void SetOnConnected(const base::Closure& callback) { 86 on_connected_ = callback; 87 } 88 void SetOnSentData(const base::Closure& callback) { 89 on_sent_data_ = callback; 90 } 91 void SetOnReceivedData(const base::Closure& callback) { 92 on_received_data_ = callback; 93 } 94 void SetOnClose(const base::Closure& callback) { 95 on_close_ = callback; 96 } 97 98 virtual int OnStartOpenConnection( 99 SocketStream* socket, 100 const CompletionCallback& callback) OVERRIDE { 101 if (!on_start_open_connection_.is_null()) 102 on_start_open_connection_.Run(); 103 return OK; 104 } 105 virtual void OnConnected(SocketStream* socket, 106 int max_pending_send_allowed) OVERRIDE { 107 if (!on_connected_.is_null()) 108 on_connected_.Run(); 109 } 110 virtual void OnSentData(SocketStream* socket, 111 int amount_sent) OVERRIDE { 112 amount_sent_ += amount_sent; 113 if (!on_sent_data_.is_null()) 114 on_sent_data_.Run(); 115 } 116 virtual void OnReceivedData(SocketStream* socket, 117 const char* data, int len) OVERRIDE { 118 received_data_ += std::string(data, len); 119 if (!on_received_data_.is_null()) 120 on_received_data_.Run(); 121 } 122 virtual void OnClose(SocketStream* socket) OVERRIDE { 123 if (!on_close_.is_null()) 124 on_close_.Run(); 125 } 126 virtual bool CanGetCookies(SocketStream* socket, 127 const GURL& url) OVERRIDE { 128 return allow_all_cookies_; 129 } 130 virtual bool CanSetCookie(SocketStream* request, 131 const GURL& url, 132 const std::string& cookie_line, 133 CookieOptions* options) OVERRIDE { 134 return allow_all_cookies_; 135 } 136 137 size_t amount_sent() const { return amount_sent_; } 138 const std::string& received_data() const { return received_data_; } 139 140 private: 141 int amount_sent_; 142 bool allow_all_cookies_; 143 std::string received_data_; 144 base::Closure on_start_open_connection_; 145 base::Closure on_connected_; 146 base::Closure on_sent_data_; 147 base::Closure on_received_data_; 148 base::Closure on_close_; 149 }; 150 151 class MockCookieStore : public CookieStore { 152 public: 153 struct Entry { 154 GURL url; 155 std::string cookie_line; 156 CookieOptions options; 157 }; 158 159 MockCookieStore() {} 160 161 bool SetCookieWithOptions(const GURL& url, 162 const std::string& cookie_line, 163 const CookieOptions& options) { 164 Entry entry; 165 entry.url = url; 166 entry.cookie_line = cookie_line; 167 entry.options = options; 168 entries_.push_back(entry); 169 return true; 170 } 171 172 std::string GetCookiesWithOptions(const GURL& url, 173 const CookieOptions& options) { 174 std::string result; 175 for (size_t i = 0; i < entries_.size(); i++) { 176 Entry& entry = entries_[i]; 177 if (url == entry.url) { 178 if (!result.empty()) { 179 result += "; "; 180 } 181 result += entry.cookie_line; 182 } 183 } 184 return result; 185 } 186 187 // CookieStore: 188 virtual void SetCookieWithOptionsAsync( 189 const GURL& url, 190 const std::string& cookie_line, 191 const CookieOptions& options, 192 const SetCookiesCallback& callback) OVERRIDE { 193 bool result = SetCookieWithOptions(url, cookie_line, options); 194 if (!callback.is_null()) 195 callback.Run(result); 196 } 197 198 virtual void GetCookiesWithOptionsAsync( 199 const GURL& url, 200 const CookieOptions& options, 201 const GetCookiesCallback& callback) OVERRIDE { 202 if (!callback.is_null()) 203 callback.Run(GetCookiesWithOptions(url, options)); 204 } 205 206 virtual void DeleteCookieAsync(const GURL& url, 207 const std::string& cookie_name, 208 const base::Closure& callback) OVERRIDE { 209 ADD_FAILURE(); 210 } 211 212 virtual void DeleteAllCreatedBetweenAsync( 213 const base::Time& delete_begin, 214 const base::Time& delete_end, 215 const DeleteCallback& callback) OVERRIDE { 216 ADD_FAILURE(); 217 } 218 219 virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE { 220 ADD_FAILURE(); 221 } 222 223 virtual CookieMonster* GetCookieMonster() OVERRIDE { return NULL; } 224 225 const std::vector<Entry>& entries() const { return entries_; } 226 227 private: 228 friend class base::RefCountedThreadSafe<MockCookieStore>; 229 virtual ~MockCookieStore() {} 230 231 std::vector<Entry> entries_; 232 }; 233 234 class MockSSLConfigService : public SSLConfigService { 235 public: 236 virtual void GetSSLConfig(SSLConfig* config) OVERRIDE {} 237 238 protected: 239 virtual ~MockSSLConfigService() {} 240 }; 241 242 class MockURLRequestContext : public URLRequestContext { 243 public: 244 explicit MockURLRequestContext(CookieStore* cookie_store) 245 : transport_security_state_() { 246 set_cookie_store(cookie_store); 247 set_transport_security_state(&transport_security_state_); 248 base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000); 249 bool include_subdomains = false; 250 transport_security_state_.AddHSTS("upgrademe.com", expiry, 251 include_subdomains); 252 } 253 254 virtual ~MockURLRequestContext() {} 255 256 private: 257 TransportSecurityState transport_security_state_; 258 }; 259 260 class MockHttpTransactionFactory : public HttpTransactionFactory { 261 public: 262 MockHttpTransactionFactory(NextProto next_proto, OrderedSocketData* data) { 263 data_ = data; 264 MockConnect connect_data(SYNCHRONOUS, OK); 265 data_->set_connect_data(connect_data); 266 session_deps_.reset(new SpdySessionDependencies(next_proto)); 267 session_deps_->socket_factory->AddSocketDataProvider(data_); 268 http_session_ = 269 SpdySessionDependencies::SpdyCreateSession(session_deps_.get()); 270 host_port_pair_.set_host("example.com"); 271 host_port_pair_.set_port(80); 272 spdy_session_key_ = SpdySessionKey(host_port_pair_, 273 ProxyServer::Direct(), 274 kPrivacyModeDisabled); 275 session_ = CreateInsecureSpdySession( 276 http_session_, spdy_session_key_, BoundNetLog()); 277 } 278 279 virtual int CreateTransaction( 280 RequestPriority priority, 281 scoped_ptr<HttpTransaction>* trans, 282 HttpTransactionDelegate* delegate) OVERRIDE { 283 NOTREACHED(); 284 return ERR_UNEXPECTED; 285 } 286 287 virtual HttpCache* GetCache() OVERRIDE { 288 NOTREACHED(); 289 return NULL; 290 } 291 292 virtual HttpNetworkSession* GetSession() OVERRIDE { 293 return http_session_.get(); 294 } 295 296 private: 297 OrderedSocketData* data_; 298 scoped_ptr<SpdySessionDependencies> session_deps_; 299 scoped_refptr<HttpNetworkSession> http_session_; 300 base::WeakPtr<SpdySession> session_; 301 HostPortPair host_port_pair_; 302 SpdySessionKey spdy_session_key_; 303 }; 304 305 } // namespace 306 307 class WebSocketJobTest : public PlatformTest, 308 public ::testing::WithParamInterface<NextProto> { 309 public: 310 WebSocketJobTest() : spdy_util_(GetParam()) {} 311 312 virtual void SetUp() OVERRIDE { 313 stream_type_ = STREAM_INVALID; 314 cookie_store_ = new MockCookieStore; 315 context_.reset(new MockURLRequestContext(cookie_store_.get())); 316 } 317 virtual void TearDown() OVERRIDE { 318 cookie_store_ = NULL; 319 context_.reset(); 320 websocket_ = NULL; 321 socket_ = NULL; 322 } 323 void DoSendRequest() { 324 EXPECT_TRUE(websocket_->SendData(kHandshakeRequestWithoutCookie, 325 kHandshakeRequestWithoutCookieLength)); 326 } 327 void DoSendData() { 328 if (received_data().size() == kHandshakeResponseWithoutCookieLength) 329 websocket_->SendData(kDataHello, kDataHelloLength); 330 } 331 void DoSync() { 332 sync_test_callback_.callback().Run(OK); 333 } 334 int WaitForResult() { 335 return sync_test_callback_.WaitForResult(); 336 } 337 protected: 338 enum StreamType { 339 STREAM_INVALID, 340 STREAM_MOCK_SOCKET, 341 STREAM_SOCKET, 342 STREAM_SPDY_WEBSOCKET, 343 }; 344 enum ThrottlingOption { 345 THROTTLING_OFF, 346 THROTTLING_ON, 347 }; 348 enum SpdyOption { 349 SPDY_OFF, 350 SPDY_ON, 351 }; 352 void InitWebSocketJob(const GURL& url, 353 MockSocketStreamDelegate* delegate, 354 StreamType stream_type) { 355 DCHECK_NE(STREAM_INVALID, stream_type); 356 stream_type_ = stream_type; 357 websocket_ = new WebSocketJob(delegate); 358 359 if (stream_type == STREAM_MOCK_SOCKET) 360 socket_ = new MockSocketStream(url, websocket_.get()); 361 362 if (stream_type == STREAM_SOCKET || stream_type == STREAM_SPDY_WEBSOCKET) { 363 if (stream_type == STREAM_SPDY_WEBSOCKET) { 364 http_factory_.reset( 365 new MockHttpTransactionFactory(GetParam(), data_.get())); 366 context_->set_http_transaction_factory(http_factory_.get()); 367 } 368 369 ssl_config_service_ = new MockSSLConfigService(); 370 context_->set_ssl_config_service(ssl_config_service_.get()); 371 proxy_service_.reset(ProxyService::CreateDirect()); 372 context_->set_proxy_service(proxy_service_.get()); 373 host_resolver_.reset(new MockHostResolver); 374 context_->set_host_resolver(host_resolver_.get()); 375 376 socket_ = new SocketStream(url, websocket_.get()); 377 socket_factory_.reset(new MockClientSocketFactory); 378 DCHECK(data_.get()); 379 socket_factory_->AddSocketDataProvider(data_.get()); 380 socket_->SetClientSocketFactory(socket_factory_.get()); 381 } 382 383 websocket_->InitSocketStream(socket_.get()); 384 websocket_->set_context(context_.get()); 385 // MockHostResolver resolves all hosts to 127.0.0.1; however, when we create 386 // a WebSocketJob purely to block another one in a throttling test, we don't 387 // perform a real connect. In that case, the following address is used 388 // instead. 389 IPAddressNumber ip; 390 ParseIPLiteralToNumber("127.0.0.1", &ip); 391 websocket_->addresses_ = AddressList::CreateFromIPAddress(ip, 80); 392 } 393 void SkipToConnecting() { 394 websocket_->state_ = WebSocketJob::CONNECTING; 395 ASSERT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(websocket_.get())); 396 } 397 WebSocketJob::State GetWebSocketJobState() { 398 return websocket_->state_; 399 } 400 void CloseWebSocketJob() { 401 if (websocket_->socket_.get()) { 402 websocket_->socket_->DetachDelegate(); 403 WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_.get()); 404 } 405 websocket_->state_ = WebSocketJob::CLOSED; 406 websocket_->delegate_ = NULL; 407 websocket_->socket_ = NULL; 408 } 409 SocketStream* GetSocket(SocketStreamJob* job) { 410 return job->socket_.get(); 411 } 412 const std::string& sent_data() const { 413 DCHECK_EQ(STREAM_MOCK_SOCKET, stream_type_); 414 MockSocketStream* socket = 415 static_cast<MockSocketStream*>(socket_.get()); 416 DCHECK(socket); 417 return socket->sent_data(); 418 } 419 const std::string& received_data() const { 420 DCHECK_NE(STREAM_INVALID, stream_type_); 421 MockSocketStreamDelegate* delegate = 422 static_cast<MockSocketStreamDelegate*>(websocket_->delegate_); 423 DCHECK(delegate); 424 return delegate->received_data(); 425 } 426 427 void TestSimpleHandshake(); 428 void TestSlowHandshake(); 429 void TestHandshakeWithCookie(); 430 void TestHandshakeWithCookieButNotAllowed(); 431 void TestHSTSUpgrade(); 432 void TestInvalidSendData(); 433 void TestConnectByWebSocket(ThrottlingOption throttling); 434 void TestConnectBySpdy(SpdyOption spdy, ThrottlingOption throttling); 435 void TestThrottlingLimit(); 436 437 SpdyWebSocketTestUtil spdy_util_; 438 StreamType stream_type_; 439 scoped_refptr<MockCookieStore> cookie_store_; 440 scoped_ptr<MockURLRequestContext> context_; 441 scoped_refptr<WebSocketJob> websocket_; 442 scoped_refptr<SocketStream> socket_; 443 scoped_ptr<MockClientSocketFactory> socket_factory_; 444 scoped_ptr<OrderedSocketData> data_; 445 TestCompletionCallback sync_test_callback_; 446 scoped_refptr<MockSSLConfigService> ssl_config_service_; 447 scoped_ptr<ProxyService> proxy_service_; 448 scoped_ptr<MockHostResolver> host_resolver_; 449 scoped_ptr<MockHttpTransactionFactory> http_factory_; 450 451 static const char kHandshakeRequestWithoutCookie[]; 452 static const char kHandshakeRequestWithCookie[]; 453 static const char kHandshakeRequestWithFilteredCookie[]; 454 static const char kHandshakeResponseWithoutCookie[]; 455 static const char kHandshakeResponseWithCookie[]; 456 static const char kDataHello[]; 457 static const char kDataWorld[]; 458 static const char* const kHandshakeRequestForSpdy[]; 459 static const char* const kHandshakeResponseForSpdy[]; 460 static const size_t kHandshakeRequestWithoutCookieLength; 461 static const size_t kHandshakeRequestWithCookieLength; 462 static const size_t kHandshakeRequestWithFilteredCookieLength; 463 static const size_t kHandshakeResponseWithoutCookieLength; 464 static const size_t kHandshakeResponseWithCookieLength; 465 static const size_t kDataHelloLength; 466 static const size_t kDataWorldLength; 467 }; 468 469 const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] = 470 "GET /demo HTTP/1.1\r\n" 471 "Host: example.com\r\n" 472 "Upgrade: WebSocket\r\n" 473 "Connection: Upgrade\r\n" 474 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 475 "Origin: http://example.com\r\n" 476 "Sec-WebSocket-Protocol: sample\r\n" 477 "Sec-WebSocket-Version: 13\r\n" 478 "\r\n"; 479 480 const char WebSocketJobTest::kHandshakeRequestWithCookie[] = 481 "GET /demo HTTP/1.1\r\n" 482 "Host: example.com\r\n" 483 "Upgrade: WebSocket\r\n" 484 "Connection: Upgrade\r\n" 485 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 486 "Origin: http://example.com\r\n" 487 "Sec-WebSocket-Protocol: sample\r\n" 488 "Sec-WebSocket-Version: 13\r\n" 489 "Cookie: WK-test=1\r\n" 490 "\r\n"; 491 492 const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie[] = 493 "GET /demo HTTP/1.1\r\n" 494 "Host: example.com\r\n" 495 "Upgrade: WebSocket\r\n" 496 "Connection: Upgrade\r\n" 497 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 498 "Origin: http://example.com\r\n" 499 "Sec-WebSocket-Protocol: sample\r\n" 500 "Sec-WebSocket-Version: 13\r\n" 501 "Cookie: CR-test=1; CR-test-httponly=1\r\n" 502 "\r\n"; 503 504 const char WebSocketJobTest::kHandshakeResponseWithoutCookie[] = 505 "HTTP/1.1 101 Switching Protocols\r\n" 506 "Upgrade: websocket\r\n" 507 "Connection: Upgrade\r\n" 508 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" 509 "Sec-WebSocket-Protocol: sample\r\n" 510 "\r\n"; 511 512 const char WebSocketJobTest::kHandshakeResponseWithCookie[] = 513 "HTTP/1.1 101 Switching Protocols\r\n" 514 "Upgrade: websocket\r\n" 515 "Connection: Upgrade\r\n" 516 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" 517 "Sec-WebSocket-Protocol: sample\r\n" 518 "Set-Cookie: CR-set-test=1\r\n" 519 "\r\n"; 520 521 const char WebSocketJobTest::kDataHello[] = "Hello, "; 522 523 const char WebSocketJobTest::kDataWorld[] = "World!\n"; 524 525 const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength = 526 arraysize(kHandshakeRequestWithoutCookie) - 1; 527 const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength = 528 arraysize(kHandshakeRequestWithCookie) - 1; 529 const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength = 530 arraysize(kHandshakeRequestWithFilteredCookie) - 1; 531 const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength = 532 arraysize(kHandshakeResponseWithoutCookie) - 1; 533 const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength = 534 arraysize(kHandshakeResponseWithCookie) - 1; 535 const size_t WebSocketJobTest::kDataHelloLength = 536 arraysize(kDataHello) - 1; 537 const size_t WebSocketJobTest::kDataWorldLength = 538 arraysize(kDataWorld) - 1; 539 540 void WebSocketJobTest::TestSimpleHandshake() { 541 GURL url("ws://example.com/demo"); 542 MockSocketStreamDelegate delegate; 543 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 544 SkipToConnecting(); 545 546 DoSendRequest(); 547 base::MessageLoop::current()->RunUntilIdle(); 548 EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); 549 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 550 websocket_->OnSentData(socket_.get(), 551 kHandshakeRequestWithoutCookieLength); 552 EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); 553 554 websocket_->OnReceivedData(socket_.get(), 555 kHandshakeResponseWithoutCookie, 556 kHandshakeResponseWithoutCookieLength); 557 base::MessageLoop::current()->RunUntilIdle(); 558 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 559 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 560 CloseWebSocketJob(); 561 } 562 563 void WebSocketJobTest::TestSlowHandshake() { 564 GURL url("ws://example.com/demo"); 565 MockSocketStreamDelegate delegate; 566 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 567 SkipToConnecting(); 568 569 DoSendRequest(); 570 // We assume request is sent in one data chunk (from WebKit) 571 // We don't support streaming request. 572 base::MessageLoop::current()->RunUntilIdle(); 573 EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); 574 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 575 websocket_->OnSentData(socket_.get(), 576 kHandshakeRequestWithoutCookieLength); 577 EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); 578 579 std::vector<std::string> lines; 580 base::SplitString(kHandshakeResponseWithoutCookie, '\n', &lines); 581 for (size_t i = 0; i < lines.size() - 2; i++) { 582 std::string line = lines[i] + "\r\n"; 583 SCOPED_TRACE("Line: " + line); 584 websocket_->OnReceivedData(socket_.get(), line.c_str(), line.size()); 585 base::MessageLoop::current()->RunUntilIdle(); 586 EXPECT_TRUE(delegate.received_data().empty()); 587 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 588 } 589 websocket_->OnReceivedData(socket_.get(), "\r\n", 2); 590 base::MessageLoop::current()->RunUntilIdle(); 591 EXPECT_FALSE(delegate.received_data().empty()); 592 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 593 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 594 CloseWebSocketJob(); 595 } 596 597 INSTANTIATE_TEST_CASE_P( 598 NextProto, 599 WebSocketJobTest, 600 testing::Values(kProtoDeprecatedSPDY2, 601 kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, 602 kProtoHTTP2Draft04)); 603 604 TEST_P(WebSocketJobTest, DelayedCookies) { 605 WebSocketJob::set_websocket_over_spdy_enabled(true); 606 GURL url("ws://example.com/demo"); 607 GURL cookieUrl("http://example.com/demo"); 608 CookieOptions cookie_options; 609 scoped_refptr<DelayedCookieMonster> cookie_store = new DelayedCookieMonster(); 610 context_->set_cookie_store(cookie_store.get()); 611 cookie_store->SetCookieWithOptionsAsync(cookieUrl, 612 "CR-test=1", 613 cookie_options, 614 CookieMonster::SetCookiesCallback()); 615 cookie_options.set_include_httponly(); 616 cookie_store->SetCookieWithOptionsAsync( 617 cookieUrl, "CR-test-httponly=1", cookie_options, 618 CookieMonster::SetCookiesCallback()); 619 620 MockSocketStreamDelegate delegate; 621 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 622 SkipToConnecting(); 623 624 bool sent = websocket_->SendData(kHandshakeRequestWithCookie, 625 kHandshakeRequestWithCookieLength); 626 EXPECT_TRUE(sent); 627 base::MessageLoop::current()->RunUntilIdle(); 628 EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data()); 629 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 630 websocket_->OnSentData(socket_.get(), 631 kHandshakeRequestWithFilteredCookieLength); 632 EXPECT_EQ(kHandshakeRequestWithCookieLength, 633 delegate.amount_sent()); 634 635 websocket_->OnReceivedData(socket_.get(), 636 kHandshakeResponseWithCookie, 637 kHandshakeResponseWithCookieLength); 638 base::MessageLoop::current()->RunUntilIdle(); 639 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 640 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 641 642 CloseWebSocketJob(); 643 } 644 645 void WebSocketJobTest::TestHandshakeWithCookie() { 646 GURL url("ws://example.com/demo"); 647 GURL cookieUrl("http://example.com/demo"); 648 CookieOptions cookie_options; 649 cookie_store_->SetCookieWithOptions( 650 cookieUrl, "CR-test=1", cookie_options); 651 cookie_options.set_include_httponly(); 652 cookie_store_->SetCookieWithOptions( 653 cookieUrl, "CR-test-httponly=1", cookie_options); 654 655 MockSocketStreamDelegate delegate; 656 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 657 SkipToConnecting(); 658 659 bool sent = websocket_->SendData(kHandshakeRequestWithCookie, 660 kHandshakeRequestWithCookieLength); 661 EXPECT_TRUE(sent); 662 base::MessageLoop::current()->RunUntilIdle(); 663 EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data()); 664 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 665 websocket_->OnSentData(socket_.get(), 666 kHandshakeRequestWithFilteredCookieLength); 667 EXPECT_EQ(kHandshakeRequestWithCookieLength, 668 delegate.amount_sent()); 669 670 websocket_->OnReceivedData(socket_.get(), 671 kHandshakeResponseWithCookie, 672 kHandshakeResponseWithCookieLength); 673 base::MessageLoop::current()->RunUntilIdle(); 674 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 675 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 676 677 EXPECT_EQ(3U, cookie_store_->entries().size()); 678 EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url); 679 EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line); 680 EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url); 681 EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line); 682 EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url); 683 EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line); 684 685 CloseWebSocketJob(); 686 } 687 688 void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() { 689 GURL url("ws://example.com/demo"); 690 GURL cookieUrl("http://example.com/demo"); 691 CookieOptions cookie_options; 692 cookie_store_->SetCookieWithOptions( 693 cookieUrl, "CR-test=1", cookie_options); 694 cookie_options.set_include_httponly(); 695 cookie_store_->SetCookieWithOptions( 696 cookieUrl, "CR-test-httponly=1", cookie_options); 697 698 MockSocketStreamDelegate delegate; 699 delegate.set_allow_all_cookies(false); 700 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 701 SkipToConnecting(); 702 703 bool sent = websocket_->SendData(kHandshakeRequestWithCookie, 704 kHandshakeRequestWithCookieLength); 705 EXPECT_TRUE(sent); 706 base::MessageLoop::current()->RunUntilIdle(); 707 EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); 708 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 709 websocket_->OnSentData(socket_.get(), kHandshakeRequestWithoutCookieLength); 710 EXPECT_EQ(kHandshakeRequestWithCookieLength, delegate.amount_sent()); 711 712 websocket_->OnReceivedData(socket_.get(), 713 kHandshakeResponseWithCookie, 714 kHandshakeResponseWithCookieLength); 715 base::MessageLoop::current()->RunUntilIdle(); 716 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 717 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 718 719 EXPECT_EQ(2U, cookie_store_->entries().size()); 720 EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url); 721 EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line); 722 EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url); 723 EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line); 724 725 CloseWebSocketJob(); 726 } 727 728 void WebSocketJobTest::TestHSTSUpgrade() { 729 GURL url("ws://upgrademe.com/"); 730 MockSocketStreamDelegate delegate; 731 scoped_refptr<SocketStreamJob> job = 732 SocketStreamJob::CreateSocketStreamJob( 733 url, &delegate, context_->transport_security_state(), 734 context_->ssl_config_service()); 735 EXPECT_TRUE(GetSocket(job.get())->is_secure()); 736 job->DetachDelegate(); 737 738 url = GURL("ws://donotupgrademe.com/"); 739 job = SocketStreamJob::CreateSocketStreamJob( 740 url, &delegate, context_->transport_security_state(), 741 context_->ssl_config_service()); 742 EXPECT_FALSE(GetSocket(job.get())->is_secure()); 743 job->DetachDelegate(); 744 } 745 746 void WebSocketJobTest::TestInvalidSendData() { 747 GURL url("ws://example.com/demo"); 748 MockSocketStreamDelegate delegate; 749 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 750 SkipToConnecting(); 751 752 DoSendRequest(); 753 // We assume request is sent in one data chunk (from WebKit) 754 // We don't support streaming request. 755 base::MessageLoop::current()->RunUntilIdle(); 756 EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); 757 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 758 websocket_->OnSentData(socket_.get(), 759 kHandshakeRequestWithoutCookieLength); 760 EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); 761 762 // We could not send any data until connection is established. 763 bool sent = websocket_->SendData(kHandshakeRequestWithoutCookie, 764 kHandshakeRequestWithoutCookieLength); 765 EXPECT_FALSE(sent); 766 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 767 CloseWebSocketJob(); 768 } 769 770 // Following tests verify cooperation between WebSocketJob and SocketStream. 771 // Other former tests use MockSocketStream as SocketStream, so we could not 772 // check SocketStream behavior. 773 // OrderedSocketData provide socket level verifiation by checking out-going 774 // packets in comparison with the MockWrite array and emulating in-coming 775 // packets with MockRead array. 776 777 void WebSocketJobTest::TestConnectByWebSocket( 778 ThrottlingOption throttling) { 779 // This is a test for verifying cooperation between WebSocketJob and 780 // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic 781 // situation. If |throttling| was |THROTTLING_ON|, throttling limits the 782 // latter connection. 783 MockWrite writes[] = { 784 MockWrite(ASYNC, 785 kHandshakeRequestWithoutCookie, 786 kHandshakeRequestWithoutCookieLength, 787 1), 788 MockWrite(ASYNC, 789 kDataHello, 790 kDataHelloLength, 791 3) 792 }; 793 MockRead reads[] = { 794 MockRead(ASYNC, 795 kHandshakeResponseWithoutCookie, 796 kHandshakeResponseWithoutCookieLength, 797 2), 798 MockRead(ASYNC, 799 kDataWorld, 800 kDataWorldLength, 801 4), 802 MockRead(SYNCHRONOUS, 0, 5) // EOF 803 }; 804 data_.reset(new OrderedSocketData( 805 reads, arraysize(reads), writes, arraysize(writes))); 806 807 GURL url("ws://example.com/demo"); 808 MockSocketStreamDelegate delegate; 809 WebSocketJobTest* test = this; 810 if (throttling == THROTTLING_ON) 811 delegate.SetOnStartOpenConnection( 812 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 813 delegate.SetOnConnected( 814 base::Bind(&WebSocketJobTest::DoSendRequest, 815 base::Unretained(test))); 816 delegate.SetOnReceivedData( 817 base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test))); 818 delegate.SetOnClose( 819 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 820 InitWebSocketJob(url, &delegate, STREAM_SOCKET); 821 822 scoped_refptr<WebSocketJob> block_websocket; 823 if (throttling == THROTTLING_ON) { 824 // Create former WebSocket object which obstructs the latter one. 825 block_websocket = new WebSocketJob(NULL); 826 block_websocket->addresses_ = AddressList(websocket_->address_list()); 827 ASSERT_TRUE( 828 WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get())); 829 } 830 831 websocket_->Connect(); 832 833 if (throttling == THROTTLING_ON) { 834 EXPECT_EQ(OK, WaitForResult()); 835 EXPECT_TRUE(websocket_->IsWaiting()); 836 837 // Remove the former WebSocket object from throttling queue to unblock the 838 // latter. 839 block_websocket->state_ = WebSocketJob::CLOSED; 840 WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get()); 841 block_websocket = NULL; 842 } 843 844 EXPECT_EQ(OK, WaitForResult()); 845 EXPECT_TRUE(data_->at_read_eof()); 846 EXPECT_TRUE(data_->at_write_eof()); 847 EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState()); 848 } 849 850 void WebSocketJobTest::TestConnectBySpdy( 851 SpdyOption spdy, ThrottlingOption throttling) { 852 // This is a test for verifying cooperation between WebSocketJob and 853 // SocketStream in the situation we have SPDY session to the server. If 854 // |throttling| was |THROTTLING_ON|, throttling limits the latter connection. 855 // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected 856 // results depend on its configuration. 857 MockWrite writes_websocket[] = { 858 MockWrite(ASYNC, 859 kHandshakeRequestWithoutCookie, 860 kHandshakeRequestWithoutCookieLength, 861 1), 862 MockWrite(ASYNC, 863 kDataHello, 864 kDataHelloLength, 865 3) 866 }; 867 MockRead reads_websocket[] = { 868 MockRead(ASYNC, 869 kHandshakeResponseWithoutCookie, 870 kHandshakeResponseWithoutCookieLength, 871 2), 872 MockRead(ASYNC, 873 kDataWorld, 874 kDataWorldLength, 875 4), 876 MockRead(SYNCHRONOUS, 0, 5) // EOF 877 }; 878 879 scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock()); 880 spdy_util_.SetHeader("path", "/demo", request_headers.get()); 881 spdy_util_.SetHeader("version", "WebSocket/13", request_headers.get()); 882 spdy_util_.SetHeader("scheme", "ws", request_headers.get()); 883 spdy_util_.SetHeader("host", "example.com", request_headers.get()); 884 spdy_util_.SetHeader("origin", "http://example.com", request_headers.get()); 885 spdy_util_.SetHeader("sec-websocket-protocol", "sample", 886 request_headers.get()); 887 888 scoped_ptr<SpdyHeaderBlock> response_headers(new SpdyHeaderBlock()); 889 spdy_util_.SetHeader("status", "101 Switching Protocols", 890 response_headers.get()); 891 spdy_util_.SetHeader("sec-websocket-protocol", "sample", 892 response_headers.get()); 893 894 const SpdyStreamId kStreamId = 1; 895 scoped_ptr<SpdyFrame> request_frame( 896 spdy_util_.ConstructSpdyWebSocketHandshakeRequestFrame( 897 request_headers.Pass(), 898 kStreamId, 899 MEDIUM)); 900 scoped_ptr<SpdyFrame> response_frame( 901 spdy_util_.ConstructSpdyWebSocketHandshakeResponseFrame( 902 response_headers.Pass(), 903 kStreamId, 904 MEDIUM)); 905 scoped_ptr<SpdyFrame> data_hello_frame( 906 spdy_util_.ConstructSpdyWebSocketDataFrame( 907 kDataHello, 908 kDataHelloLength, 909 kStreamId, 910 false)); 911 scoped_ptr<SpdyFrame> data_world_frame( 912 spdy_util_.ConstructSpdyWebSocketDataFrame( 913 kDataWorld, 914 kDataWorldLength, 915 kStreamId, 916 false)); 917 MockWrite writes_spdy[] = { 918 CreateMockWrite(*request_frame.get(), 1), 919 CreateMockWrite(*data_hello_frame.get(), 3), 920 }; 921 MockRead reads_spdy[] = { 922 CreateMockRead(*response_frame.get(), 2), 923 CreateMockRead(*data_world_frame.get(), 4), 924 MockRead(SYNCHRONOUS, 0, 5) // EOF 925 }; 926 927 if (spdy == SPDY_ON) 928 data_.reset(new OrderedSocketData( 929 reads_spdy, arraysize(reads_spdy), 930 writes_spdy, arraysize(writes_spdy))); 931 else 932 data_.reset(new OrderedSocketData( 933 reads_websocket, arraysize(reads_websocket), 934 writes_websocket, arraysize(writes_websocket))); 935 936 GURL url("ws://example.com/demo"); 937 MockSocketStreamDelegate delegate; 938 WebSocketJobTest* test = this; 939 if (throttling == THROTTLING_ON) 940 delegate.SetOnStartOpenConnection( 941 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 942 delegate.SetOnConnected( 943 base::Bind(&WebSocketJobTest::DoSendRequest, 944 base::Unretained(test))); 945 delegate.SetOnReceivedData( 946 base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test))); 947 delegate.SetOnClose( 948 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 949 InitWebSocketJob(url, &delegate, STREAM_SPDY_WEBSOCKET); 950 951 scoped_refptr<WebSocketJob> block_websocket; 952 if (throttling == THROTTLING_ON) { 953 // Create former WebSocket object which obstructs the latter one. 954 block_websocket = new WebSocketJob(NULL); 955 block_websocket->addresses_ = AddressList(websocket_->address_list()); 956 ASSERT_TRUE( 957 WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get())); 958 } 959 960 websocket_->Connect(); 961 962 if (throttling == THROTTLING_ON) { 963 EXPECT_EQ(OK, WaitForResult()); 964 EXPECT_TRUE(websocket_->IsWaiting()); 965 966 // Remove the former WebSocket object from throttling queue to unblock the 967 // latter. 968 block_websocket->state_ = WebSocketJob::CLOSED; 969 WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get()); 970 block_websocket = NULL; 971 } 972 973 EXPECT_EQ(OK, WaitForResult()); 974 EXPECT_TRUE(data_->at_read_eof()); 975 EXPECT_TRUE(data_->at_write_eof()); 976 EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState()); 977 } 978 979 void WebSocketJobTest::TestThrottlingLimit() { 980 std::vector<scoped_refptr<WebSocketJob> > jobs; 981 const int kMaxWebSocketJobsThrottled = 1024; 982 IPAddressNumber ip; 983 ParseIPLiteralToNumber("127.0.0.1", &ip); 984 for (int i = 0; i < kMaxWebSocketJobsThrottled + 1; ++i) { 985 scoped_refptr<WebSocketJob> job = new WebSocketJob(NULL); 986 job->addresses_ = AddressList(AddressList::CreateFromIPAddress(ip, 80)); 987 if (i >= kMaxWebSocketJobsThrottled) 988 EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job)); 989 else 990 EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job)); 991 jobs.push_back(job); 992 } 993 994 // Close the jobs in reverse order. Otherwise, We need to make them prepared 995 // for Wakeup call. 996 for (std::vector<scoped_refptr<WebSocketJob> >::reverse_iterator iter = 997 jobs.rbegin(); 998 iter != jobs.rend(); 999 ++iter) { 1000 WebSocketJob* job = (*iter).get(); 1001 job->state_ = WebSocketJob::CLOSED; 1002 WebSocketThrottle::GetInstance()->RemoveFromQueue(job); 1003 } 1004 } 1005 1006 // Execute tests in both spdy-disabled mode and spdy-enabled mode. 1007 TEST_P(WebSocketJobTest, SimpleHandshake) { 1008 WebSocketJob::set_websocket_over_spdy_enabled(false); 1009 TestSimpleHandshake(); 1010 } 1011 1012 TEST_P(WebSocketJobTest, SlowHandshake) { 1013 WebSocketJob::set_websocket_over_spdy_enabled(false); 1014 TestSlowHandshake(); 1015 } 1016 1017 TEST_P(WebSocketJobTest, HandshakeWithCookie) { 1018 WebSocketJob::set_websocket_over_spdy_enabled(false); 1019 TestHandshakeWithCookie(); 1020 } 1021 1022 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) { 1023 WebSocketJob::set_websocket_over_spdy_enabled(false); 1024 TestHandshakeWithCookieButNotAllowed(); 1025 } 1026 1027 TEST_P(WebSocketJobTest, HSTSUpgrade) { 1028 WebSocketJob::set_websocket_over_spdy_enabled(false); 1029 TestHSTSUpgrade(); 1030 } 1031 1032 TEST_P(WebSocketJobTest, InvalidSendData) { 1033 WebSocketJob::set_websocket_over_spdy_enabled(false); 1034 TestInvalidSendData(); 1035 } 1036 1037 TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) { 1038 WebSocketJob::set_websocket_over_spdy_enabled(true); 1039 TestSimpleHandshake(); 1040 } 1041 1042 TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) { 1043 WebSocketJob::set_websocket_over_spdy_enabled(true); 1044 TestSlowHandshake(); 1045 } 1046 1047 TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) { 1048 WebSocketJob::set_websocket_over_spdy_enabled(true); 1049 TestHandshakeWithCookie(); 1050 } 1051 1052 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) { 1053 WebSocketJob::set_websocket_over_spdy_enabled(true); 1054 TestHandshakeWithCookieButNotAllowed(); 1055 } 1056 1057 TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) { 1058 WebSocketJob::set_websocket_over_spdy_enabled(true); 1059 TestHSTSUpgrade(); 1060 } 1061 1062 TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) { 1063 WebSocketJob::set_websocket_over_spdy_enabled(true); 1064 TestInvalidSendData(); 1065 } 1066 1067 TEST_P(WebSocketJobTest, ConnectByWebSocket) { 1068 WebSocketJob::set_websocket_over_spdy_enabled(false); 1069 TestConnectByWebSocket(THROTTLING_OFF); 1070 } 1071 1072 TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) { 1073 WebSocketJob::set_websocket_over_spdy_enabled(true); 1074 TestConnectByWebSocket(THROTTLING_OFF); 1075 } 1076 1077 TEST_P(WebSocketJobTest, ConnectBySpdy) { 1078 WebSocketJob::set_websocket_over_spdy_enabled(false); 1079 TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF); 1080 } 1081 1082 TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) { 1083 WebSocketJob::set_websocket_over_spdy_enabled(true); 1084 TestConnectBySpdy(SPDY_ON, THROTTLING_OFF); 1085 } 1086 1087 TEST_P(WebSocketJobTest, ThrottlingWebSocket) { 1088 WebSocketJob::set_websocket_over_spdy_enabled(false); 1089 TestConnectByWebSocket(THROTTLING_ON); 1090 } 1091 1092 TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) { 1093 TestThrottlingLimit(); 1094 } 1095 1096 TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) { 1097 WebSocketJob::set_websocket_over_spdy_enabled(true); 1098 TestConnectByWebSocket(THROTTLING_ON); 1099 } 1100 1101 TEST_P(WebSocketJobTest, ThrottlingSpdy) { 1102 WebSocketJob::set_websocket_over_spdy_enabled(false); 1103 TestConnectBySpdy(SPDY_OFF, THROTTLING_ON); 1104 } 1105 1106 TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) { 1107 WebSocketJob::set_websocket_over_spdy_enabled(true); 1108 TestConnectBySpdy(SPDY_ON, THROTTLING_ON); 1109 } 1110 1111 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. 1112 // TODO(toyoshim,yutak): Add tests to verify closing handshake. 1113 } // namespace net 1114