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(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, 601 kProtoHTTP2Draft04)); 602 603 TEST_P(WebSocketJobTest, DelayedCookies) { 604 WebSocketJob::set_websocket_over_spdy_enabled(true); 605 GURL url("ws://example.com/demo"); 606 GURL cookieUrl("http://example.com/demo"); 607 CookieOptions cookie_options; 608 scoped_refptr<DelayedCookieMonster> cookie_store = new DelayedCookieMonster(); 609 context_->set_cookie_store(cookie_store.get()); 610 cookie_store->SetCookieWithOptionsAsync(cookieUrl, 611 "CR-test=1", 612 cookie_options, 613 CookieMonster::SetCookiesCallback()); 614 cookie_options.set_include_httponly(); 615 cookie_store->SetCookieWithOptionsAsync( 616 cookieUrl, "CR-test-httponly=1", cookie_options, 617 CookieMonster::SetCookiesCallback()); 618 619 MockSocketStreamDelegate delegate; 620 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 621 SkipToConnecting(); 622 623 bool sent = websocket_->SendData(kHandshakeRequestWithCookie, 624 kHandshakeRequestWithCookieLength); 625 EXPECT_TRUE(sent); 626 base::MessageLoop::current()->RunUntilIdle(); 627 EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data()); 628 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 629 websocket_->OnSentData(socket_.get(), 630 kHandshakeRequestWithFilteredCookieLength); 631 EXPECT_EQ(kHandshakeRequestWithCookieLength, 632 delegate.amount_sent()); 633 634 websocket_->OnReceivedData(socket_.get(), 635 kHandshakeResponseWithCookie, 636 kHandshakeResponseWithCookieLength); 637 base::MessageLoop::current()->RunUntilIdle(); 638 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 639 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 640 641 CloseWebSocketJob(); 642 } 643 644 void WebSocketJobTest::TestHandshakeWithCookie() { 645 GURL url("ws://example.com/demo"); 646 GURL cookieUrl("http://example.com/demo"); 647 CookieOptions cookie_options; 648 cookie_store_->SetCookieWithOptions( 649 cookieUrl, "CR-test=1", cookie_options); 650 cookie_options.set_include_httponly(); 651 cookie_store_->SetCookieWithOptions( 652 cookieUrl, "CR-test-httponly=1", cookie_options); 653 654 MockSocketStreamDelegate delegate; 655 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 656 SkipToConnecting(); 657 658 bool sent = websocket_->SendData(kHandshakeRequestWithCookie, 659 kHandshakeRequestWithCookieLength); 660 EXPECT_TRUE(sent); 661 base::MessageLoop::current()->RunUntilIdle(); 662 EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data()); 663 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 664 websocket_->OnSentData(socket_.get(), 665 kHandshakeRequestWithFilteredCookieLength); 666 EXPECT_EQ(kHandshakeRequestWithCookieLength, 667 delegate.amount_sent()); 668 669 websocket_->OnReceivedData(socket_.get(), 670 kHandshakeResponseWithCookie, 671 kHandshakeResponseWithCookieLength); 672 base::MessageLoop::current()->RunUntilIdle(); 673 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 674 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 675 676 EXPECT_EQ(3U, cookie_store_->entries().size()); 677 EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url); 678 EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line); 679 EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url); 680 EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line); 681 EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url); 682 EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line); 683 684 CloseWebSocketJob(); 685 } 686 687 void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() { 688 GURL url("ws://example.com/demo"); 689 GURL cookieUrl("http://example.com/demo"); 690 CookieOptions cookie_options; 691 cookie_store_->SetCookieWithOptions( 692 cookieUrl, "CR-test=1", cookie_options); 693 cookie_options.set_include_httponly(); 694 cookie_store_->SetCookieWithOptions( 695 cookieUrl, "CR-test-httponly=1", cookie_options); 696 697 MockSocketStreamDelegate delegate; 698 delegate.set_allow_all_cookies(false); 699 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 700 SkipToConnecting(); 701 702 bool sent = websocket_->SendData(kHandshakeRequestWithCookie, 703 kHandshakeRequestWithCookieLength); 704 EXPECT_TRUE(sent); 705 base::MessageLoop::current()->RunUntilIdle(); 706 EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); 707 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 708 websocket_->OnSentData(socket_.get(), kHandshakeRequestWithoutCookieLength); 709 EXPECT_EQ(kHandshakeRequestWithCookieLength, delegate.amount_sent()); 710 711 websocket_->OnReceivedData(socket_.get(), 712 kHandshakeResponseWithCookie, 713 kHandshakeResponseWithCookieLength); 714 base::MessageLoop::current()->RunUntilIdle(); 715 EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); 716 EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); 717 718 EXPECT_EQ(2U, cookie_store_->entries().size()); 719 EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url); 720 EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line); 721 EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url); 722 EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line); 723 724 CloseWebSocketJob(); 725 } 726 727 void WebSocketJobTest::TestHSTSUpgrade() { 728 GURL url("ws://upgrademe.com/"); 729 MockSocketStreamDelegate delegate; 730 scoped_refptr<SocketStreamJob> job = 731 SocketStreamJob::CreateSocketStreamJob( 732 url, &delegate, context_->transport_security_state(), 733 context_->ssl_config_service()); 734 EXPECT_TRUE(GetSocket(job.get())->is_secure()); 735 job->DetachDelegate(); 736 737 url = GURL("ws://donotupgrademe.com/"); 738 job = SocketStreamJob::CreateSocketStreamJob( 739 url, &delegate, context_->transport_security_state(), 740 context_->ssl_config_service()); 741 EXPECT_FALSE(GetSocket(job.get())->is_secure()); 742 job->DetachDelegate(); 743 } 744 745 void WebSocketJobTest::TestInvalidSendData() { 746 GURL url("ws://example.com/demo"); 747 MockSocketStreamDelegate delegate; 748 InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); 749 SkipToConnecting(); 750 751 DoSendRequest(); 752 // We assume request is sent in one data chunk (from WebKit) 753 // We don't support streaming request. 754 base::MessageLoop::current()->RunUntilIdle(); 755 EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); 756 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 757 websocket_->OnSentData(socket_.get(), 758 kHandshakeRequestWithoutCookieLength); 759 EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); 760 761 // We could not send any data until connection is established. 762 bool sent = websocket_->SendData(kHandshakeRequestWithoutCookie, 763 kHandshakeRequestWithoutCookieLength); 764 EXPECT_FALSE(sent); 765 EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); 766 CloseWebSocketJob(); 767 } 768 769 // Following tests verify cooperation between WebSocketJob and SocketStream. 770 // Other former tests use MockSocketStream as SocketStream, so we could not 771 // check SocketStream behavior. 772 // OrderedSocketData provide socket level verifiation by checking out-going 773 // packets in comparison with the MockWrite array and emulating in-coming 774 // packets with MockRead array. 775 776 void WebSocketJobTest::TestConnectByWebSocket( 777 ThrottlingOption throttling) { 778 // This is a test for verifying cooperation between WebSocketJob and 779 // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic 780 // situation. If |throttling| was |THROTTLING_ON|, throttling limits the 781 // latter connection. 782 MockWrite writes[] = { 783 MockWrite(ASYNC, 784 kHandshakeRequestWithoutCookie, 785 kHandshakeRequestWithoutCookieLength, 786 1), 787 MockWrite(ASYNC, 788 kDataHello, 789 kDataHelloLength, 790 3) 791 }; 792 MockRead reads[] = { 793 MockRead(ASYNC, 794 kHandshakeResponseWithoutCookie, 795 kHandshakeResponseWithoutCookieLength, 796 2), 797 MockRead(ASYNC, 798 kDataWorld, 799 kDataWorldLength, 800 4), 801 MockRead(SYNCHRONOUS, 0, 5) // EOF 802 }; 803 data_.reset(new OrderedSocketData( 804 reads, arraysize(reads), writes, arraysize(writes))); 805 806 GURL url("ws://example.com/demo"); 807 MockSocketStreamDelegate delegate; 808 WebSocketJobTest* test = this; 809 if (throttling == THROTTLING_ON) 810 delegate.SetOnStartOpenConnection( 811 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 812 delegate.SetOnConnected( 813 base::Bind(&WebSocketJobTest::DoSendRequest, 814 base::Unretained(test))); 815 delegate.SetOnReceivedData( 816 base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test))); 817 delegate.SetOnClose( 818 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 819 InitWebSocketJob(url, &delegate, STREAM_SOCKET); 820 821 scoped_refptr<WebSocketJob> block_websocket; 822 if (throttling == THROTTLING_ON) { 823 // Create former WebSocket object which obstructs the latter one. 824 block_websocket = new WebSocketJob(NULL); 825 block_websocket->addresses_ = AddressList(websocket_->address_list()); 826 ASSERT_TRUE( 827 WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get())); 828 } 829 830 websocket_->Connect(); 831 832 if (throttling == THROTTLING_ON) { 833 EXPECT_EQ(OK, WaitForResult()); 834 EXPECT_TRUE(websocket_->IsWaiting()); 835 836 // Remove the former WebSocket object from throttling queue to unblock the 837 // latter. 838 block_websocket->state_ = WebSocketJob::CLOSED; 839 WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get()); 840 block_websocket = NULL; 841 } 842 843 EXPECT_EQ(OK, WaitForResult()); 844 EXPECT_TRUE(data_->at_read_eof()); 845 EXPECT_TRUE(data_->at_write_eof()); 846 EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState()); 847 } 848 849 void WebSocketJobTest::TestConnectBySpdy( 850 SpdyOption spdy, ThrottlingOption throttling) { 851 // This is a test for verifying cooperation between WebSocketJob and 852 // SocketStream in the situation we have SPDY session to the server. If 853 // |throttling| was |THROTTLING_ON|, throttling limits the latter connection. 854 // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected 855 // results depend on its configuration. 856 MockWrite writes_websocket[] = { 857 MockWrite(ASYNC, 858 kHandshakeRequestWithoutCookie, 859 kHandshakeRequestWithoutCookieLength, 860 1), 861 MockWrite(ASYNC, 862 kDataHello, 863 kDataHelloLength, 864 3) 865 }; 866 MockRead reads_websocket[] = { 867 MockRead(ASYNC, 868 kHandshakeResponseWithoutCookie, 869 kHandshakeResponseWithoutCookieLength, 870 2), 871 MockRead(ASYNC, 872 kDataWorld, 873 kDataWorldLength, 874 4), 875 MockRead(SYNCHRONOUS, 0, 5) // EOF 876 }; 877 878 scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock()); 879 spdy_util_.SetHeader("path", "/demo", request_headers.get()); 880 spdy_util_.SetHeader("version", "WebSocket/13", request_headers.get()); 881 spdy_util_.SetHeader("scheme", "ws", request_headers.get()); 882 spdy_util_.SetHeader("host", "example.com", request_headers.get()); 883 spdy_util_.SetHeader("origin", "http://example.com", request_headers.get()); 884 spdy_util_.SetHeader("sec-websocket-protocol", "sample", 885 request_headers.get()); 886 887 scoped_ptr<SpdyHeaderBlock> response_headers(new SpdyHeaderBlock()); 888 spdy_util_.SetHeader("status", "101 Switching Protocols", 889 response_headers.get()); 890 spdy_util_.SetHeader("sec-websocket-protocol", "sample", 891 response_headers.get()); 892 893 const SpdyStreamId kStreamId = 1; 894 scoped_ptr<SpdyFrame> request_frame( 895 spdy_util_.ConstructSpdyWebSocketHandshakeRequestFrame( 896 request_headers.Pass(), 897 kStreamId, 898 MEDIUM)); 899 scoped_ptr<SpdyFrame> response_frame( 900 spdy_util_.ConstructSpdyWebSocketHandshakeResponseFrame( 901 response_headers.Pass(), 902 kStreamId, 903 MEDIUM)); 904 scoped_ptr<SpdyFrame> data_hello_frame( 905 spdy_util_.ConstructSpdyWebSocketDataFrame( 906 kDataHello, 907 kDataHelloLength, 908 kStreamId, 909 false)); 910 scoped_ptr<SpdyFrame> data_world_frame( 911 spdy_util_.ConstructSpdyWebSocketDataFrame( 912 kDataWorld, 913 kDataWorldLength, 914 kStreamId, 915 false)); 916 MockWrite writes_spdy[] = { 917 CreateMockWrite(*request_frame.get(), 1), 918 CreateMockWrite(*data_hello_frame.get(), 3), 919 }; 920 MockRead reads_spdy[] = { 921 CreateMockRead(*response_frame.get(), 2), 922 CreateMockRead(*data_world_frame.get(), 4), 923 MockRead(SYNCHRONOUS, 0, 5) // EOF 924 }; 925 926 if (spdy == SPDY_ON) 927 data_.reset(new OrderedSocketData( 928 reads_spdy, arraysize(reads_spdy), 929 writes_spdy, arraysize(writes_spdy))); 930 else 931 data_.reset(new OrderedSocketData( 932 reads_websocket, arraysize(reads_websocket), 933 writes_websocket, arraysize(writes_websocket))); 934 935 GURL url("ws://example.com/demo"); 936 MockSocketStreamDelegate delegate; 937 WebSocketJobTest* test = this; 938 if (throttling == THROTTLING_ON) 939 delegate.SetOnStartOpenConnection( 940 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 941 delegate.SetOnConnected( 942 base::Bind(&WebSocketJobTest::DoSendRequest, 943 base::Unretained(test))); 944 delegate.SetOnReceivedData( 945 base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test))); 946 delegate.SetOnClose( 947 base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); 948 InitWebSocketJob(url, &delegate, STREAM_SPDY_WEBSOCKET); 949 950 scoped_refptr<WebSocketJob> block_websocket; 951 if (throttling == THROTTLING_ON) { 952 // Create former WebSocket object which obstructs the latter one. 953 block_websocket = new WebSocketJob(NULL); 954 block_websocket->addresses_ = AddressList(websocket_->address_list()); 955 ASSERT_TRUE( 956 WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get())); 957 } 958 959 websocket_->Connect(); 960 961 if (throttling == THROTTLING_ON) { 962 EXPECT_EQ(OK, WaitForResult()); 963 EXPECT_TRUE(websocket_->IsWaiting()); 964 965 // Remove the former WebSocket object from throttling queue to unblock the 966 // latter. 967 block_websocket->state_ = WebSocketJob::CLOSED; 968 WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get()); 969 block_websocket = NULL; 970 } 971 972 EXPECT_EQ(OK, WaitForResult()); 973 EXPECT_TRUE(data_->at_read_eof()); 974 EXPECT_TRUE(data_->at_write_eof()); 975 EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState()); 976 } 977 978 void WebSocketJobTest::TestThrottlingLimit() { 979 std::vector<scoped_refptr<WebSocketJob> > jobs; 980 const int kMaxWebSocketJobsThrottled = 1024; 981 IPAddressNumber ip; 982 ParseIPLiteralToNumber("127.0.0.1", &ip); 983 for (int i = 0; i < kMaxWebSocketJobsThrottled + 1; ++i) { 984 scoped_refptr<WebSocketJob> job = new WebSocketJob(NULL); 985 job->addresses_ = AddressList(AddressList::CreateFromIPAddress(ip, 80)); 986 if (i >= kMaxWebSocketJobsThrottled) 987 EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job)); 988 else 989 EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job)); 990 jobs.push_back(job); 991 } 992 993 // Close the jobs in reverse order. Otherwise, We need to make them prepared 994 // for Wakeup call. 995 for (std::vector<scoped_refptr<WebSocketJob> >::reverse_iterator iter = 996 jobs.rbegin(); 997 iter != jobs.rend(); 998 ++iter) { 999 WebSocketJob* job = (*iter).get(); 1000 job->state_ = WebSocketJob::CLOSED; 1001 WebSocketThrottle::GetInstance()->RemoveFromQueue(job); 1002 } 1003 } 1004 1005 // Execute tests in both spdy-disabled mode and spdy-enabled mode. 1006 TEST_P(WebSocketJobTest, SimpleHandshake) { 1007 WebSocketJob::set_websocket_over_spdy_enabled(false); 1008 TestSimpleHandshake(); 1009 } 1010 1011 TEST_P(WebSocketJobTest, SlowHandshake) { 1012 WebSocketJob::set_websocket_over_spdy_enabled(false); 1013 TestSlowHandshake(); 1014 } 1015 1016 TEST_P(WebSocketJobTest, HandshakeWithCookie) { 1017 WebSocketJob::set_websocket_over_spdy_enabled(false); 1018 TestHandshakeWithCookie(); 1019 } 1020 1021 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) { 1022 WebSocketJob::set_websocket_over_spdy_enabled(false); 1023 TestHandshakeWithCookieButNotAllowed(); 1024 } 1025 1026 TEST_P(WebSocketJobTest, HSTSUpgrade) { 1027 WebSocketJob::set_websocket_over_spdy_enabled(false); 1028 TestHSTSUpgrade(); 1029 } 1030 1031 TEST_P(WebSocketJobTest, InvalidSendData) { 1032 WebSocketJob::set_websocket_over_spdy_enabled(false); 1033 TestInvalidSendData(); 1034 } 1035 1036 TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) { 1037 WebSocketJob::set_websocket_over_spdy_enabled(true); 1038 TestSimpleHandshake(); 1039 } 1040 1041 TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) { 1042 WebSocketJob::set_websocket_over_spdy_enabled(true); 1043 TestSlowHandshake(); 1044 } 1045 1046 TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) { 1047 WebSocketJob::set_websocket_over_spdy_enabled(true); 1048 TestHandshakeWithCookie(); 1049 } 1050 1051 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) { 1052 WebSocketJob::set_websocket_over_spdy_enabled(true); 1053 TestHandshakeWithCookieButNotAllowed(); 1054 } 1055 1056 TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) { 1057 WebSocketJob::set_websocket_over_spdy_enabled(true); 1058 TestHSTSUpgrade(); 1059 } 1060 1061 TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) { 1062 WebSocketJob::set_websocket_over_spdy_enabled(true); 1063 TestInvalidSendData(); 1064 } 1065 1066 TEST_P(WebSocketJobTest, ConnectByWebSocket) { 1067 WebSocketJob::set_websocket_over_spdy_enabled(false); 1068 TestConnectByWebSocket(THROTTLING_OFF); 1069 } 1070 1071 TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) { 1072 WebSocketJob::set_websocket_over_spdy_enabled(true); 1073 TestConnectByWebSocket(THROTTLING_OFF); 1074 } 1075 1076 TEST_P(WebSocketJobTest, ConnectBySpdy) { 1077 WebSocketJob::set_websocket_over_spdy_enabled(false); 1078 TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF); 1079 } 1080 1081 TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) { 1082 WebSocketJob::set_websocket_over_spdy_enabled(true); 1083 TestConnectBySpdy(SPDY_ON, THROTTLING_OFF); 1084 } 1085 1086 TEST_P(WebSocketJobTest, ThrottlingWebSocket) { 1087 WebSocketJob::set_websocket_over_spdy_enabled(false); 1088 TestConnectByWebSocket(THROTTLING_ON); 1089 } 1090 1091 TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) { 1092 TestThrottlingLimit(); 1093 } 1094 1095 TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) { 1096 WebSocketJob::set_websocket_over_spdy_enabled(true); 1097 TestConnectByWebSocket(THROTTLING_ON); 1098 } 1099 1100 TEST_P(WebSocketJobTest, ThrottlingSpdy) { 1101 WebSocketJob::set_websocket_over_spdy_enabled(false); 1102 TestConnectBySpdy(SPDY_OFF, THROTTLING_ON); 1103 } 1104 1105 TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) { 1106 WebSocketJob::set_websocket_over_spdy_enabled(true); 1107 TestConnectBySpdy(SPDY_ON, THROTTLING_ON); 1108 } 1109 1110 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. 1111 // TODO(toyoshim,yutak): Add tests to verify closing handshake. 1112 } // namespace net 1113