1 // Copyright (c) 2011 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.
5 #include "net/websockets/websocket_job.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/string_split.h"
12 #include "base/string_util.h"
13 #include "googleurl/src/gurl.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/cookie_store.h"
16 #include "net/base/mock_host_resolver.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/ssl_config_service.h"
19 #include "net/base/sys_addrinfo.h"
20 #include "net/base/test_completion_callback.h"
21 #include "net/base/transport_security_state.h"
22 #include "net/http/http_transaction_factory.h"
23 #include "net/proxy/proxy_service.h"
24 #include "net/socket/socket_test_util.h"
25 #include "net/socket_stream/socket_stream.h"
26 #include "net/spdy/spdy_session.h"
27 #include "net/spdy/spdy_test_util.h"
28 #include "net/spdy/spdy_websocket_test_util.h"
29 #include "net/url_request/url_request_context.h"
30 #include "net/websockets/websocket_throttle.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/platform_test.h"
37 class MockSocketStream
: public net::SocketStream
{
39 MockSocketStream(const GURL
& url
, net::SocketStream::Delegate
* delegate
)
40 : SocketStream(url
, delegate
) {}
41 virtual ~MockSocketStream() {}
43 virtual void Connect() {}
44 virtual bool SendData(const char* data
, int len
) {
45 sent_data_
+= std::string(data
, len
);
49 virtual void Close() {}
50 virtual void RestartWithAuth(
51 const string16
& username
, const string16
& password
) {}
52 virtual void DetachDelegate() {
56 const std::string
& sent_data() const {
61 std::string sent_data_
;
64 class MockSocketStreamDelegate
: public net::SocketStream::Delegate
{
66 MockSocketStreamDelegate()
67 : amount_sent_(0), allow_all_cookies_(true) {}
68 void set_allow_all_cookies(bool allow_all_cookies
) {
69 allow_all_cookies_
= allow_all_cookies
;
71 virtual ~MockSocketStreamDelegate() {}
73 void SetOnConnected(Callback0::Type
* callback
) {
74 on_connected_
.reset(callback
);
76 void SetOnSentData(Callback0::Type
* callback
) {
77 on_sent_data_
.reset(callback
);
79 void SetOnReceivedData(Callback0::Type
* callback
) {
80 on_received_data_
.reset(callback
);
82 void SetOnClose(Callback0::Type
* callback
) {
83 on_close_
.reset(callback
);
86 virtual void OnConnected(net::SocketStream
* socket
,
87 int max_pending_send_allowed
) {
88 if (on_connected_
.get())
91 virtual void OnSentData(net::SocketStream
* socket
, int amount_sent
) {
92 amount_sent_
+= amount_sent
;
93 if (on_sent_data_
.get())
96 virtual void OnReceivedData(net::SocketStream
* socket
,
97 const char* data
, int len
) {
98 received_data_
+= std::string(data
, len
);
99 if (on_received_data_
.get())
100 on_received_data_
->Run();
102 virtual void OnClose(net::SocketStream
* socket
) {
106 virtual bool CanGetCookies(net::SocketStream
* socket
, const GURL
& url
) {
107 return allow_all_cookies_
;
109 virtual bool CanSetCookie(net::SocketStream
* request
,
111 const std::string
& cookie_line
,
112 net::CookieOptions
* options
) {
113 return allow_all_cookies_
;
116 size_t amount_sent() const { return amount_sent_
; }
117 const std::string
& received_data() const { return received_data_
; }
121 bool allow_all_cookies_
;
122 std::string received_data_
;
123 scoped_ptr
<Callback0::Type
> on_connected_
;
124 scoped_ptr
<Callback0::Type
> on_sent_data_
;
125 scoped_ptr
<Callback0::Type
> on_received_data_
;
126 scoped_ptr
<Callback0::Type
> on_close_
;
129 class MockCookieStore
: public net::CookieStore
{
133 std::string cookie_line
;
134 net::CookieOptions options
;
138 virtual bool SetCookieWithOptions(const GURL
& url
,
139 const std::string
& cookie_line
,
140 const net::CookieOptions
& options
) {
143 entry
.cookie_line
= cookie_line
;
144 entry
.options
= options
;
145 entries_
.push_back(entry
);
148 virtual std::string
GetCookiesWithOptions(
150 const net::CookieOptions
& options
) {
152 for (size_t i
= 0; i
< entries_
.size(); i
++) {
153 Entry
&entry
= entries_
[i
];
154 if (url
== entry
.url
) {
155 if (!result
.empty()) {
158 result
+= entry
.cookie_line
;
163 virtual void GetCookiesWithInfo(const GURL
& url
,
164 const net::CookieOptions
& options
,
165 std::string
* cookie_line
,
166 std::vector
<CookieInfo
>* cookie_infos
) {
169 virtual void DeleteCookie(const GURL
& url
,
170 const std::string
& cookie_name
) {}
171 virtual net::CookieMonster
* GetCookieMonster() { return NULL
; }
173 const std::vector
<Entry
>& entries() const { return entries_
; }
176 friend class base::RefCountedThreadSafe
<MockCookieStore
>;
177 virtual ~MockCookieStore() {}
179 std::vector
<Entry
> entries_
;
182 class MockSSLConfigService
: public net::SSLConfigService
{
184 virtual void GetSSLConfig(net::SSLConfig
* config
) {};
187 class MockURLRequestContext
: public net::URLRequestContext
{
189 explicit MockURLRequestContext(net::CookieStore
* cookie_store
) {
190 set_cookie_store(cookie_store
);
191 transport_security_state_
= new net::TransportSecurityState(std::string());
192 set_transport_security_state(transport_security_state_
.get());
193 net::TransportSecurityState::DomainState state
;
194 state
.expiry
= base::Time::Now() + base::TimeDelta::FromSeconds(1000);
195 transport_security_state_
->EnableHost("upgrademe.com", state
);
199 friend class base::RefCountedThreadSafe
<MockURLRequestContext
>;
200 virtual ~MockURLRequestContext() {}
202 scoped_refptr
<net::TransportSecurityState
> transport_security_state_
;
205 class MockHttpTransactionFactory
: public net::HttpTransactionFactory
{
207 MockHttpTransactionFactory(scoped_refptr
<net::OrderedSocketData
>& data
) {
209 net::MockConnect
connect_data(false, net::OK
);
210 data_
->set_connect_data(connect_data
);
211 session_deps_
.reset(new net::SpdySessionDependencies
);
212 session_deps_
->socket_factory
->AddSocketDataProvider(data_
.get());
214 net::SpdySessionDependencies::SpdyCreateSession(session_deps_
.get());
215 host_port_pair_
.set_host("example.com");
216 host_port_pair_
.set_port(80);
217 host_port_proxy_pair_
.first
= host_port_pair_
;
218 host_port_proxy_pair_
.second
= net::ProxyServer::Direct();
219 net::SpdySessionPool
* spdy_session_pool
=
220 http_session_
->spdy_session_pool();
221 DCHECK(spdy_session_pool
);
222 EXPECT_FALSE(spdy_session_pool
->HasSession(host_port_proxy_pair_
));
224 spdy_session_pool
->Get(host_port_proxy_pair_
, net::BoundNetLog());
225 EXPECT_TRUE(spdy_session_pool
->HasSession(host_port_proxy_pair_
));
227 transport_params_
= new net::TransportSocketParams(host_port_pair_
,
232 net::ClientSocketHandle
* connection
= new net::ClientSocketHandle
;
233 EXPECT_EQ(net::OK
, connection
->Init(host_port_pair_
.ToString(),
237 http_session_
->transport_socket_pool(),
238 net::BoundNetLog()));
240 session_
->InitializeWithSocket(connection
, false, net::OK
));
242 virtual int CreateTransaction(scoped_ptr
<net::HttpTransaction
>* trans
) {
244 return net::ERR_UNEXPECTED
;
246 virtual net::HttpCache
* GetCache() {
250 virtual net::HttpNetworkSession
* GetSession() {
251 return http_session_
.get();
254 scoped_refptr
<net::OrderedSocketData
> data_
;
255 scoped_ptr
<net::SpdySessionDependencies
> session_deps_
;
256 scoped_refptr
<net::HttpNetworkSession
> http_session_
;
257 scoped_refptr
<net::TransportSocketParams
> transport_params_
;
258 scoped_refptr
<net::SpdySession
> session_
;
259 net::HostPortPair host_port_pair_
;
260 net::HostPortProxyPair host_port_proxy_pair_
;
267 class WebSocketJobTest
: public PlatformTest
{
269 virtual void SetUp() {
270 spdy::SpdyFramer::set_enable_compression_default(false);
271 stream_type_
= STREAM_INVALID
;
272 cookie_store_
= new MockCookieStore
;
273 context_
= new MockURLRequestContext(cookie_store_
.get());
275 virtual void TearDown() {
276 cookie_store_
= NULL
;
281 void DoSendRequest() {
282 EXPECT_TRUE(websocket_
->SendData(kHandshakeRequestWithoutCookie
,
283 kHandshakeRequestWithoutCookieLength
));
286 if (received_data().size() == kHandshakeResponseWithoutCookieLength
)
287 websocket_
->SendData(kDataHello
, kDataHelloLength
);
290 sync_callback_
.Run(OK
);
292 int WaitForResult() {
293 return sync_callback_
.WaitForResult();
300 STREAM_SPDY_WEBSOCKET
,
302 void InitWebSocketJob(const GURL
& url
,
303 MockSocketStreamDelegate
* delegate
,
304 StreamType stream_type
) {
305 DCHECK_NE(STREAM_INVALID
, stream_type
);
306 stream_type_
= stream_type
;
307 websocket_
= new WebSocketJob(delegate
);
309 if (stream_type
== STREAM_MOCK_SOCKET
)
310 socket_
= new MockSocketStream(url
, websocket_
.get());
312 if (stream_type
== STREAM_SOCKET
|| stream_type
== STREAM_SPDY_WEBSOCKET
) {
313 if (stream_type
== STREAM_SPDY_WEBSOCKET
) {
314 http_factory_
.reset(new MockHttpTransactionFactory(data_
));
315 context_
->set_http_transaction_factory(http_factory_
.get());
318 ssl_config_service_
= new MockSSLConfigService();
319 context_
->set_ssl_config_service(ssl_config_service_
);
320 proxy_service_
.reset(net::ProxyService::CreateDirect());
321 context_
->set_proxy_service(proxy_service_
.get());
322 host_resolver_
.reset(new net::MockHostResolver
);
323 context_
->set_host_resolver(host_resolver_
.get());
325 socket_
= new SocketStream(url
, websocket_
.get());
326 socket_factory_
.reset(new MockClientSocketFactory
);
328 socket_factory_
->AddSocketDataProvider(data_
.get());
329 socket_
->SetClientSocketFactory(socket_factory_
.get());
332 websocket_
->InitSocketStream(socket_
.get());
333 websocket_
->set_context(context_
.get());
334 struct addrinfo addr
;
335 memset(&addr
, 0, sizeof(struct addrinfo
));
336 addr
.ai_family
= AF_INET
;
337 addr
.ai_addrlen
= sizeof(struct sockaddr_in
);
338 struct sockaddr_in sa_in
;
339 memset(&sa_in
, 0, sizeof(struct sockaddr_in
));
340 memcpy(&sa_in
.sin_addr
, "\x7f\0\0\1", 4);
341 addr
.ai_addr
= reinterpret_cast<sockaddr
*>(&sa_in
);
343 websocket_
->addresses_
= AddressList::CreateByCopying(&addr
);
345 void SkipToConnecting() {
346 websocket_
->state_
= WebSocketJob::CONNECTING
;
347 WebSocketThrottle::GetInstance()->PutInQueue(websocket_
);
349 WebSocketJob::State
GetWebSocketJobState() {
350 return websocket_
->state_
;
352 void CloseWebSocketJob() {
353 if (websocket_
->socket_
) {
354 websocket_
->socket_
->DetachDelegate();
355 WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_
);
357 websocket_
->state_
= WebSocketJob::CLOSED
;
358 websocket_
->delegate_
= NULL
;
359 websocket_
->socket_
= NULL
;
361 SocketStream
* GetSocket(SocketStreamJob
* job
) {
362 return job
->socket_
.get();
364 const std::string
& sent_data() const {
365 DCHECK_EQ(STREAM_MOCK_SOCKET
, stream_type_
);
366 MockSocketStream
* socket
=
367 static_cast<MockSocketStream
*>(socket_
.get());
369 return socket
->sent_data();
371 const std::string
& received_data() const {
372 DCHECK_NE(STREAM_INVALID
, stream_type_
);
373 MockSocketStreamDelegate
* delegate
=
374 static_cast<MockSocketStreamDelegate
*>(websocket_
->delegate_
);
376 return delegate
->received_data();
379 void TestSimpleHandshake();
380 void TestSlowHandshake();
381 void TestHandshakeWithCookie();
382 void TestHandshakeWithCookieButNotAllowed();
383 void TestHSTSUpgrade();
384 void TestInvalidSendData();
385 void TestConnectByWebSocket();
386 void TestConnectBySpdy(bool use_spdy
);
388 StreamType stream_type_
;
389 scoped_refptr
<MockCookieStore
> cookie_store_
;
390 scoped_refptr
<MockURLRequestContext
> context_
;
391 scoped_refptr
<WebSocketJob
> websocket_
;
392 scoped_refptr
<SocketStream
> socket_
;
393 scoped_ptr
<MockClientSocketFactory
> socket_factory_
;
394 scoped_refptr
<OrderedSocketData
> data_
;
395 TestCompletionCallback sync_callback_
;
396 scoped_refptr
<MockSSLConfigService
> ssl_config_service_
;
397 scoped_ptr
<net::ProxyService
> proxy_service_
;
398 scoped_ptr
<net::MockHostResolver
> host_resolver_
;
399 scoped_ptr
<MockHttpTransactionFactory
> http_factory_
;
401 static const char kHandshakeRequestWithoutCookie
[];
402 static const char kHandshakeRequestWithCookie
[];
403 static const char kHandshakeRequestWithFilteredCookie
[];
404 static const char kHandshakeResponseWithoutCookie
[];
405 static const char kHandshakeResponseWithCookie
[];
406 static const char kDataHello
[];
407 static const char kDataWorld
[];
408 static const char* const kHandshakeRequestForSpdy
[];
409 static const char* const kHandshakeResponseForSpdy
[];
410 static const size_t kHandshakeRequestWithoutCookieLength
;
411 static const size_t kHandshakeRequestWithCookieLength
;
412 static const size_t kHandshakeRequestWithFilteredCookieLength
;
413 static const size_t kHandshakeResponseWithoutCookieLength
;
414 static const size_t kHandshakeResponseWithCookieLength
;
415 static const size_t kDataHelloLength
;
416 static const size_t kDataWorldLength
;
419 const char WebSocketJobTest::kHandshakeRequestWithoutCookie
[] =
420 "GET /demo HTTP/1.1\r\n"
421 "Host: example.com\r\n"
422 "Connection: Upgrade\r\n"
423 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
424 "Sec-WebSocket-Protocol: sample\r\n"
425 "Upgrade: WebSocket\r\n"
426 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
427 "Origin: http://example.com\r\n"
431 const char WebSocketJobTest::kHandshakeRequestWithCookie
[] =
432 "GET /demo HTTP/1.1\r\n"
433 "Host: example.com\r\n"
434 "Connection: Upgrade\r\n"
435 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
436 "Sec-WebSocket-Protocol: sample\r\n"
437 "Upgrade: WebSocket\r\n"
438 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
439 "Origin: http://example.com\r\n"
440 "Cookie: WK-test=1\r\n"
444 const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie
[] =
445 "GET /demo HTTP/1.1\r\n"
446 "Host: example.com\r\n"
447 "Connection: Upgrade\r\n"
448 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
449 "Sec-WebSocket-Protocol: sample\r\n"
450 "Upgrade: WebSocket\r\n"
451 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
452 "Origin: http://example.com\r\n"
453 "Cookie: CR-test=1; CR-test-httponly=1\r\n"
457 const char WebSocketJobTest::kHandshakeResponseWithoutCookie
[] =
458 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
459 "Upgrade: WebSocket\r\n"
460 "Connection: Upgrade\r\n"
461 "Sec-WebSocket-Origin: http://example.com\r\n"
462 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
463 "Sec-WebSocket-Protocol: sample\r\n"
467 const char WebSocketJobTest::kHandshakeResponseWithCookie
[] =
468 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
469 "Upgrade: WebSocket\r\n"
470 "Connection: Upgrade\r\n"
471 "Sec-WebSocket-Origin: http://example.com\r\n"
472 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
473 "Sec-WebSocket-Protocol: sample\r\n"
474 "Set-Cookie: CR-set-test=1\r\n"
478 const char WebSocketJobTest::kDataHello
[] = "Hello, ";
480 const char WebSocketJobTest::kDataWorld
[] = "World!\n";
482 // TODO(toyoshim): I should clarify which WebSocket headers for handshake must
483 // be exported to SPDY SYN_STREAM and SYN_REPLY.
484 // Because it depends on HyBi versions, just define it as follow for now.
485 const char* const WebSocketJobTest::kHandshakeRequestForSpdy
[] = {
486 "host", "example.com",
487 "origin", "http://example.com",
488 "sec-websocket-protocol", "sample",
489 "url", "ws://example.com/demo"
492 const char* const WebSocketJobTest::kHandshakeResponseForSpdy
[] = {
493 "sec-websocket-origin", "http://example.com",
494 "sec-websocket-location", "ws://example.com/demo",
495 "sec-websocket-protocol", "sample",
498 const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength
=
499 arraysize(kHandshakeRequestWithoutCookie
) - 1;
500 const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength
=
501 arraysize(kHandshakeRequestWithCookie
) - 1;
502 const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength
=
503 arraysize(kHandshakeRequestWithFilteredCookie
) - 1;
504 const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength
=
505 arraysize(kHandshakeResponseWithoutCookie
) - 1;
506 const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength
=
507 arraysize(kHandshakeResponseWithCookie
) - 1;
508 const size_t WebSocketJobTest::kDataHelloLength
=
509 arraysize(kDataHello
) - 1;
510 const size_t WebSocketJobTest::kDataWorldLength
=
511 arraysize(kDataWorld
) - 1;
513 void WebSocketJobTest::TestSimpleHandshake() {
514 GURL
url("ws://example.com/demo");
515 MockSocketStreamDelegate delegate
;
516 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
520 MessageLoop::current()->RunAllPending();
521 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
522 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
523 websocket_
->OnSentData(socket_
.get(),
524 kHandshakeRequestWithoutCookieLength
);
525 EXPECT_EQ(kHandshakeRequestWithoutCookieLength
, delegate
.amount_sent());
527 websocket_
->OnReceivedData(socket_
.get(),
528 kHandshakeResponseWithoutCookie
,
529 kHandshakeResponseWithoutCookieLength
);
530 MessageLoop::current()->RunAllPending();
531 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
532 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
536 void WebSocketJobTest::TestSlowHandshake() {
537 GURL
url("ws://example.com/demo");
538 MockSocketStreamDelegate delegate
;
539 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
543 // We assume request is sent in one data chunk (from WebKit)
544 // We don't support streaming request.
545 MessageLoop::current()->RunAllPending();
546 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
547 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
548 websocket_
->OnSentData(socket_
.get(),
549 kHandshakeRequestWithoutCookieLength
);
550 EXPECT_EQ(kHandshakeRequestWithoutCookieLength
, delegate
.amount_sent());
552 std::vector
<std::string
> lines
;
553 base::SplitString(kHandshakeResponseWithoutCookie
, '\n', &lines
);
554 for (size_t i
= 0; i
< lines
.size() - 2; i
++) {
555 std::string line
= lines
[i
] + "\r\n";
556 SCOPED_TRACE("Line: " + line
);
557 websocket_
->OnReceivedData(socket_
,
560 MessageLoop::current()->RunAllPending();
561 EXPECT_TRUE(delegate
.received_data().empty());
562 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
564 websocket_
->OnReceivedData(socket_
.get(), "\r\n", 2);
565 MessageLoop::current()->RunAllPending();
566 EXPECT_TRUE(delegate
.received_data().empty());
567 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
568 websocket_
->OnReceivedData(socket_
.get(), "8jKS'y:G*Co,Wxa-", 16);
569 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
570 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
574 void WebSocketJobTest::TestHandshakeWithCookie() {
575 GURL
url("ws://example.com/demo");
576 GURL
cookieUrl("http://example.com/demo");
577 CookieOptions cookie_options
;
578 cookie_store_
->SetCookieWithOptions(
579 cookieUrl
, "CR-test=1", cookie_options
);
580 cookie_options
.set_include_httponly();
581 cookie_store_
->SetCookieWithOptions(
582 cookieUrl
, "CR-test-httponly=1", cookie_options
);
584 MockSocketStreamDelegate delegate
;
585 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
588 bool sent
= websocket_
->SendData(kHandshakeRequestWithCookie
,
589 kHandshakeRequestWithCookieLength
);
591 MessageLoop::current()->RunAllPending();
592 EXPECT_EQ(kHandshakeRequestWithFilteredCookie
, sent_data());
593 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
594 websocket_
->OnSentData(socket_
,
595 kHandshakeRequestWithFilteredCookieLength
);
596 EXPECT_EQ(kHandshakeRequestWithCookieLength
,
597 delegate
.amount_sent());
599 websocket_
->OnReceivedData(socket_
.get(),
600 kHandshakeResponseWithCookie
,
601 kHandshakeResponseWithCookieLength
);
602 MessageLoop::current()->RunAllPending();
603 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
604 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
606 EXPECT_EQ(3U, cookie_store_
->entries().size());
607 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[0].url
);
608 EXPECT_EQ("CR-test=1", cookie_store_
->entries()[0].cookie_line
);
609 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[1].url
);
610 EXPECT_EQ("CR-test-httponly=1", cookie_store_
->entries()[1].cookie_line
);
611 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[2].url
);
612 EXPECT_EQ("CR-set-test=1", cookie_store_
->entries()[2].cookie_line
);
617 void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() {
618 GURL
url("ws://example.com/demo");
619 GURL
cookieUrl("http://example.com/demo");
620 CookieOptions cookie_options
;
621 cookie_store_
->SetCookieWithOptions(
622 cookieUrl
, "CR-test=1", cookie_options
);
623 cookie_options
.set_include_httponly();
624 cookie_store_
->SetCookieWithOptions(
625 cookieUrl
, "CR-test-httponly=1", cookie_options
);
627 MockSocketStreamDelegate delegate
;
628 delegate
.set_allow_all_cookies(false);
629 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
632 bool sent
= websocket_
->SendData(kHandshakeRequestWithCookie
,
633 kHandshakeRequestWithCookieLength
);
635 MessageLoop::current()->RunAllPending();
636 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
637 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
638 websocket_
->OnSentData(socket_
, kHandshakeRequestWithoutCookieLength
);
639 EXPECT_EQ(kHandshakeRequestWithCookieLength
,
640 delegate
.amount_sent());
642 websocket_
->OnReceivedData(socket_
.get(),
643 kHandshakeResponseWithCookie
,
644 kHandshakeResponseWithCookieLength
);
645 MessageLoop::current()->RunAllPending();
646 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
647 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
649 EXPECT_EQ(2U, cookie_store_
->entries().size());
650 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[0].url
);
651 EXPECT_EQ("CR-test=1", cookie_store_
->entries()[0].cookie_line
);
652 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[1].url
);
653 EXPECT_EQ("CR-test-httponly=1", cookie_store_
->entries()[1].cookie_line
);
658 void WebSocketJobTest::TestHSTSUpgrade() {
659 GURL
url("ws://upgrademe.com/");
660 MockSocketStreamDelegate delegate
;
661 scoped_refptr
<SocketStreamJob
> job
=
662 SocketStreamJob::CreateSocketStreamJob(
663 url
, &delegate
, context_
->transport_security_state(),
664 context_
->ssl_config_service());
665 EXPECT_TRUE(GetSocket(job
.get())->is_secure());
666 job
->DetachDelegate();
668 url
= GURL("ws://donotupgrademe.com/");
669 job
= SocketStreamJob::CreateSocketStreamJob(
670 url
, &delegate
, context_
->transport_security_state(),
671 context_
->ssl_config_service());
672 EXPECT_FALSE(GetSocket(job
.get())->is_secure());
673 job
->DetachDelegate();
676 void WebSocketJobTest::TestInvalidSendData() {
677 GURL
url("ws://example.com/demo");
678 MockSocketStreamDelegate delegate
;
679 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
683 // We assume request is sent in one data chunk (from WebKit)
684 // We don't support streaming request.
685 MessageLoop::current()->RunAllPending();
686 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
687 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
688 websocket_
->OnSentData(socket_
.get(),
689 kHandshakeRequestWithoutCookieLength
);
690 EXPECT_EQ(kHandshakeRequestWithoutCookieLength
, delegate
.amount_sent());
692 // We could not send any data until connection is established.
693 bool sent
= websocket_
->SendData(kHandshakeRequestWithoutCookie
,
694 kHandshakeRequestWithoutCookieLength
);
696 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
700 // Following tests verify cooperation between WebSocketJob and SocketStream.
701 // Other former tests use MockSocketStream as SocketStream, so we could not
702 // check SocketStream behavior.
703 // OrderedSocketData provide socket level verifiation by checking out-going
704 // packets in comparison with the MockWrite array and emulating in-coming
705 // packets with MockRead array.
707 void WebSocketJobTest::TestConnectByWebSocket() {
708 // This is a test for verifying cooperation between WebSocketJob and
709 // SocketStream in basic situation.
710 MockWrite writes
[] = {
712 kHandshakeRequestWithoutCookie
,
713 kHandshakeRequestWithoutCookieLength
,
722 kHandshakeResponseWithoutCookie
,
723 kHandshakeResponseWithoutCookieLength
,
729 MockRead(false, 0, 5) // EOF
731 data_
= new OrderedSocketData(
732 reads
, arraysize(reads
), writes
, arraysize(writes
));
734 GURL
url("ws://example.com/demo");
735 MockSocketStreamDelegate delegate
;
736 WebSocketJobTest
* test
= this;
737 delegate
.SetOnConnected(
738 NewCallback(test
, &WebSocketJobTest::DoSendRequest
));
739 delegate
.SetOnReceivedData(
740 NewCallback(test
, &WebSocketJobTest::DoSendData
));
742 NewCallback(test
, &WebSocketJobTest::DoSync
));
743 InitWebSocketJob(url
, &delegate
, STREAM_SOCKET
);
745 websocket_
->Connect();
746 EXPECT_EQ(OK
, WaitForResult());
747 EXPECT_TRUE(data_
->at_read_eof());
748 EXPECT_TRUE(data_
->at_write_eof());
749 EXPECT_EQ(WebSocketJob::CLOSED
, GetWebSocketJobState());
752 void WebSocketJobTest::TestConnectBySpdy(bool use_spdy
) {
753 // This is a test for verifying cooperation between WebSocketJob and
754 // SocketStream in the situation we have SPDY session to the server.
755 MockWrite writes_websocket
[] = {
757 kHandshakeRequestWithoutCookie
,
758 kHandshakeRequestWithoutCookieLength
,
765 MockRead reads_websocket
[] = {
767 kHandshakeResponseWithoutCookie
,
768 kHandshakeResponseWithoutCookieLength
,
774 MockRead(false, 0, 5) // EOF
777 const spdy::SpdyStreamId kStreamId
= 1;
778 scoped_ptr
<spdy::SpdyFrame
> request_frame(
779 ConstructSpdyWebSocketHandshakeRequestFrame(
780 kHandshakeRequestForSpdy
,
781 arraysize(kHandshakeRequestForSpdy
) / 2,
784 scoped_ptr
<spdy::SpdyFrame
> response_frame(
785 ConstructSpdyWebSocketHandshakeResponseFrame(
786 kHandshakeResponseForSpdy
,
787 arraysize(kHandshakeResponseForSpdy
) / 2,
790 scoped_ptr
<spdy::SpdyFrame
> data_hello_frame(
791 ConstructSpdyWebSocketDataFrame(
796 scoped_ptr
<spdy::SpdyFrame
> data_world_frame(
797 ConstructSpdyWebSocketDataFrame(
802 MockWrite writes_spdy
[] = {
803 CreateMockWrite(*request_frame
.get(), 1),
804 CreateMockWrite(*data_hello_frame
.get(), 3),
806 MockRead reads_spdy
[] = {
807 CreateMockRead(*response_frame
.get(), 2),
808 CreateMockRead(*data_world_frame
.get(), 4),
809 MockRead(false, 0, 5) // EOF
813 data_
= new OrderedSocketData(
814 reads_spdy
, arraysize(reads_spdy
),
815 writes_spdy
, arraysize(writes_spdy
));
817 data_
= new OrderedSocketData(
818 reads_websocket
, arraysize(reads_websocket
),
819 writes_websocket
, arraysize(writes_websocket
));
821 GURL
url("ws://example.com/demo");
822 MockSocketStreamDelegate delegate
;
823 WebSocketJobTest
* test
= this;
824 delegate
.SetOnConnected(
825 NewCallback(test
, &WebSocketJobTest::DoSendRequest
));
826 delegate
.SetOnReceivedData(
827 NewCallback(test
, &WebSocketJobTest::DoSendData
));
829 NewCallback(test
, &WebSocketJobTest::DoSync
));
830 InitWebSocketJob(url
, &delegate
, STREAM_SPDY_WEBSOCKET
);
832 websocket_
->Connect();
833 EXPECT_EQ(OK
, WaitForResult());
835 EXPECT_TRUE(data_
->at_read_eof());
836 EXPECT_TRUE(data_
->at_write_eof());
837 EXPECT_EQ(WebSocketJob::CLOSED
, GetWebSocketJobState());
840 // Execute tests in both spdy-disabled mode and spdy-enabled mode.
841 TEST_F(WebSocketJobTest
, SimpleHandshake
) {
842 WebSocketJob::set_websocket_over_spdy_enabled(false);
843 TestSimpleHandshake();
846 TEST_F(WebSocketJobTest
, SlowHandshake
) {
847 WebSocketJob::set_websocket_over_spdy_enabled(false);
851 TEST_F(WebSocketJobTest
, HandshakeWithCookie
) {
852 WebSocketJob::set_websocket_over_spdy_enabled(false);
853 TestHandshakeWithCookie();
856 TEST_F(WebSocketJobTest
, HandshakeWithCookieButNotAllowed
) {
857 WebSocketJob::set_websocket_over_spdy_enabled(false);
858 TestHandshakeWithCookieButNotAllowed();
861 TEST_F(WebSocketJobTest
, HSTSUpgrade
) {
862 WebSocketJob::set_websocket_over_spdy_enabled(false);
866 TEST_F(WebSocketJobTest
, InvalidSendData
) {
867 WebSocketJob::set_websocket_over_spdy_enabled(false);
868 TestInvalidSendData();
871 TEST_F(WebSocketJobTest
, SimpleHandshakeSpdyEnabled
) {
872 WebSocketJob::set_websocket_over_spdy_enabled(true);
873 TestSimpleHandshake();
876 TEST_F(WebSocketJobTest
, SlowHandshakeSpdyEnabled
) {
877 WebSocketJob::set_websocket_over_spdy_enabled(true);
881 TEST_F(WebSocketJobTest
, HandshakeWithCookieSpdyEnabled
) {
882 WebSocketJob::set_websocket_over_spdy_enabled(true);
883 TestHandshakeWithCookie();
886 TEST_F(WebSocketJobTest
, HandshakeWithCookieButNotAllowedSpdyEnabled
) {
887 WebSocketJob::set_websocket_over_spdy_enabled(true);
888 TestHandshakeWithCookieButNotAllowed();
891 TEST_F(WebSocketJobTest
, HSTSUpgradeSpdyEnabled
) {
892 WebSocketJob::set_websocket_over_spdy_enabled(true);
896 TEST_F(WebSocketJobTest
, InvalidSendDataSpdyEnabled
) {
897 WebSocketJob::set_websocket_over_spdy_enabled(true);
898 TestInvalidSendData();
901 TEST_F(WebSocketJobTest
, ConnectByWebSocket
) {
902 WebSocketJob::set_websocket_over_spdy_enabled(false);
903 TestConnectByWebSocket();
906 TEST_F(WebSocketJobTest
, ConnectByWebSocketSpdyEnabled
) {
907 WebSocketJob::set_websocket_over_spdy_enabled(true);
908 TestConnectByWebSocket();
911 TEST_F(WebSocketJobTest
, ConnectBySpdy
) {
912 WebSocketJob::set_websocket_over_spdy_enabled(false);
913 TestConnectBySpdy(false);
916 TEST_F(WebSocketJobTest
, ConnectBySpdySpdyEnabled
) {
917 WebSocketJob::set_websocket_over_spdy_enabled(true);
918 TestConnectBySpdy(true);
921 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation.
922 // TODO(toyoshim,yutak): Add tests to verify closing handshake.