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