| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |