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.
5 #include "net/websockets/websocket_job.h"
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"
42 class MockSocketStream
: public SocketStream
{
44 MockSocketStream(const GURL
& url
, SocketStream::Delegate
* delegate
,
45 URLRequestContext
* context
, CookieStore
* cookie_store
)
46 : SocketStream(url
, delegate
, context
, cookie_store
) {}
48 void Connect() override
{}
49 bool SendData(const char* data
, int len
) override
{
50 sent_data_
+= std::string(data
, len
);
54 void Close() override
{}
55 void RestartWithAuth(const AuthCredentials
& credentials
) override
{}
57 void DetachDelegate() override
{ delegate_
= NULL
; }
59 const std::string
& sent_data() const {
64 ~MockSocketStream() override
{}
67 std::string sent_data_
;
70 class MockSocketStreamDelegate
: public SocketStream::Delegate
{
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
;
77 ~MockSocketStreamDelegate() override
{}
79 void SetOnStartOpenConnection(const base::Closure
& callback
) {
80 on_start_open_connection_
= callback
;
82 void SetOnConnected(const base::Closure
& callback
) {
83 on_connected_
= callback
;
85 void SetOnSentData(const base::Closure
& callback
) {
86 on_sent_data_
= callback
;
88 void SetOnReceivedData(const base::Closure
& callback
) {
89 on_received_data_
= callback
;
91 void SetOnClose(const base::Closure
& callback
) {
95 int OnStartOpenConnection(SocketStream
* socket
,
96 const CompletionCallback
& callback
) override
{
97 if (!on_start_open_connection_
.is_null())
98 on_start_open_connection_
.Run();
101 void OnConnected(SocketStream
* socket
,
102 int max_pending_send_allowed
) override
{
103 if (!on_connected_
.is_null())
106 void OnSentData(SocketStream
* socket
, int amount_sent
) override
{
107 amount_sent_
+= amount_sent
;
108 if (!on_sent_data_
.is_null())
111 void OnReceivedData(SocketStream
* socket
,
114 received_data_
+= std::string(data
, len
);
115 if (!on_received_data_
.is_null())
116 on_received_data_
.Run();
118 void OnClose(SocketStream
* socket
) override
{
119 if (!on_close_
.is_null())
122 bool CanGetCookies(SocketStream
* socket
, const GURL
& url
) override
{
123 return allow_all_cookies_
;
125 bool CanSetCookie(SocketStream
* request
,
127 const std::string
& cookie_line
,
128 CookieOptions
* options
) override
{
129 return allow_all_cookies_
;
132 size_t amount_sent() const { return amount_sent_
; }
133 const std::string
& received_data() const { return received_data_
; }
137 bool allow_all_cookies_
;
138 std::string received_data_
;
139 base::Closure on_start_open_connection_
;
140 base::Closure on_connected_
;
141 base::Closure on_sent_data_
;
142 base::Closure on_received_data_
;
143 base::Closure on_close_
;
146 class MockCookieStore
: public CookieStore
{
150 std::string cookie_line
;
151 CookieOptions options
;
156 bool SetCookieWithOptions(const GURL
& url
,
157 const std::string
& cookie_line
,
158 const CookieOptions
& options
) {
161 entry
.cookie_line
= cookie_line
;
162 entry
.options
= options
;
163 entries_
.push_back(entry
);
167 std::string
GetCookiesWithOptions(const GURL
& url
,
168 const CookieOptions
& options
) {
170 for (size_t i
= 0; i
< entries_
.size(); i
++) {
171 Entry
& entry
= entries_
[i
];
172 if (url
== entry
.url
) {
173 if (!result
.empty()) {
176 result
+= entry
.cookie_line
;
183 void SetCookieWithOptionsAsync(const GURL
& url
,
184 const std::string
& cookie_line
,
185 const CookieOptions
& options
,
186 const SetCookiesCallback
& callback
) override
{
187 bool result
= SetCookieWithOptions(url
, cookie_line
, options
);
188 if (!callback
.is_null())
189 callback
.Run(result
);
192 void GetCookiesWithOptionsAsync(const GURL
& url
,
193 const CookieOptions
& options
,
194 const GetCookiesCallback
& callback
) override
{
195 if (!callback
.is_null())
196 callback
.Run(GetCookiesWithOptions(url
, options
));
199 void GetAllCookiesForURLAsync(
201 const GetCookieListCallback
& callback
) override
{
205 void DeleteCookieAsync(const GURL
& url
,
206 const std::string
& cookie_name
,
207 const base::Closure
& callback
) override
{
211 void DeleteAllCreatedBetweenAsync(const base::Time
& delete_begin
,
212 const base::Time
& delete_end
,
213 const DeleteCallback
& callback
) override
{
217 void DeleteAllCreatedBetweenForHostAsync(
218 const base::Time delete_begin
,
219 const base::Time delete_end
,
221 const DeleteCallback
& callback
) override
{
225 void DeleteSessionCookiesAsync(const DeleteCallback
&) override
{
229 CookieMonster
* GetCookieMonster() override
{ return NULL
; }
231 scoped_ptr
<CookieStore::CookieChangedSubscription
>
232 AddCallbackForCookie(const GURL
& url
, const std::string
& name
,
233 const CookieChangedCallback
& callback
) override
{
235 return scoped_ptr
<CookieChangedSubscription
>();
238 const std::vector
<Entry
>& entries() const { return entries_
; }
241 friend class base::RefCountedThreadSafe
<MockCookieStore
>;
242 ~MockCookieStore() override
{}
244 std::vector
<Entry
> entries_
;
247 class MockSSLConfigService
: public SSLConfigService
{
249 void GetSSLConfig(SSLConfig
* config
) override
{}
252 ~MockSSLConfigService() override
{}
255 class MockURLRequestContext
: public URLRequestContext
{
257 explicit MockURLRequestContext(CookieStore
* cookie_store
)
258 : transport_security_state_() {
259 set_cookie_store(cookie_store
);
260 set_transport_security_state(&transport_security_state_
);
261 base::Time expiry
= base::Time::Now() + base::TimeDelta::FromDays(1000);
262 bool include_subdomains
= false;
263 transport_security_state_
.AddHSTS("upgrademe.com", expiry
,
267 ~MockURLRequestContext() override
{ AssertNoURLRequests(); }
270 TransportSecurityState transport_security_state_
;
273 class MockHttpTransactionFactory
: public HttpTransactionFactory
{
275 MockHttpTransactionFactory(NextProto next_proto
,
276 OrderedSocketData
* data
,
277 bool enable_websocket_over_spdy
) {
279 MockConnect
connect_data(SYNCHRONOUS
, OK
);
280 data_
->set_connect_data(connect_data
);
281 session_deps_
.reset(new SpdySessionDependencies(next_proto
));
282 session_deps_
->enable_websocket_over_spdy
= enable_websocket_over_spdy
;
283 session_deps_
->socket_factory
->AddSocketDataProvider(data_
);
285 SpdySessionDependencies::SpdyCreateSession(session_deps_
.get());
286 host_port_pair_
.set_host("example.com");
287 host_port_pair_
.set_port(80);
288 spdy_session_key_
= SpdySessionKey(host_port_pair_
,
289 ProxyServer::Direct(),
290 PRIVACY_MODE_DISABLED
);
291 session_
= CreateInsecureSpdySession(
292 http_session_
, spdy_session_key_
, BoundNetLog());
295 int CreateTransaction(RequestPriority priority
,
296 scoped_ptr
<HttpTransaction
>* trans
) override
{
298 return ERR_UNEXPECTED
;
301 HttpCache
* GetCache() override
{
306 HttpNetworkSession
* GetSession() override
{ return http_session_
.get(); }
309 OrderedSocketData
* data_
;
310 scoped_ptr
<SpdySessionDependencies
> session_deps_
;
311 scoped_refptr
<HttpNetworkSession
> http_session_
;
312 base::WeakPtr
<SpdySession
> session_
;
313 HostPortPair host_port_pair_
;
314 SpdySessionKey spdy_session_key_
;
317 class DeletingSocketStreamDelegate
: public SocketStream::Delegate
{
319 DeletingSocketStreamDelegate()
320 : delete_next_(false) {}
322 // Since this class needs to be able to delete |job_|, it must be the only
323 // reference holder (except for temporary references). Provide access to the
324 // pointer for tests to use.
325 WebSocketJob
* job() { return job_
.get(); }
327 void set_job(WebSocketJob
* job
) { job_
= job
; }
329 // After calling this, the next call to a method on this delegate will delete
330 // the WebSocketJob object.
331 void set_delete_next(bool delete_next
) { delete_next_
= delete_next
; }
333 void DeleteJobMaybe() {
335 job_
->DetachContext();
336 job_
->DetachDelegate();
341 // SocketStream::Delegate implementation
343 // OnStartOpenConnection() is not implemented by SocketStreamDispatcherHost
345 void OnConnected(SocketStream
* socket
,
346 int max_pending_send_allowed
) override
{
350 void OnSentData(SocketStream
* socket
, int amount_sent
) override
{
354 void OnReceivedData(SocketStream
* socket
,
360 void OnClose(SocketStream
* socket
) override
{ DeleteJobMaybe(); }
362 void OnAuthRequired(SocketStream
* socket
,
363 AuthChallengeInfo
* auth_info
) override
{
367 void OnSSLCertificateError(SocketStream
* socket
,
368 const SSLInfo
& ssl_info
,
369 bool fatal
) override
{
373 void OnError(const SocketStream
* socket
, int error
) override
{
377 // CanGetCookies() and CanSetCookies() do not appear to be able to delete the
378 // WebSocketJob object.
381 scoped_refptr
<WebSocketJob
> job_
;
387 class WebSocketJobTest
: public PlatformTest
,
388 public ::testing::WithParamInterface
<NextProto
> {
391 : spdy_util_(GetParam()),
392 enable_websocket_over_spdy_(false) {}
394 void SetUp() override
{
395 stream_type_
= STREAM_INVALID
;
396 cookie_store_
= new MockCookieStore
;
397 context_
.reset(new MockURLRequestContext(cookie_store_
.get()));
399 void TearDown() override
{
400 cookie_store_
= NULL
;
405 void DoSendRequest() {
406 EXPECT_TRUE(websocket_
->SendData(kHandshakeRequestWithoutCookie
,
407 kHandshakeRequestWithoutCookieLength
));
410 if (received_data().size() == kHandshakeResponseWithoutCookieLength
)
411 websocket_
->SendData(kDataHello
, kDataHelloLength
);
414 sync_test_callback_
.callback().Run(OK
);
416 int WaitForResult() {
417 return sync_test_callback_
.WaitForResult();
425 STREAM_SPDY_WEBSOCKET
,
427 enum ThrottlingOption
{
435 void InitWebSocketJob(const GURL
& url
,
436 MockSocketStreamDelegate
* delegate
,
437 StreamType stream_type
) {
438 DCHECK_NE(STREAM_INVALID
, stream_type
);
439 stream_type_
= stream_type
;
440 websocket_
= new WebSocketJob(delegate
);
442 if (stream_type
== STREAM_MOCK_SOCKET
)
443 socket_
= new MockSocketStream(url
, websocket_
.get(), context_
.get(),
446 if (stream_type
== STREAM_SOCKET
|| stream_type
== STREAM_SPDY_WEBSOCKET
) {
447 if (stream_type
== STREAM_SPDY_WEBSOCKET
) {
448 http_factory_
.reset(new MockHttpTransactionFactory(
449 GetParam(), data_
.get(), enable_websocket_over_spdy_
));
450 context_
->set_http_transaction_factory(http_factory_
.get());
453 ssl_config_service_
= new MockSSLConfigService();
454 context_
->set_ssl_config_service(ssl_config_service_
.get());
455 proxy_service_
.reset(ProxyService::CreateDirect());
456 context_
->set_proxy_service(proxy_service_
.get());
457 host_resolver_
.reset(new MockHostResolver
);
458 context_
->set_host_resolver(host_resolver_
.get());
460 socket_
= new SocketStream(url
, websocket_
.get(), context_
.get(), NULL
);
461 socket_factory_
.reset(new MockClientSocketFactory
);
463 socket_factory_
->AddSocketDataProvider(data_
.get());
464 socket_
->SetClientSocketFactory(socket_factory_
.get());
467 websocket_
->InitSocketStream(socket_
.get());
468 // MockHostResolver resolves all hosts to 127.0.0.1; however, when we create
469 // a WebSocketJob purely to block another one in a throttling test, we don't
470 // perform a real connect. In that case, the following address is used
473 ParseIPLiteralToNumber("127.0.0.1", &ip
);
474 websocket_
->addresses_
= AddressList::CreateFromIPAddress(ip
, 80);
476 void SkipToConnecting() {
477 websocket_
->state_
= WebSocketJob::CONNECTING
;
478 ASSERT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(websocket_
.get()));
480 WebSocketJob::State
GetWebSocketJobState() {
481 return websocket_
->state_
;
483 void CloseWebSocketJob() {
484 if (websocket_
->socket_
.get()) {
485 websocket_
->socket_
->DetachDelegate();
486 WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_
.get());
488 websocket_
->state_
= WebSocketJob::CLOSED
;
489 websocket_
->delegate_
= NULL
;
490 websocket_
->socket_
= NULL
;
492 SocketStream
* GetSocket(SocketStreamJob
* job
) {
493 return job
->socket_
.get();
495 const std::string
& sent_data() const {
496 DCHECK_EQ(STREAM_MOCK_SOCKET
, stream_type_
);
497 MockSocketStream
* socket
=
498 static_cast<MockSocketStream
*>(socket_
.get());
500 return socket
->sent_data();
502 const std::string
& received_data() const {
503 DCHECK_NE(STREAM_INVALID
, stream_type_
);
504 MockSocketStreamDelegate
* delegate
=
505 static_cast<MockSocketStreamDelegate
*>(websocket_
->delegate_
);
507 return delegate
->received_data();
510 void TestSimpleHandshake();
511 void TestSlowHandshake();
512 void TestHandshakeWithCookie();
513 void TestHandshakeWithCookieButNotAllowed();
514 void TestHSTSUpgrade();
515 void TestInvalidSendData();
516 void TestConnectByWebSocket(ThrottlingOption throttling
);
517 void TestConnectBySpdy(SpdyOption spdy
, ThrottlingOption throttling
);
518 void TestThrottlingLimit();
520 SpdyWebSocketTestUtil spdy_util_
;
521 StreamType stream_type_
;
522 scoped_refptr
<MockCookieStore
> cookie_store_
;
523 scoped_ptr
<MockURLRequestContext
> context_
;
524 scoped_refptr
<WebSocketJob
> websocket_
;
525 scoped_refptr
<SocketStream
> socket_
;
526 scoped_ptr
<MockClientSocketFactory
> socket_factory_
;
527 scoped_ptr
<OrderedSocketData
> data_
;
528 TestCompletionCallback sync_test_callback_
;
529 scoped_refptr
<MockSSLConfigService
> ssl_config_service_
;
530 scoped_ptr
<ProxyService
> proxy_service_
;
531 scoped_ptr
<MockHostResolver
> host_resolver_
;
532 scoped_ptr
<MockHttpTransactionFactory
> http_factory_
;
534 // Must be set before call to enable_websocket_over_spdy, defaults to false.
535 bool enable_websocket_over_spdy_
;
537 static const char kHandshakeRequestWithoutCookie
[];
538 static const char kHandshakeRequestWithCookie
[];
539 static const char kHandshakeRequestWithFilteredCookie
[];
540 static const char kHandshakeResponseWithoutCookie
[];
541 static const char kHandshakeResponseWithCookie
[];
542 static const char kDataHello
[];
543 static const char kDataWorld
[];
544 static const char* const kHandshakeRequestForSpdy
[];
545 static const char* const kHandshakeResponseForSpdy
[];
546 static const size_t kHandshakeRequestWithoutCookieLength
;
547 static const size_t kHandshakeRequestWithCookieLength
;
548 static const size_t kHandshakeRequestWithFilteredCookieLength
;
549 static const size_t kHandshakeResponseWithoutCookieLength
;
550 static const size_t kHandshakeResponseWithCookieLength
;
551 static const size_t kDataHelloLength
;
552 static const size_t kDataWorldLength
;
555 // Tests using this fixture verify that the WebSocketJob can handle being
556 // deleted while calling back to the delegate correctly. These tests need to be
557 // run under AddressSanitizer or other systems for detecting use-after-free
558 // errors in order to find problems.
559 class WebSocketJobDeleteTest
: public ::testing::Test
{
561 WebSocketJobDeleteTest()
562 : delegate_(new DeletingSocketStreamDelegate
),
563 cookie_store_(new MockCookieStore
),
564 context_(new MockURLRequestContext(cookie_store_
.get())) {
565 WebSocketJob
* websocket
= new WebSocketJob(delegate_
.get());
566 delegate_
->set_job(websocket
);
568 socket_
= new MockSocketStream(
569 GURL("ws://127.0.0.1/"), websocket
, context_
.get(), NULL
);
571 websocket
->InitSocketStream(socket_
.get());
574 void SetDeleteNext() { return delegate_
->set_delete_next(true); }
575 WebSocketJob
* job() { return delegate_
->job(); }
577 scoped_ptr
<DeletingSocketStreamDelegate
> delegate_
;
578 scoped_refptr
<MockCookieStore
> cookie_store_
;
579 scoped_ptr
<MockURLRequestContext
> context_
;
580 scoped_refptr
<SocketStream
> socket_
;
583 const char WebSocketJobTest::kHandshakeRequestWithoutCookie
[] =
584 "GET /demo HTTP/1.1\r\n"
585 "Host: example.com\r\n"
586 "Upgrade: WebSocket\r\n"
587 "Connection: Upgrade\r\n"
588 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
589 "Origin: http://example.com\r\n"
590 "Sec-WebSocket-Protocol: sample\r\n"
591 "Sec-WebSocket-Version: 13\r\n"
594 const char WebSocketJobTest::kHandshakeRequestWithCookie
[] =
595 "GET /demo HTTP/1.1\r\n"
596 "Host: example.com\r\n"
597 "Upgrade: WebSocket\r\n"
598 "Connection: Upgrade\r\n"
599 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
600 "Origin: http://example.com\r\n"
601 "Sec-WebSocket-Protocol: sample\r\n"
602 "Sec-WebSocket-Version: 13\r\n"
603 "Cookie: WK-test=1\r\n"
606 const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie
[] =
607 "GET /demo HTTP/1.1\r\n"
608 "Host: example.com\r\n"
609 "Upgrade: WebSocket\r\n"
610 "Connection: Upgrade\r\n"
611 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
612 "Origin: http://example.com\r\n"
613 "Sec-WebSocket-Protocol: sample\r\n"
614 "Sec-WebSocket-Version: 13\r\n"
615 "Cookie: CR-test=1; CR-test-httponly=1\r\n"
618 const char WebSocketJobTest::kHandshakeResponseWithoutCookie
[] =
619 "HTTP/1.1 101 Switching Protocols\r\n"
620 "Upgrade: websocket\r\n"
621 "Connection: Upgrade\r\n"
622 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
623 "Sec-WebSocket-Protocol: sample\r\n"
626 const char WebSocketJobTest::kHandshakeResponseWithCookie
[] =
627 "HTTP/1.1 101 Switching Protocols\r\n"
628 "Upgrade: websocket\r\n"
629 "Connection: Upgrade\r\n"
630 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
631 "Sec-WebSocket-Protocol: sample\r\n"
632 "Set-Cookie: CR-set-test=1\r\n"
635 const char WebSocketJobTest::kDataHello
[] = "Hello, ";
637 const char WebSocketJobTest::kDataWorld
[] = "World!\n";
639 const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength
=
640 arraysize(kHandshakeRequestWithoutCookie
) - 1;
641 const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength
=
642 arraysize(kHandshakeRequestWithCookie
) - 1;
643 const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength
=
644 arraysize(kHandshakeRequestWithFilteredCookie
) - 1;
645 const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength
=
646 arraysize(kHandshakeResponseWithoutCookie
) - 1;
647 const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength
=
648 arraysize(kHandshakeResponseWithCookie
) - 1;
649 const size_t WebSocketJobTest::kDataHelloLength
=
650 arraysize(kDataHello
) - 1;
651 const size_t WebSocketJobTest::kDataWorldLength
=
652 arraysize(kDataWorld
) - 1;
654 void WebSocketJobTest::TestSimpleHandshake() {
655 GURL
url("ws://example.com/demo");
656 MockSocketStreamDelegate delegate
;
657 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
661 base::MessageLoop::current()->RunUntilIdle();
662 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
663 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
664 websocket_
->OnSentData(socket_
.get(),
665 kHandshakeRequestWithoutCookieLength
);
666 EXPECT_EQ(kHandshakeRequestWithoutCookieLength
, delegate
.amount_sent());
668 websocket_
->OnReceivedData(socket_
.get(),
669 kHandshakeResponseWithoutCookie
,
670 kHandshakeResponseWithoutCookieLength
);
671 base::MessageLoop::current()->RunUntilIdle();
672 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
673 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
677 void WebSocketJobTest::TestSlowHandshake() {
678 GURL
url("ws://example.com/demo");
679 MockSocketStreamDelegate delegate
;
680 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
684 // We assume request is sent in one data chunk (from WebKit)
685 // We don't support streaming request.
686 base::MessageLoop::current()->RunUntilIdle();
687 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
688 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
689 websocket_
->OnSentData(socket_
.get(),
690 kHandshakeRequestWithoutCookieLength
);
691 EXPECT_EQ(kHandshakeRequestWithoutCookieLength
, delegate
.amount_sent());
693 std::vector
<std::string
> lines
;
694 base::SplitString(kHandshakeResponseWithoutCookie
, '\n', &lines
);
695 for (size_t i
= 0; i
< lines
.size() - 2; i
++) {
696 std::string line
= lines
[i
] + "\r\n";
697 SCOPED_TRACE("Line: " + line
);
698 websocket_
->OnReceivedData(socket_
.get(), line
.c_str(), line
.size());
699 base::MessageLoop::current()->RunUntilIdle();
700 EXPECT_TRUE(delegate
.received_data().empty());
701 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
703 websocket_
->OnReceivedData(socket_
.get(), "\r\n", 2);
704 base::MessageLoop::current()->RunUntilIdle();
705 EXPECT_FALSE(delegate
.received_data().empty());
706 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
707 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
711 INSTANTIATE_TEST_CASE_P(
714 testing::Values(kProtoDeprecatedSPDY2
,
715 kProtoSPDY3
, kProtoSPDY31
, kProtoSPDY4
));
717 TEST_P(WebSocketJobTest
, DelayedCookies
) {
718 enable_websocket_over_spdy_
= true;
719 GURL
url("ws://example.com/demo");
720 GURL
cookieUrl("http://example.com/demo");
721 CookieOptions cookie_options
;
722 scoped_refptr
<DelayedCookieMonster
> cookie_store
= new DelayedCookieMonster();
723 context_
->set_cookie_store(cookie_store
.get());
724 cookie_store
->SetCookieWithOptionsAsync(cookieUrl
,
727 CookieMonster::SetCookiesCallback());
728 cookie_options
.set_include_httponly();
729 cookie_store
->SetCookieWithOptionsAsync(
730 cookieUrl
, "CR-test-httponly=1", cookie_options
,
731 CookieMonster::SetCookiesCallback());
733 MockSocketStreamDelegate delegate
;
734 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
737 bool sent
= websocket_
->SendData(kHandshakeRequestWithCookie
,
738 kHandshakeRequestWithCookieLength
);
740 base::MessageLoop::current()->RunUntilIdle();
741 EXPECT_EQ(kHandshakeRequestWithFilteredCookie
, sent_data());
742 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
743 websocket_
->OnSentData(socket_
.get(),
744 kHandshakeRequestWithFilteredCookieLength
);
745 EXPECT_EQ(kHandshakeRequestWithCookieLength
,
746 delegate
.amount_sent());
748 websocket_
->OnReceivedData(socket_
.get(),
749 kHandshakeResponseWithCookie
,
750 kHandshakeResponseWithCookieLength
);
751 base::MessageLoop::current()->RunUntilIdle();
752 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
753 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
758 void WebSocketJobTest::TestHandshakeWithCookie() {
759 GURL
url("ws://example.com/demo");
760 GURL
cookieUrl("http://example.com/demo");
761 CookieOptions cookie_options
;
762 cookie_store_
->SetCookieWithOptions(
763 cookieUrl
, "CR-test=1", cookie_options
);
764 cookie_options
.set_include_httponly();
765 cookie_store_
->SetCookieWithOptions(
766 cookieUrl
, "CR-test-httponly=1", cookie_options
);
768 MockSocketStreamDelegate delegate
;
769 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
772 bool sent
= websocket_
->SendData(kHandshakeRequestWithCookie
,
773 kHandshakeRequestWithCookieLength
);
775 base::MessageLoop::current()->RunUntilIdle();
776 EXPECT_EQ(kHandshakeRequestWithFilteredCookie
, sent_data());
777 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
778 websocket_
->OnSentData(socket_
.get(),
779 kHandshakeRequestWithFilteredCookieLength
);
780 EXPECT_EQ(kHandshakeRequestWithCookieLength
,
781 delegate
.amount_sent());
783 websocket_
->OnReceivedData(socket_
.get(),
784 kHandshakeResponseWithCookie
,
785 kHandshakeResponseWithCookieLength
);
786 base::MessageLoop::current()->RunUntilIdle();
787 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
788 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
790 EXPECT_EQ(3U, cookie_store_
->entries().size());
791 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[0].url
);
792 EXPECT_EQ("CR-test=1", cookie_store_
->entries()[0].cookie_line
);
793 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[1].url
);
794 EXPECT_EQ("CR-test-httponly=1", cookie_store_
->entries()[1].cookie_line
);
795 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[2].url
);
796 EXPECT_EQ("CR-set-test=1", cookie_store_
->entries()[2].cookie_line
);
801 void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() {
802 GURL
url("ws://example.com/demo");
803 GURL
cookieUrl("http://example.com/demo");
804 CookieOptions cookie_options
;
805 cookie_store_
->SetCookieWithOptions(
806 cookieUrl
, "CR-test=1", cookie_options
);
807 cookie_options
.set_include_httponly();
808 cookie_store_
->SetCookieWithOptions(
809 cookieUrl
, "CR-test-httponly=1", cookie_options
);
811 MockSocketStreamDelegate delegate
;
812 delegate
.set_allow_all_cookies(false);
813 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
816 bool sent
= websocket_
->SendData(kHandshakeRequestWithCookie
,
817 kHandshakeRequestWithCookieLength
);
819 base::MessageLoop::current()->RunUntilIdle();
820 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
821 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
822 websocket_
->OnSentData(socket_
.get(), kHandshakeRequestWithoutCookieLength
);
823 EXPECT_EQ(kHandshakeRequestWithCookieLength
, delegate
.amount_sent());
825 websocket_
->OnReceivedData(socket_
.get(),
826 kHandshakeResponseWithCookie
,
827 kHandshakeResponseWithCookieLength
);
828 base::MessageLoop::current()->RunUntilIdle();
829 EXPECT_EQ(kHandshakeResponseWithoutCookie
, delegate
.received_data());
830 EXPECT_EQ(WebSocketJob::OPEN
, GetWebSocketJobState());
832 EXPECT_EQ(2U, cookie_store_
->entries().size());
833 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[0].url
);
834 EXPECT_EQ("CR-test=1", cookie_store_
->entries()[0].cookie_line
);
835 EXPECT_EQ(cookieUrl
, cookie_store_
->entries()[1].url
);
836 EXPECT_EQ("CR-test-httponly=1", cookie_store_
->entries()[1].cookie_line
);
841 void WebSocketJobTest::TestHSTSUpgrade() {
842 GURL
url("ws://upgrademe.com/");
843 MockSocketStreamDelegate delegate
;
844 scoped_refptr
<SocketStreamJob
> job
=
845 SocketStreamJob::CreateSocketStreamJob(
846 url
, &delegate
, context_
->transport_security_state(),
847 context_
->ssl_config_service(), NULL
, NULL
);
848 EXPECT_TRUE(GetSocket(job
.get())->is_secure());
849 job
->DetachDelegate();
851 url
= GURL("ws://donotupgrademe.com/");
852 job
= SocketStreamJob::CreateSocketStreamJob(
853 url
, &delegate
, context_
->transport_security_state(),
854 context_
->ssl_config_service(), NULL
, NULL
);
855 EXPECT_FALSE(GetSocket(job
.get())->is_secure());
856 job
->DetachDelegate();
859 void WebSocketJobTest::TestInvalidSendData() {
860 GURL
url("ws://example.com/demo");
861 MockSocketStreamDelegate delegate
;
862 InitWebSocketJob(url
, &delegate
, STREAM_MOCK_SOCKET
);
866 // We assume request is sent in one data chunk (from WebKit)
867 // We don't support streaming request.
868 base::MessageLoop::current()->RunUntilIdle();
869 EXPECT_EQ(kHandshakeRequestWithoutCookie
, sent_data());
870 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
871 websocket_
->OnSentData(socket_
.get(),
872 kHandshakeRequestWithoutCookieLength
);
873 EXPECT_EQ(kHandshakeRequestWithoutCookieLength
, delegate
.amount_sent());
875 // We could not send any data until connection is established.
876 bool sent
= websocket_
->SendData(kHandshakeRequestWithoutCookie
,
877 kHandshakeRequestWithoutCookieLength
);
879 EXPECT_EQ(WebSocketJob::CONNECTING
, GetWebSocketJobState());
883 // Following tests verify cooperation between WebSocketJob and SocketStream.
884 // Other former tests use MockSocketStream as SocketStream, so we could not
885 // check SocketStream behavior.
886 // OrderedSocketData provide socket level verifiation by checking out-going
887 // packets in comparison with the MockWrite array and emulating in-coming
888 // packets with MockRead array.
890 void WebSocketJobTest::TestConnectByWebSocket(
891 ThrottlingOption throttling
) {
892 // This is a test for verifying cooperation between WebSocketJob and
893 // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic
894 // situation. If |throttling| was |THROTTLING_ON|, throttling limits the
895 // latter connection.
896 MockWrite writes
[] = {
898 kHandshakeRequestWithoutCookie
,
899 kHandshakeRequestWithoutCookieLength
,
908 kHandshakeResponseWithoutCookie
,
909 kHandshakeResponseWithoutCookieLength
,
915 MockRead(SYNCHRONOUS
, 0, 5) // EOF
917 data_
.reset(new OrderedSocketData(
918 reads
, arraysize(reads
), writes
, arraysize(writes
)));
920 GURL
url("ws://example.com/demo");
921 MockSocketStreamDelegate delegate
;
922 WebSocketJobTest
* test
= this;
923 if (throttling
== THROTTLING_ON
)
924 delegate
.SetOnStartOpenConnection(
925 base::Bind(&WebSocketJobTest::DoSync
, base::Unretained(test
)));
926 delegate
.SetOnConnected(
927 base::Bind(&WebSocketJobTest::DoSendRequest
,
928 base::Unretained(test
)));
929 delegate
.SetOnReceivedData(
930 base::Bind(&WebSocketJobTest::DoSendData
, base::Unretained(test
)));
932 base::Bind(&WebSocketJobTest::DoSync
, base::Unretained(test
)));
933 InitWebSocketJob(url
, &delegate
, STREAM_SOCKET
);
935 scoped_refptr
<WebSocketJob
> block_websocket
;
936 if (throttling
== THROTTLING_ON
) {
937 // Create former WebSocket object which obstructs the latter one.
938 block_websocket
= new WebSocketJob(NULL
);
939 block_websocket
->addresses_
= AddressList(websocket_
->address_list());
941 WebSocketThrottle::GetInstance()->PutInQueue(block_websocket
.get()));
944 websocket_
->Connect();
946 if (throttling
== THROTTLING_ON
) {
947 EXPECT_EQ(OK
, WaitForResult());
948 EXPECT_TRUE(websocket_
->IsWaiting());
950 // Remove the former WebSocket object from throttling queue to unblock the
952 block_websocket
->state_
= WebSocketJob::CLOSED
;
953 WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket
.get());
954 block_websocket
= NULL
;
957 EXPECT_EQ(OK
, WaitForResult());
958 EXPECT_TRUE(data_
->at_read_eof());
959 EXPECT_TRUE(data_
->at_write_eof());
960 EXPECT_EQ(WebSocketJob::CLOSED
, GetWebSocketJobState());
963 void WebSocketJobTest::TestConnectBySpdy(
964 SpdyOption spdy
, ThrottlingOption throttling
) {
965 // This is a test for verifying cooperation between WebSocketJob and
966 // SocketStream in the situation we have SPDY session to the server. If
967 // |throttling| was |THROTTLING_ON|, throttling limits the latter connection.
968 // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected
969 // results depend on its configuration.
970 MockWrite writes_websocket
[] = {
972 kHandshakeRequestWithoutCookie
,
973 kHandshakeRequestWithoutCookieLength
,
980 MockRead reads_websocket
[] = {
982 kHandshakeResponseWithoutCookie
,
983 kHandshakeResponseWithoutCookieLength
,
989 MockRead(SYNCHRONOUS
, 0, 5) // EOF
992 scoped_ptr
<SpdyHeaderBlock
> request_headers(new SpdyHeaderBlock());
993 spdy_util_
.SetHeader("path", "/demo", request_headers
.get());
994 spdy_util_
.SetHeader("version", "WebSocket/13", request_headers
.get());
995 spdy_util_
.SetHeader("scheme", "ws", request_headers
.get());
996 spdy_util_
.SetHeader("host", "example.com", request_headers
.get());
997 spdy_util_
.SetHeader("origin", "http://example.com", request_headers
.get());
998 spdy_util_
.SetHeader("sec-websocket-protocol", "sample",
999 request_headers
.get());
1001 scoped_ptr
<SpdyHeaderBlock
> response_headers(new SpdyHeaderBlock());
1002 spdy_util_
.SetHeader("status", "101 Switching Protocols",
1003 response_headers
.get());
1004 spdy_util_
.SetHeader("sec-websocket-protocol", "sample",
1005 response_headers
.get());
1007 const SpdyStreamId kStreamId
= 1;
1008 scoped_ptr
<SpdyFrame
> request_frame(
1009 spdy_util_
.ConstructSpdyWebSocketHandshakeRequestFrame(
1010 request_headers
.Pass(),
1013 scoped_ptr
<SpdyFrame
> response_frame(
1014 spdy_util_
.ConstructSpdyWebSocketHandshakeResponseFrame(
1015 response_headers
.Pass(),
1018 scoped_ptr
<SpdyFrame
> data_hello_frame(
1019 spdy_util_
.ConstructSpdyWebSocketDataFrame(
1024 scoped_ptr
<SpdyFrame
> data_world_frame(
1025 spdy_util_
.ConstructSpdyWebSocketDataFrame(
1030 MockWrite writes_spdy
[] = {
1031 CreateMockWrite(*request_frame
.get(), 1),
1032 CreateMockWrite(*data_hello_frame
.get(), 3),
1034 MockRead reads_spdy
[] = {
1035 CreateMockRead(*response_frame
.get(), 2),
1036 CreateMockRead(*data_world_frame
.get(), 4),
1037 MockRead(SYNCHRONOUS
, 0, 5) // EOF
1040 if (spdy
== SPDY_ON
)
1041 data_
.reset(new OrderedSocketData(
1042 reads_spdy
, arraysize(reads_spdy
),
1043 writes_spdy
, arraysize(writes_spdy
)));
1045 data_
.reset(new OrderedSocketData(
1046 reads_websocket
, arraysize(reads_websocket
),
1047 writes_websocket
, arraysize(writes_websocket
)));
1049 GURL
url("ws://example.com/demo");
1050 MockSocketStreamDelegate delegate
;
1051 WebSocketJobTest
* test
= this;
1052 if (throttling
== THROTTLING_ON
)
1053 delegate
.SetOnStartOpenConnection(
1054 base::Bind(&WebSocketJobTest::DoSync
, base::Unretained(test
)));
1055 delegate
.SetOnConnected(
1056 base::Bind(&WebSocketJobTest::DoSendRequest
,
1057 base::Unretained(test
)));
1058 delegate
.SetOnReceivedData(
1059 base::Bind(&WebSocketJobTest::DoSendData
, base::Unretained(test
)));
1060 delegate
.SetOnClose(
1061 base::Bind(&WebSocketJobTest::DoSync
, base::Unretained(test
)));
1062 InitWebSocketJob(url
, &delegate
, STREAM_SPDY_WEBSOCKET
);
1064 scoped_refptr
<WebSocketJob
> block_websocket
;
1065 if (throttling
== THROTTLING_ON
) {
1066 // Create former WebSocket object which obstructs the latter one.
1067 block_websocket
= new WebSocketJob(NULL
);
1068 block_websocket
->addresses_
= AddressList(websocket_
->address_list());
1070 WebSocketThrottle::GetInstance()->PutInQueue(block_websocket
.get()));
1073 websocket_
->Connect();
1075 if (throttling
== THROTTLING_ON
) {
1076 EXPECT_EQ(OK
, WaitForResult());
1077 EXPECT_TRUE(websocket_
->IsWaiting());
1079 // Remove the former WebSocket object from throttling queue to unblock the
1081 block_websocket
->state_
= WebSocketJob::CLOSED
;
1082 WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket
.get());
1083 block_websocket
= NULL
;
1086 EXPECT_EQ(OK
, WaitForResult());
1087 EXPECT_TRUE(data_
->at_read_eof());
1088 EXPECT_TRUE(data_
->at_write_eof());
1089 EXPECT_EQ(WebSocketJob::CLOSED
, GetWebSocketJobState());
1092 void WebSocketJobTest::TestThrottlingLimit() {
1093 std::vector
<scoped_refptr
<WebSocketJob
> > jobs
;
1094 const int kMaxWebSocketJobsThrottled
= 1024;
1096 ParseIPLiteralToNumber("127.0.0.1", &ip
);
1097 for (int i
= 0; i
< kMaxWebSocketJobsThrottled
+ 1; ++i
) {
1098 scoped_refptr
<WebSocketJob
> job
= new WebSocketJob(NULL
);
1099 job
->addresses_
= AddressList(AddressList::CreateFromIPAddress(ip
, 80));
1100 if (i
>= kMaxWebSocketJobsThrottled
)
1101 EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job
.get()));
1103 EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job
.get()));
1104 jobs
.push_back(job
);
1107 // Close the jobs in reverse order. Otherwise, We need to make them prepared
1109 for (std::vector
<scoped_refptr
<WebSocketJob
> >::reverse_iterator iter
=
1111 iter
!= jobs
.rend();
1113 WebSocketJob
* job
= (*iter
).get();
1114 job
->state_
= WebSocketJob::CLOSED
;
1115 WebSocketThrottle::GetInstance()->RemoveFromQueue(job
);
1119 // Execute tests in both spdy-disabled mode and spdy-enabled mode.
1120 TEST_P(WebSocketJobTest
, SimpleHandshake
) {
1121 TestSimpleHandshake();
1124 TEST_P(WebSocketJobTest
, SlowHandshake
) {
1125 TestSlowHandshake();
1128 TEST_P(WebSocketJobTest
, HandshakeWithCookie
) {
1129 TestHandshakeWithCookie();
1132 TEST_P(WebSocketJobTest
, HandshakeWithCookieButNotAllowed
) {
1133 TestHandshakeWithCookieButNotAllowed();
1136 TEST_P(WebSocketJobTest
, HSTSUpgrade
) {
1140 TEST_P(WebSocketJobTest
, InvalidSendData
) {
1141 TestInvalidSendData();
1144 TEST_P(WebSocketJobTest
, SimpleHandshakeSpdyEnabled
) {
1145 enable_websocket_over_spdy_
= true;
1146 TestSimpleHandshake();
1149 TEST_P(WebSocketJobTest
, SlowHandshakeSpdyEnabled
) {
1150 enable_websocket_over_spdy_
= true;
1151 TestSlowHandshake();
1154 TEST_P(WebSocketJobTest
, HandshakeWithCookieSpdyEnabled
) {
1155 enable_websocket_over_spdy_
= true;
1156 TestHandshakeWithCookie();
1159 TEST_P(WebSocketJobTest
, HandshakeWithCookieButNotAllowedSpdyEnabled
) {
1160 enable_websocket_over_spdy_
= true;
1161 TestHandshakeWithCookieButNotAllowed();
1164 TEST_P(WebSocketJobTest
, HSTSUpgradeSpdyEnabled
) {
1165 enable_websocket_over_spdy_
= true;
1169 TEST_P(WebSocketJobTest
, InvalidSendDataSpdyEnabled
) {
1170 enable_websocket_over_spdy_
= true;
1171 TestInvalidSendData();
1174 TEST_P(WebSocketJobTest
, ConnectByWebSocket
) {
1175 enable_websocket_over_spdy_
= true;
1176 TestConnectByWebSocket(THROTTLING_OFF
);
1179 TEST_P(WebSocketJobTest
, ConnectByWebSocketSpdyEnabled
) {
1180 enable_websocket_over_spdy_
= true;
1181 TestConnectByWebSocket(THROTTLING_OFF
);
1184 TEST_P(WebSocketJobTest
, ConnectBySpdy
) {
1185 TestConnectBySpdy(SPDY_OFF
, THROTTLING_OFF
);
1188 TEST_P(WebSocketJobTest
, ConnectBySpdySpdyEnabled
) {
1189 enable_websocket_over_spdy_
= true;
1190 TestConnectBySpdy(SPDY_ON
, THROTTLING_OFF
);
1193 TEST_P(WebSocketJobTest
, ThrottlingWebSocket
) {
1194 TestConnectByWebSocket(THROTTLING_ON
);
1197 TEST_P(WebSocketJobTest
, ThrottlingMaxNumberOfThrottledJobLimit
) {
1198 TestThrottlingLimit();
1201 TEST_P(WebSocketJobTest
, ThrottlingWebSocketSpdyEnabled
) {
1202 enable_websocket_over_spdy_
= true;
1203 TestConnectByWebSocket(THROTTLING_ON
);
1206 TEST_P(WebSocketJobTest
, ThrottlingSpdy
) {
1207 TestConnectBySpdy(SPDY_OFF
, THROTTLING_ON
);
1210 TEST_P(WebSocketJobTest
, ThrottlingSpdySpdyEnabled
) {
1211 enable_websocket_over_spdy_
= true;
1212 TestConnectBySpdy(SPDY_ON
, THROTTLING_ON
);
1215 TEST_F(WebSocketJobDeleteTest
, OnClose
) {
1217 job()->OnClose(socket_
.get());
1218 // OnClose() sets WebSocketJob::_socket to NULL before we can detach it, so
1219 // socket_->delegate is still set at this point. Clear it to avoid hitting
1220 // DCHECK(!delegate_) in the SocketStream destructor. SocketStream::Finish()
1221 // is the only caller of this method in real code, and it also sets delegate_
1223 socket_
->DetachDelegate();
1224 EXPECT_FALSE(job());
1227 TEST_F(WebSocketJobDeleteTest
, OnAuthRequired
) {
1229 job()->OnAuthRequired(socket_
.get(), NULL
);
1230 EXPECT_FALSE(job());
1233 TEST_F(WebSocketJobDeleteTest
, OnSSLCertificateError
) {
1236 job()->OnSSLCertificateError(socket_
.get(), ssl_info
, true);
1237 EXPECT_FALSE(job());
1240 TEST_F(WebSocketJobDeleteTest
, OnError
) {
1242 job()->OnError(socket_
.get(), ERR_CONNECTION_RESET
);
1243 EXPECT_FALSE(job());
1246 TEST_F(WebSocketJobDeleteTest
, OnSentSpdyHeaders
) {
1249 job()->OnSentSpdyHeaders();
1250 EXPECT_FALSE(job());
1253 TEST_F(WebSocketJobDeleteTest
, OnSentHandshakeRequest
) {
1254 static const char kMinimalRequest
[] =
1255 "GET /demo HTTP/1.1\r\n"
1256 "Host: example.com\r\n"
1257 "Upgrade: WebSocket\r\n"
1258 "Connection: Upgrade\r\n"
1259 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
1260 "Origin: http://example.com\r\n"
1261 "Sec-WebSocket-Version: 13\r\n"
1263 const size_t kMinimalRequestSize
= arraysize(kMinimalRequest
) - 1;
1265 job()->SendData(kMinimalRequest
, kMinimalRequestSize
);
1267 job()->OnSentData(socket_
.get(), kMinimalRequestSize
);
1268 EXPECT_FALSE(job());
1271 TEST_F(WebSocketJobDeleteTest
, NotifyHeadersComplete
) {
1272 static const char kMinimalResponse
[] =
1273 "HTTP/1.1 101 Switching Protocols\r\n"
1274 "Upgrade: websocket\r\n"
1275 "Connection: Upgrade\r\n"
1276 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1280 job()->OnReceivedData(
1281 socket_
.get(), kMinimalResponse
, arraysize(kMinimalResponse
) - 1);
1282 EXPECT_FALSE(job());
1285 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation.
1286 // TODO(toyoshim,yutak): Add tests to verify closing handshake.