• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/websockets/websocket_job.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "net/base/completion_callback.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/cookies/cookie_store.h"
20 #include "net/cookies/cookie_store_test_helpers.h"
21 #include "net/dns/mock_host_resolver.h"
22 #include "net/http/http_transaction_factory.h"
23 #include "net/http/transport_security_state.h"
24 #include "net/proxy/proxy_service.h"
25 #include "net/socket/next_proto.h"
26 #include "net/socket/socket_test_util.h"
27 #include "net/socket_stream/socket_stream.h"
28 #include "net/spdy/spdy_session.h"
29 #include "net/spdy/spdy_websocket_test_util.h"
30 #include "net/ssl/ssl_config_service.h"
31 #include "net/url_request/url_request_context.h"
32 #include "net/websockets/websocket_throttle.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "testing/platform_test.h"
36 #include "url/gurl.h"
37 
38 namespace net {
39 
40 namespace {
41 
42 class MockSocketStream : public SocketStream {
43  public:
MockSocketStream(const GURL & url,SocketStream::Delegate * delegate,URLRequestContext * context,CookieStore * cookie_store)44   MockSocketStream(const GURL& url, SocketStream::Delegate* delegate,
45                    URLRequestContext* context, CookieStore* cookie_store)
46       : SocketStream(url, delegate, context, cookie_store) {}
47 
Connect()48   virtual void Connect() OVERRIDE {}
SendData(const char * data,int len)49   virtual bool SendData(const char* data, int len) OVERRIDE {
50     sent_data_ += std::string(data, len);
51     return true;
52   }
53 
Close()54   virtual void Close() OVERRIDE {}
RestartWithAuth(const AuthCredentials & credentials)55   virtual void RestartWithAuth(
56       const AuthCredentials& credentials) OVERRIDE {
57   }
58 
DetachDelegate()59   virtual void DetachDelegate() OVERRIDE {
60     delegate_ = NULL;
61   }
62 
sent_data() const63   const std::string& sent_data() const {
64     return sent_data_;
65   }
66 
67  protected:
~MockSocketStream()68   virtual ~MockSocketStream() {}
69 
70  private:
71   std::string sent_data_;
72 };
73 
74 class MockSocketStreamDelegate : public SocketStream::Delegate {
75  public:
MockSocketStreamDelegate()76   MockSocketStreamDelegate()
77       : amount_sent_(0), allow_all_cookies_(true) {}
set_allow_all_cookies(bool allow_all_cookies)78   void set_allow_all_cookies(bool allow_all_cookies) {
79     allow_all_cookies_ = allow_all_cookies;
80   }
~MockSocketStreamDelegate()81   virtual ~MockSocketStreamDelegate() {}
82 
SetOnStartOpenConnection(const base::Closure & callback)83   void SetOnStartOpenConnection(const base::Closure& callback) {
84     on_start_open_connection_ = callback;
85   }
SetOnConnected(const base::Closure & callback)86   void SetOnConnected(const base::Closure& callback) {
87     on_connected_ = callback;
88   }
SetOnSentData(const base::Closure & callback)89   void SetOnSentData(const base::Closure& callback) {
90     on_sent_data_ = callback;
91   }
SetOnReceivedData(const base::Closure & callback)92   void SetOnReceivedData(const base::Closure& callback) {
93     on_received_data_ = callback;
94   }
SetOnClose(const base::Closure & callback)95   void SetOnClose(const base::Closure& callback) {
96     on_close_ = callback;
97   }
98 
OnStartOpenConnection(SocketStream * socket,const CompletionCallback & callback)99   virtual int OnStartOpenConnection(
100       SocketStream* socket,
101       const CompletionCallback& callback) OVERRIDE {
102     if (!on_start_open_connection_.is_null())
103       on_start_open_connection_.Run();
104     return OK;
105   }
OnConnected(SocketStream * socket,int max_pending_send_allowed)106   virtual void OnConnected(SocketStream* socket,
107                            int max_pending_send_allowed) OVERRIDE {
108     if (!on_connected_.is_null())
109       on_connected_.Run();
110   }
OnSentData(SocketStream * socket,int amount_sent)111   virtual void OnSentData(SocketStream* socket,
112                           int amount_sent) OVERRIDE {
113     amount_sent_ += amount_sent;
114     if (!on_sent_data_.is_null())
115       on_sent_data_.Run();
116   }
OnReceivedData(SocketStream * socket,const char * data,int len)117   virtual void OnReceivedData(SocketStream* socket,
118                               const char* data, int len) OVERRIDE {
119     received_data_ += std::string(data, len);
120     if (!on_received_data_.is_null())
121       on_received_data_.Run();
122   }
OnClose(SocketStream * socket)123   virtual void OnClose(SocketStream* socket) OVERRIDE {
124     if (!on_close_.is_null())
125       on_close_.Run();
126   }
CanGetCookies(SocketStream * socket,const GURL & url)127   virtual bool CanGetCookies(SocketStream* socket,
128                              const GURL& url) OVERRIDE {
129     return allow_all_cookies_;
130   }
CanSetCookie(SocketStream * request,const GURL & url,const std::string & cookie_line,CookieOptions * options)131   virtual bool CanSetCookie(SocketStream* request,
132                             const GURL& url,
133                             const std::string& cookie_line,
134                             CookieOptions* options) OVERRIDE {
135     return allow_all_cookies_;
136   }
137 
amount_sent() const138   size_t amount_sent() const { return amount_sent_; }
received_data() const139   const std::string& received_data() const { return received_data_; }
140 
141  private:
142   int amount_sent_;
143   bool allow_all_cookies_;
144   std::string received_data_;
145   base::Closure on_start_open_connection_;
146   base::Closure on_connected_;
147   base::Closure on_sent_data_;
148   base::Closure on_received_data_;
149   base::Closure on_close_;
150 };
151 
152 class MockCookieStore : public CookieStore {
153  public:
154   struct Entry {
155     GURL url;
156     std::string cookie_line;
157     CookieOptions options;
158   };
159 
MockCookieStore()160   MockCookieStore() {}
161 
SetCookieWithOptions(const GURL & url,const std::string & cookie_line,const CookieOptions & options)162   bool SetCookieWithOptions(const GURL& url,
163                             const std::string& cookie_line,
164                             const CookieOptions& options) {
165     Entry entry;
166     entry.url = url;
167     entry.cookie_line = cookie_line;
168     entry.options = options;
169     entries_.push_back(entry);
170     return true;
171   }
172 
GetCookiesWithOptions(const GURL & url,const CookieOptions & options)173   std::string GetCookiesWithOptions(const GURL& url,
174                                     const CookieOptions& options) {
175     std::string result;
176     for (size_t i = 0; i < entries_.size(); i++) {
177       Entry& entry = entries_[i];
178       if (url == entry.url) {
179         if (!result.empty()) {
180           result += "; ";
181         }
182         result += entry.cookie_line;
183       }
184     }
185     return result;
186   }
187 
188   // CookieStore:
SetCookieWithOptionsAsync(const GURL & url,const std::string & cookie_line,const CookieOptions & options,const SetCookiesCallback & callback)189   virtual void SetCookieWithOptionsAsync(
190       const GURL& url,
191       const std::string& cookie_line,
192       const CookieOptions& options,
193       const SetCookiesCallback& callback) OVERRIDE {
194     bool result = SetCookieWithOptions(url, cookie_line, options);
195     if (!callback.is_null())
196       callback.Run(result);
197   }
198 
GetCookiesWithOptionsAsync(const GURL & url,const CookieOptions & options,const GetCookiesCallback & callback)199   virtual void GetCookiesWithOptionsAsync(
200       const GURL& url,
201       const CookieOptions& options,
202       const GetCookiesCallback& callback) OVERRIDE {
203     if (!callback.is_null())
204       callback.Run(GetCookiesWithOptions(url, options));
205   }
206 
GetAllCookiesForURLAsync(const GURL & url,const GetCookieListCallback & callback)207   virtual void GetAllCookiesForURLAsync(
208       const GURL& url,
209       const GetCookieListCallback& callback) OVERRIDE {
210     ADD_FAILURE();
211   }
212 
DeleteCookieAsync(const GURL & url,const std::string & cookie_name,const base::Closure & callback)213   virtual void DeleteCookieAsync(const GURL& url,
214                                  const std::string& cookie_name,
215                                  const base::Closure& callback) OVERRIDE {
216     ADD_FAILURE();
217   }
218 
DeleteAllCreatedBetweenAsync(const base::Time & delete_begin,const base::Time & delete_end,const DeleteCallback & callback)219   virtual void DeleteAllCreatedBetweenAsync(
220       const base::Time& delete_begin,
221       const base::Time& delete_end,
222       const DeleteCallback& callback) OVERRIDE {
223     ADD_FAILURE();
224   }
225 
DeleteAllCreatedBetweenForHostAsync(const base::Time delete_begin,const base::Time delete_end,const GURL & url,const DeleteCallback & callback)226   virtual void DeleteAllCreatedBetweenForHostAsync(
227       const base::Time delete_begin,
228       const base::Time delete_end,
229       const GURL& url,
230       const DeleteCallback& callback) OVERRIDE {
231     ADD_FAILURE();
232   }
233 
DeleteSessionCookiesAsync(const DeleteCallback &)234   virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE {
235     ADD_FAILURE();
236   }
237 
GetCookieMonster()238   virtual CookieMonster* GetCookieMonster() OVERRIDE { return NULL; }
239 
entries() const240   const std::vector<Entry>& entries() const { return entries_; }
241 
242  private:
243   friend class base::RefCountedThreadSafe<MockCookieStore>;
~MockCookieStore()244   virtual ~MockCookieStore() {}
245 
246   std::vector<Entry> entries_;
247 };
248 
249 class MockSSLConfigService : public SSLConfigService {
250  public:
GetSSLConfig(SSLConfig * config)251   virtual void GetSSLConfig(SSLConfig* config) OVERRIDE {}
252 
253  protected:
~MockSSLConfigService()254   virtual ~MockSSLConfigService() {}
255 };
256 
257 class MockURLRequestContext : public URLRequestContext {
258  public:
MockURLRequestContext(CookieStore * cookie_store)259   explicit MockURLRequestContext(CookieStore* cookie_store)
260       : transport_security_state_() {
261     set_cookie_store(cookie_store);
262     set_transport_security_state(&transport_security_state_);
263     base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
264     bool include_subdomains = false;
265     transport_security_state_.AddHSTS("upgrademe.com", expiry,
266                                       include_subdomains);
267   }
268 
~MockURLRequestContext()269   virtual ~MockURLRequestContext() {}
270 
271  private:
272   TransportSecurityState transport_security_state_;
273 };
274 
275 class MockHttpTransactionFactory : public HttpTransactionFactory {
276  public:
MockHttpTransactionFactory(NextProto next_proto,OrderedSocketData * data,bool enable_websocket_over_spdy)277   MockHttpTransactionFactory(NextProto next_proto,
278                              OrderedSocketData* data,
279                              bool enable_websocket_over_spdy) {
280     data_ = data;
281     MockConnect connect_data(SYNCHRONOUS, OK);
282     data_->set_connect_data(connect_data);
283     session_deps_.reset(new SpdySessionDependencies(next_proto));
284     session_deps_->enable_websocket_over_spdy = enable_websocket_over_spdy;
285     session_deps_->socket_factory->AddSocketDataProvider(data_);
286     http_session_ =
287         SpdySessionDependencies::SpdyCreateSession(session_deps_.get());
288     host_port_pair_.set_host("example.com");
289     host_port_pair_.set_port(80);
290     spdy_session_key_ = SpdySessionKey(host_port_pair_,
291                                             ProxyServer::Direct(),
292                                             PRIVACY_MODE_DISABLED);
293     session_ = CreateInsecureSpdySession(
294         http_session_, spdy_session_key_, BoundNetLog());
295   }
296 
CreateTransaction(RequestPriority priority,scoped_ptr<HttpTransaction> * trans)297   virtual int CreateTransaction(
298       RequestPriority priority,
299       scoped_ptr<HttpTransaction>* trans) OVERRIDE {
300     NOTREACHED();
301     return ERR_UNEXPECTED;
302   }
303 
GetCache()304   virtual HttpCache* GetCache() OVERRIDE {
305     NOTREACHED();
306     return NULL;
307   }
308 
GetSession()309   virtual HttpNetworkSession* GetSession() OVERRIDE {
310     return http_session_.get();
311   }
312 
313  private:
314   OrderedSocketData* data_;
315   scoped_ptr<SpdySessionDependencies> session_deps_;
316   scoped_refptr<HttpNetworkSession> http_session_;
317   base::WeakPtr<SpdySession> session_;
318   HostPortPair host_port_pair_;
319   SpdySessionKey spdy_session_key_;
320 };
321 
322 class DeletingSocketStreamDelegate : public SocketStream::Delegate {
323  public:
DeletingSocketStreamDelegate()324   DeletingSocketStreamDelegate()
325       : delete_next_(false) {}
326 
327   // Since this class needs to be able to delete |job_|, it must be the only
328   // reference holder (except for temporary references). Provide access to the
329   // pointer for tests to use.
job()330   WebSocketJob* job() { return job_.get(); }
331 
set_job(WebSocketJob * job)332   void set_job(WebSocketJob* job) { job_ = job; }
333 
334   // After calling this, the next call to a method on this delegate will delete
335   // the WebSocketJob object.
set_delete_next(bool delete_next)336   void set_delete_next(bool delete_next) { delete_next_ = delete_next; }
337 
DeleteJobMaybe()338   void DeleteJobMaybe() {
339     if (delete_next_) {
340       job_->DetachContext();
341       job_->DetachDelegate();
342       job_ = NULL;
343     }
344   }
345 
346   // SocketStream::Delegate implementation
347 
348   // OnStartOpenConnection() is not implemented by SocketStreamDispatcherHost
349 
OnConnected(SocketStream * socket,int max_pending_send_allowed)350   virtual void OnConnected(SocketStream* socket,
351                            int max_pending_send_allowed) OVERRIDE {
352     DeleteJobMaybe();
353   }
354 
OnSentData(SocketStream * socket,int amount_sent)355   virtual void OnSentData(SocketStream* socket, int amount_sent) OVERRIDE {
356     DeleteJobMaybe();
357   }
358 
OnReceivedData(SocketStream * socket,const char * data,int len)359   virtual void OnReceivedData(SocketStream* socket,
360                               const char* data,
361                               int len) OVERRIDE {
362     DeleteJobMaybe();
363   }
364 
OnClose(SocketStream * socket)365   virtual void OnClose(SocketStream* socket) OVERRIDE { DeleteJobMaybe(); }
366 
OnAuthRequired(SocketStream * socket,AuthChallengeInfo * auth_info)367   virtual void OnAuthRequired(SocketStream* socket,
368                               AuthChallengeInfo* auth_info) OVERRIDE {
369     DeleteJobMaybe();
370   }
371 
OnSSLCertificateError(SocketStream * socket,const SSLInfo & ssl_info,bool fatal)372   virtual void OnSSLCertificateError(SocketStream* socket,
373                                      const SSLInfo& ssl_info,
374                                      bool fatal) OVERRIDE {
375     DeleteJobMaybe();
376   }
377 
OnError(const SocketStream * socket,int error)378   virtual void OnError(const SocketStream* socket, int error) OVERRIDE {
379     DeleteJobMaybe();
380   }
381 
382   // CanGetCookies() and CanSetCookies() do not appear to be able to delete the
383   // WebSocketJob object.
384 
385  private:
386   scoped_refptr<WebSocketJob> job_;
387   bool delete_next_;
388 };
389 
390 }  // namespace
391 
392 class WebSocketJobTest : public PlatformTest,
393                          public ::testing::WithParamInterface<NextProto> {
394  public:
WebSocketJobTest()395   WebSocketJobTest()
396       : spdy_util_(GetParam()),
397         enable_websocket_over_spdy_(false) {}
398 
SetUp()399   virtual void SetUp() OVERRIDE {
400     stream_type_ = STREAM_INVALID;
401     cookie_store_ = new MockCookieStore;
402     context_.reset(new MockURLRequestContext(cookie_store_.get()));
403   }
TearDown()404   virtual void TearDown() OVERRIDE {
405     cookie_store_ = NULL;
406     context_.reset();
407     websocket_ = NULL;
408     socket_ = NULL;
409   }
DoSendRequest()410   void DoSendRequest() {
411     EXPECT_TRUE(websocket_->SendData(kHandshakeRequestWithoutCookie,
412                                      kHandshakeRequestWithoutCookieLength));
413   }
DoSendData()414   void DoSendData() {
415     if (received_data().size() == kHandshakeResponseWithoutCookieLength)
416       websocket_->SendData(kDataHello, kDataHelloLength);
417   }
DoSync()418   void DoSync() {
419     sync_test_callback_.callback().Run(OK);
420   }
WaitForResult()421   int WaitForResult() {
422     return sync_test_callback_.WaitForResult();
423   }
424 
425  protected:
426   enum StreamType {
427     STREAM_INVALID,
428     STREAM_MOCK_SOCKET,
429     STREAM_SOCKET,
430     STREAM_SPDY_WEBSOCKET,
431   };
432   enum ThrottlingOption {
433     THROTTLING_OFF,
434     THROTTLING_ON,
435   };
436   enum SpdyOption {
437     SPDY_OFF,
438     SPDY_ON,
439   };
InitWebSocketJob(const GURL & url,MockSocketStreamDelegate * delegate,StreamType stream_type)440   void InitWebSocketJob(const GURL& url,
441                         MockSocketStreamDelegate* delegate,
442                         StreamType stream_type) {
443     DCHECK_NE(STREAM_INVALID, stream_type);
444     stream_type_ = stream_type;
445     websocket_ = new WebSocketJob(delegate);
446 
447     if (stream_type == STREAM_MOCK_SOCKET)
448       socket_ = new MockSocketStream(url, websocket_.get(), context_.get(),
449                                      NULL);
450 
451     if (stream_type == STREAM_SOCKET || stream_type == STREAM_SPDY_WEBSOCKET) {
452       if (stream_type == STREAM_SPDY_WEBSOCKET) {
453         http_factory_.reset(new MockHttpTransactionFactory(
454             GetParam(), data_.get(), enable_websocket_over_spdy_));
455         context_->set_http_transaction_factory(http_factory_.get());
456       }
457 
458       ssl_config_service_ = new MockSSLConfigService();
459       context_->set_ssl_config_service(ssl_config_service_.get());
460       proxy_service_.reset(ProxyService::CreateDirect());
461       context_->set_proxy_service(proxy_service_.get());
462       host_resolver_.reset(new MockHostResolver);
463       context_->set_host_resolver(host_resolver_.get());
464 
465       socket_ = new SocketStream(url, websocket_.get(), context_.get(), NULL);
466       socket_factory_.reset(new MockClientSocketFactory);
467       DCHECK(data_.get());
468       socket_factory_->AddSocketDataProvider(data_.get());
469       socket_->SetClientSocketFactory(socket_factory_.get());
470     }
471 
472     websocket_->InitSocketStream(socket_.get());
473     // MockHostResolver resolves all hosts to 127.0.0.1; however, when we create
474     // a WebSocketJob purely to block another one in a throttling test, we don't
475     // perform a real connect. In that case, the following address is used
476     // instead.
477     IPAddressNumber ip;
478     ParseIPLiteralToNumber("127.0.0.1", &ip);
479     websocket_->addresses_ = AddressList::CreateFromIPAddress(ip, 80);
480   }
SkipToConnecting()481   void SkipToConnecting() {
482     websocket_->state_ = WebSocketJob::CONNECTING;
483     ASSERT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(websocket_.get()));
484   }
GetWebSocketJobState()485   WebSocketJob::State GetWebSocketJobState() {
486     return websocket_->state_;
487   }
CloseWebSocketJob()488   void CloseWebSocketJob() {
489     if (websocket_->socket_.get()) {
490       websocket_->socket_->DetachDelegate();
491       WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_.get());
492     }
493     websocket_->state_ = WebSocketJob::CLOSED;
494     websocket_->delegate_ = NULL;
495     websocket_->socket_ = NULL;
496   }
GetSocket(SocketStreamJob * job)497   SocketStream* GetSocket(SocketStreamJob* job) {
498     return job->socket_.get();
499   }
sent_data() const500   const std::string& sent_data() const {
501     DCHECK_EQ(STREAM_MOCK_SOCKET, stream_type_);
502     MockSocketStream* socket =
503         static_cast<MockSocketStream*>(socket_.get());
504     DCHECK(socket);
505     return socket->sent_data();
506   }
received_data() const507   const std::string& received_data() const {
508     DCHECK_NE(STREAM_INVALID, stream_type_);
509     MockSocketStreamDelegate* delegate =
510         static_cast<MockSocketStreamDelegate*>(websocket_->delegate_);
511     DCHECK(delegate);
512     return delegate->received_data();
513   }
514 
515   void TestSimpleHandshake();
516   void TestSlowHandshake();
517   void TestHandshakeWithCookie();
518   void TestHandshakeWithCookieButNotAllowed();
519   void TestHSTSUpgrade();
520   void TestInvalidSendData();
521   void TestConnectByWebSocket(ThrottlingOption throttling);
522   void TestConnectBySpdy(SpdyOption spdy, ThrottlingOption throttling);
523   void TestThrottlingLimit();
524 
525   SpdyWebSocketTestUtil spdy_util_;
526   StreamType stream_type_;
527   scoped_refptr<MockCookieStore> cookie_store_;
528   scoped_ptr<MockURLRequestContext> context_;
529   scoped_refptr<WebSocketJob> websocket_;
530   scoped_refptr<SocketStream> socket_;
531   scoped_ptr<MockClientSocketFactory> socket_factory_;
532   scoped_ptr<OrderedSocketData> data_;
533   TestCompletionCallback sync_test_callback_;
534   scoped_refptr<MockSSLConfigService> ssl_config_service_;
535   scoped_ptr<ProxyService> proxy_service_;
536   scoped_ptr<MockHostResolver> host_resolver_;
537   scoped_ptr<MockHttpTransactionFactory> http_factory_;
538 
539   // Must be set before call to enable_websocket_over_spdy, defaults to false.
540   bool enable_websocket_over_spdy_;
541 
542   static const char kHandshakeRequestWithoutCookie[];
543   static const char kHandshakeRequestWithCookie[];
544   static const char kHandshakeRequestWithFilteredCookie[];
545   static const char kHandshakeResponseWithoutCookie[];
546   static const char kHandshakeResponseWithCookie[];
547   static const char kDataHello[];
548   static const char kDataWorld[];
549   static const char* const kHandshakeRequestForSpdy[];
550   static const char* const kHandshakeResponseForSpdy[];
551   static const size_t kHandshakeRequestWithoutCookieLength;
552   static const size_t kHandshakeRequestWithCookieLength;
553   static const size_t kHandshakeRequestWithFilteredCookieLength;
554   static const size_t kHandshakeResponseWithoutCookieLength;
555   static const size_t kHandshakeResponseWithCookieLength;
556   static const size_t kDataHelloLength;
557   static const size_t kDataWorldLength;
558 };
559 
560 // Tests using this fixture verify that the WebSocketJob can handle being
561 // deleted while calling back to the delegate correctly. These tests need to be
562 // run under AddressSanitizer or other systems for detecting use-after-free
563 // errors in order to find problems.
564 class WebSocketJobDeleteTest : public ::testing::Test {
565  protected:
WebSocketJobDeleteTest()566   WebSocketJobDeleteTest()
567       : delegate_(new DeletingSocketStreamDelegate),
568         cookie_store_(new MockCookieStore),
569         context_(new MockURLRequestContext(cookie_store_.get())) {
570     WebSocketJob* websocket = new WebSocketJob(delegate_.get());
571     delegate_->set_job(websocket);
572 
573     socket_ = new MockSocketStream(
574         GURL("ws://127.0.0.1/"), websocket, context_.get(), NULL);
575 
576     websocket->InitSocketStream(socket_.get());
577   }
578 
SetDeleteNext()579   void SetDeleteNext() { return delegate_->set_delete_next(true); }
job()580   WebSocketJob* job() { return delegate_->job(); }
581 
582   scoped_ptr<DeletingSocketStreamDelegate> delegate_;
583   scoped_refptr<MockCookieStore> cookie_store_;
584   scoped_ptr<MockURLRequestContext> context_;
585   scoped_refptr<SocketStream> socket_;
586 };
587 
588 const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] =
589     "GET /demo HTTP/1.1\r\n"
590     "Host: example.com\r\n"
591     "Upgrade: WebSocket\r\n"
592     "Connection: Upgrade\r\n"
593     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
594     "Origin: http://example.com\r\n"
595     "Sec-WebSocket-Protocol: sample\r\n"
596     "Sec-WebSocket-Version: 13\r\n"
597     "\r\n";
598 
599 const char WebSocketJobTest::kHandshakeRequestWithCookie[] =
600     "GET /demo HTTP/1.1\r\n"
601     "Host: example.com\r\n"
602     "Upgrade: WebSocket\r\n"
603     "Connection: Upgrade\r\n"
604     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
605     "Origin: http://example.com\r\n"
606     "Sec-WebSocket-Protocol: sample\r\n"
607     "Sec-WebSocket-Version: 13\r\n"
608     "Cookie: WK-test=1\r\n"
609     "\r\n";
610 
611 const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie[] =
612     "GET /demo HTTP/1.1\r\n"
613     "Host: example.com\r\n"
614     "Upgrade: WebSocket\r\n"
615     "Connection: Upgrade\r\n"
616     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
617     "Origin: http://example.com\r\n"
618     "Sec-WebSocket-Protocol: sample\r\n"
619     "Sec-WebSocket-Version: 13\r\n"
620     "Cookie: CR-test=1; CR-test-httponly=1\r\n"
621     "\r\n";
622 
623 const char WebSocketJobTest::kHandshakeResponseWithoutCookie[] =
624     "HTTP/1.1 101 Switching Protocols\r\n"
625     "Upgrade: websocket\r\n"
626     "Connection: Upgrade\r\n"
627     "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
628     "Sec-WebSocket-Protocol: sample\r\n"
629     "\r\n";
630 
631 const char WebSocketJobTest::kHandshakeResponseWithCookie[] =
632     "HTTP/1.1 101 Switching Protocols\r\n"
633     "Upgrade: websocket\r\n"
634     "Connection: Upgrade\r\n"
635     "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
636     "Sec-WebSocket-Protocol: sample\r\n"
637     "Set-Cookie: CR-set-test=1\r\n"
638     "\r\n";
639 
640 const char WebSocketJobTest::kDataHello[] = "Hello, ";
641 
642 const char WebSocketJobTest::kDataWorld[] = "World!\n";
643 
644 const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength =
645     arraysize(kHandshakeRequestWithoutCookie) - 1;
646 const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength =
647     arraysize(kHandshakeRequestWithCookie) - 1;
648 const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength =
649     arraysize(kHandshakeRequestWithFilteredCookie) - 1;
650 const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength =
651     arraysize(kHandshakeResponseWithoutCookie) - 1;
652 const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength =
653     arraysize(kHandshakeResponseWithCookie) - 1;
654 const size_t WebSocketJobTest::kDataHelloLength =
655     arraysize(kDataHello) - 1;
656 const size_t WebSocketJobTest::kDataWorldLength =
657     arraysize(kDataWorld) - 1;
658 
TestSimpleHandshake()659 void WebSocketJobTest::TestSimpleHandshake() {
660   GURL url("ws://example.com/demo");
661   MockSocketStreamDelegate delegate;
662   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
663   SkipToConnecting();
664 
665   DoSendRequest();
666   base::MessageLoop::current()->RunUntilIdle();
667   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
668   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
669   websocket_->OnSentData(socket_.get(),
670                          kHandshakeRequestWithoutCookieLength);
671   EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
672 
673   websocket_->OnReceivedData(socket_.get(),
674                              kHandshakeResponseWithoutCookie,
675                              kHandshakeResponseWithoutCookieLength);
676   base::MessageLoop::current()->RunUntilIdle();
677   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
678   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
679   CloseWebSocketJob();
680 }
681 
TestSlowHandshake()682 void WebSocketJobTest::TestSlowHandshake() {
683   GURL url("ws://example.com/demo");
684   MockSocketStreamDelegate delegate;
685   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
686   SkipToConnecting();
687 
688   DoSendRequest();
689   // We assume request is sent in one data chunk (from WebKit)
690   // We don't support streaming request.
691   base::MessageLoop::current()->RunUntilIdle();
692   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
693   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
694   websocket_->OnSentData(socket_.get(),
695                          kHandshakeRequestWithoutCookieLength);
696   EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
697 
698   std::vector<std::string> lines;
699   base::SplitString(kHandshakeResponseWithoutCookie, '\n', &lines);
700   for (size_t i = 0; i < lines.size() - 2; i++) {
701     std::string line = lines[i] + "\r\n";
702     SCOPED_TRACE("Line: " + line);
703     websocket_->OnReceivedData(socket_.get(), line.c_str(), line.size());
704     base::MessageLoop::current()->RunUntilIdle();
705     EXPECT_TRUE(delegate.received_data().empty());
706     EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
707   }
708   websocket_->OnReceivedData(socket_.get(), "\r\n", 2);
709   base::MessageLoop::current()->RunUntilIdle();
710   EXPECT_FALSE(delegate.received_data().empty());
711   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
712   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
713   CloseWebSocketJob();
714 }
715 
716 INSTANTIATE_TEST_CASE_P(
717     NextProto,
718     WebSocketJobTest,
719     testing::Values(kProtoDeprecatedSPDY2,
720                     kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
721 
TEST_P(WebSocketJobTest,DelayedCookies)722 TEST_P(WebSocketJobTest, DelayedCookies) {
723   enable_websocket_over_spdy_ = true;
724   GURL url("ws://example.com/demo");
725   GURL cookieUrl("http://example.com/demo");
726   CookieOptions cookie_options;
727   scoped_refptr<DelayedCookieMonster> cookie_store = new DelayedCookieMonster();
728   context_->set_cookie_store(cookie_store.get());
729   cookie_store->SetCookieWithOptionsAsync(cookieUrl,
730                                           "CR-test=1",
731                                           cookie_options,
732                                           CookieMonster::SetCookiesCallback());
733   cookie_options.set_include_httponly();
734   cookie_store->SetCookieWithOptionsAsync(
735       cookieUrl, "CR-test-httponly=1", cookie_options,
736       CookieMonster::SetCookiesCallback());
737 
738   MockSocketStreamDelegate delegate;
739   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
740   SkipToConnecting();
741 
742   bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
743                                    kHandshakeRequestWithCookieLength);
744   EXPECT_TRUE(sent);
745   base::MessageLoop::current()->RunUntilIdle();
746   EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data());
747   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
748   websocket_->OnSentData(socket_.get(),
749                          kHandshakeRequestWithFilteredCookieLength);
750   EXPECT_EQ(kHandshakeRequestWithCookieLength,
751             delegate.amount_sent());
752 
753   websocket_->OnReceivedData(socket_.get(),
754                              kHandshakeResponseWithCookie,
755                              kHandshakeResponseWithCookieLength);
756   base::MessageLoop::current()->RunUntilIdle();
757   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
758   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
759 
760   CloseWebSocketJob();
761 }
762 
TestHandshakeWithCookie()763 void WebSocketJobTest::TestHandshakeWithCookie() {
764   GURL url("ws://example.com/demo");
765   GURL cookieUrl("http://example.com/demo");
766   CookieOptions cookie_options;
767   cookie_store_->SetCookieWithOptions(
768       cookieUrl, "CR-test=1", cookie_options);
769   cookie_options.set_include_httponly();
770   cookie_store_->SetCookieWithOptions(
771       cookieUrl, "CR-test-httponly=1", cookie_options);
772 
773   MockSocketStreamDelegate delegate;
774   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
775   SkipToConnecting();
776 
777   bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
778                                    kHandshakeRequestWithCookieLength);
779   EXPECT_TRUE(sent);
780   base::MessageLoop::current()->RunUntilIdle();
781   EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data());
782   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
783   websocket_->OnSentData(socket_.get(),
784                          kHandshakeRequestWithFilteredCookieLength);
785   EXPECT_EQ(kHandshakeRequestWithCookieLength,
786             delegate.amount_sent());
787 
788   websocket_->OnReceivedData(socket_.get(),
789                              kHandshakeResponseWithCookie,
790                              kHandshakeResponseWithCookieLength);
791   base::MessageLoop::current()->RunUntilIdle();
792   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
793   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
794 
795   EXPECT_EQ(3U, cookie_store_->entries().size());
796   EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
797   EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
798   EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
799   EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
800   EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url);
801   EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line);
802 
803   CloseWebSocketJob();
804 }
805 
TestHandshakeWithCookieButNotAllowed()806 void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() {
807   GURL url("ws://example.com/demo");
808   GURL cookieUrl("http://example.com/demo");
809   CookieOptions cookie_options;
810   cookie_store_->SetCookieWithOptions(
811       cookieUrl, "CR-test=1", cookie_options);
812   cookie_options.set_include_httponly();
813   cookie_store_->SetCookieWithOptions(
814       cookieUrl, "CR-test-httponly=1", cookie_options);
815 
816   MockSocketStreamDelegate delegate;
817   delegate.set_allow_all_cookies(false);
818   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
819   SkipToConnecting();
820 
821   bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
822                                    kHandshakeRequestWithCookieLength);
823   EXPECT_TRUE(sent);
824   base::MessageLoop::current()->RunUntilIdle();
825   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
826   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
827   websocket_->OnSentData(socket_.get(), kHandshakeRequestWithoutCookieLength);
828   EXPECT_EQ(kHandshakeRequestWithCookieLength, delegate.amount_sent());
829 
830   websocket_->OnReceivedData(socket_.get(),
831                              kHandshakeResponseWithCookie,
832                              kHandshakeResponseWithCookieLength);
833   base::MessageLoop::current()->RunUntilIdle();
834   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
835   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
836 
837   EXPECT_EQ(2U, cookie_store_->entries().size());
838   EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
839   EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
840   EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
841   EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
842 
843   CloseWebSocketJob();
844 }
845 
TestHSTSUpgrade()846 void WebSocketJobTest::TestHSTSUpgrade() {
847   GURL url("ws://upgrademe.com/");
848   MockSocketStreamDelegate delegate;
849   scoped_refptr<SocketStreamJob> job =
850       SocketStreamJob::CreateSocketStreamJob(
851           url, &delegate, context_->transport_security_state(),
852           context_->ssl_config_service(), NULL, NULL);
853   EXPECT_TRUE(GetSocket(job.get())->is_secure());
854   job->DetachDelegate();
855 
856   url = GURL("ws://donotupgrademe.com/");
857   job = SocketStreamJob::CreateSocketStreamJob(
858       url, &delegate, context_->transport_security_state(),
859       context_->ssl_config_service(), NULL, NULL);
860   EXPECT_FALSE(GetSocket(job.get())->is_secure());
861   job->DetachDelegate();
862 }
863 
TestInvalidSendData()864 void WebSocketJobTest::TestInvalidSendData() {
865   GURL url("ws://example.com/demo");
866   MockSocketStreamDelegate delegate;
867   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
868   SkipToConnecting();
869 
870   DoSendRequest();
871   // We assume request is sent in one data chunk (from WebKit)
872   // We don't support streaming request.
873   base::MessageLoop::current()->RunUntilIdle();
874   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
875   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
876   websocket_->OnSentData(socket_.get(),
877                          kHandshakeRequestWithoutCookieLength);
878   EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
879 
880   // We could not send any data until connection is established.
881   bool sent = websocket_->SendData(kHandshakeRequestWithoutCookie,
882                                    kHandshakeRequestWithoutCookieLength);
883   EXPECT_FALSE(sent);
884   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
885   CloseWebSocketJob();
886 }
887 
888 // Following tests verify cooperation between WebSocketJob and SocketStream.
889 // Other former tests use MockSocketStream as SocketStream, so we could not
890 // check SocketStream behavior.
891 // OrderedSocketData provide socket level verifiation by checking out-going
892 // packets in comparison with the MockWrite array and emulating in-coming
893 // packets with MockRead array.
894 
TestConnectByWebSocket(ThrottlingOption throttling)895 void WebSocketJobTest::TestConnectByWebSocket(
896     ThrottlingOption throttling) {
897   // This is a test for verifying cooperation between WebSocketJob and
898   // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic
899   // situation. If |throttling| was |THROTTLING_ON|, throttling limits the
900   // latter connection.
901   MockWrite writes[] = {
902     MockWrite(ASYNC,
903               kHandshakeRequestWithoutCookie,
904               kHandshakeRequestWithoutCookieLength,
905               1),
906     MockWrite(ASYNC,
907               kDataHello,
908               kDataHelloLength,
909               3)
910   };
911   MockRead reads[] = {
912     MockRead(ASYNC,
913              kHandshakeResponseWithoutCookie,
914              kHandshakeResponseWithoutCookieLength,
915              2),
916     MockRead(ASYNC,
917              kDataWorld,
918              kDataWorldLength,
919              4),
920     MockRead(SYNCHRONOUS, 0, 5)  // EOF
921   };
922   data_.reset(new OrderedSocketData(
923       reads, arraysize(reads), writes, arraysize(writes)));
924 
925   GURL url("ws://example.com/demo");
926   MockSocketStreamDelegate delegate;
927   WebSocketJobTest* test = this;
928   if (throttling == THROTTLING_ON)
929     delegate.SetOnStartOpenConnection(
930         base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
931   delegate.SetOnConnected(
932       base::Bind(&WebSocketJobTest::DoSendRequest,
933                  base::Unretained(test)));
934   delegate.SetOnReceivedData(
935       base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test)));
936   delegate.SetOnClose(
937       base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
938   InitWebSocketJob(url, &delegate, STREAM_SOCKET);
939 
940   scoped_refptr<WebSocketJob> block_websocket;
941   if (throttling == THROTTLING_ON) {
942     // Create former WebSocket object which obstructs the latter one.
943     block_websocket = new WebSocketJob(NULL);
944     block_websocket->addresses_ = AddressList(websocket_->address_list());
945     ASSERT_TRUE(
946         WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get()));
947   }
948 
949   websocket_->Connect();
950 
951   if (throttling == THROTTLING_ON) {
952     EXPECT_EQ(OK, WaitForResult());
953     EXPECT_TRUE(websocket_->IsWaiting());
954 
955     // Remove the former WebSocket object from throttling queue to unblock the
956     // latter.
957     block_websocket->state_ = WebSocketJob::CLOSED;
958     WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get());
959     block_websocket = NULL;
960   }
961 
962   EXPECT_EQ(OK, WaitForResult());
963   EXPECT_TRUE(data_->at_read_eof());
964   EXPECT_TRUE(data_->at_write_eof());
965   EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState());
966 }
967 
TestConnectBySpdy(SpdyOption spdy,ThrottlingOption throttling)968 void WebSocketJobTest::TestConnectBySpdy(
969     SpdyOption spdy, ThrottlingOption throttling) {
970   // This is a test for verifying cooperation between WebSocketJob and
971   // SocketStream in the situation we have SPDY session to the server. If
972   // |throttling| was |THROTTLING_ON|, throttling limits the latter connection.
973   // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected
974   // results depend on its configuration.
975   MockWrite writes_websocket[] = {
976     MockWrite(ASYNC,
977               kHandshakeRequestWithoutCookie,
978               kHandshakeRequestWithoutCookieLength,
979               1),
980     MockWrite(ASYNC,
981               kDataHello,
982               kDataHelloLength,
983               3)
984   };
985   MockRead reads_websocket[] = {
986     MockRead(ASYNC,
987              kHandshakeResponseWithoutCookie,
988              kHandshakeResponseWithoutCookieLength,
989              2),
990     MockRead(ASYNC,
991              kDataWorld,
992              kDataWorldLength,
993              4),
994     MockRead(SYNCHRONOUS, 0, 5)  // EOF
995   };
996 
997   scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock());
998   spdy_util_.SetHeader("path", "/demo", request_headers.get());
999   spdy_util_.SetHeader("version", "WebSocket/13", request_headers.get());
1000   spdy_util_.SetHeader("scheme", "ws", request_headers.get());
1001   spdy_util_.SetHeader("host", "example.com", request_headers.get());
1002   spdy_util_.SetHeader("origin", "http://example.com", request_headers.get());
1003   spdy_util_.SetHeader("sec-websocket-protocol", "sample",
1004                        request_headers.get());
1005 
1006   scoped_ptr<SpdyHeaderBlock> response_headers(new SpdyHeaderBlock());
1007   spdy_util_.SetHeader("status", "101 Switching Protocols",
1008                        response_headers.get());
1009   spdy_util_.SetHeader("sec-websocket-protocol", "sample",
1010                        response_headers.get());
1011 
1012   const SpdyStreamId kStreamId = 1;
1013   scoped_ptr<SpdyFrame> request_frame(
1014       spdy_util_.ConstructSpdyWebSocketHandshakeRequestFrame(
1015           request_headers.Pass(),
1016           kStreamId,
1017           MEDIUM));
1018   scoped_ptr<SpdyFrame> response_frame(
1019       spdy_util_.ConstructSpdyWebSocketHandshakeResponseFrame(
1020           response_headers.Pass(),
1021           kStreamId,
1022           MEDIUM));
1023   scoped_ptr<SpdyFrame> data_hello_frame(
1024       spdy_util_.ConstructSpdyWebSocketDataFrame(
1025           kDataHello,
1026           kDataHelloLength,
1027           kStreamId,
1028           false));
1029   scoped_ptr<SpdyFrame> data_world_frame(
1030       spdy_util_.ConstructSpdyWebSocketDataFrame(
1031           kDataWorld,
1032           kDataWorldLength,
1033           kStreamId,
1034           false));
1035   MockWrite writes_spdy[] = {
1036     CreateMockWrite(*request_frame.get(), 1),
1037     CreateMockWrite(*data_hello_frame.get(), 3),
1038   };
1039   MockRead reads_spdy[] = {
1040     CreateMockRead(*response_frame.get(), 2),
1041     CreateMockRead(*data_world_frame.get(), 4),
1042     MockRead(SYNCHRONOUS, 0, 5)  // EOF
1043   };
1044 
1045   if (spdy == SPDY_ON)
1046     data_.reset(new OrderedSocketData(
1047         reads_spdy, arraysize(reads_spdy),
1048         writes_spdy, arraysize(writes_spdy)));
1049   else
1050     data_.reset(new OrderedSocketData(
1051         reads_websocket, arraysize(reads_websocket),
1052         writes_websocket, arraysize(writes_websocket)));
1053 
1054   GURL url("ws://example.com/demo");
1055   MockSocketStreamDelegate delegate;
1056   WebSocketJobTest* test = this;
1057   if (throttling == THROTTLING_ON)
1058     delegate.SetOnStartOpenConnection(
1059         base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
1060   delegate.SetOnConnected(
1061       base::Bind(&WebSocketJobTest::DoSendRequest,
1062                  base::Unretained(test)));
1063   delegate.SetOnReceivedData(
1064       base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test)));
1065   delegate.SetOnClose(
1066       base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
1067   InitWebSocketJob(url, &delegate, STREAM_SPDY_WEBSOCKET);
1068 
1069   scoped_refptr<WebSocketJob> block_websocket;
1070   if (throttling == THROTTLING_ON) {
1071     // Create former WebSocket object which obstructs the latter one.
1072     block_websocket = new WebSocketJob(NULL);
1073     block_websocket->addresses_ = AddressList(websocket_->address_list());
1074     ASSERT_TRUE(
1075         WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get()));
1076   }
1077 
1078   websocket_->Connect();
1079 
1080   if (throttling == THROTTLING_ON) {
1081     EXPECT_EQ(OK, WaitForResult());
1082     EXPECT_TRUE(websocket_->IsWaiting());
1083 
1084     // Remove the former WebSocket object from throttling queue to unblock the
1085     // latter.
1086     block_websocket->state_ = WebSocketJob::CLOSED;
1087     WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get());
1088     block_websocket = NULL;
1089   }
1090 
1091   EXPECT_EQ(OK, WaitForResult());
1092   EXPECT_TRUE(data_->at_read_eof());
1093   EXPECT_TRUE(data_->at_write_eof());
1094   EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState());
1095 }
1096 
TestThrottlingLimit()1097 void WebSocketJobTest::TestThrottlingLimit() {
1098   std::vector<scoped_refptr<WebSocketJob> > jobs;
1099   const int kMaxWebSocketJobsThrottled = 1024;
1100   IPAddressNumber ip;
1101   ParseIPLiteralToNumber("127.0.0.1", &ip);
1102   for (int i = 0; i < kMaxWebSocketJobsThrottled + 1; ++i) {
1103     scoped_refptr<WebSocketJob> job = new WebSocketJob(NULL);
1104     job->addresses_ = AddressList(AddressList::CreateFromIPAddress(ip, 80));
1105     if (i >= kMaxWebSocketJobsThrottled)
1106       EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job));
1107     else
1108       EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job));
1109     jobs.push_back(job);
1110   }
1111 
1112   // Close the jobs in reverse order. Otherwise, We need to make them prepared
1113   // for Wakeup call.
1114   for (std::vector<scoped_refptr<WebSocketJob> >::reverse_iterator iter =
1115            jobs.rbegin();
1116        iter != jobs.rend();
1117        ++iter) {
1118     WebSocketJob* job = (*iter).get();
1119     job->state_ = WebSocketJob::CLOSED;
1120     WebSocketThrottle::GetInstance()->RemoveFromQueue(job);
1121   }
1122 }
1123 
1124 // Execute tests in both spdy-disabled mode and spdy-enabled mode.
TEST_P(WebSocketJobTest,SimpleHandshake)1125 TEST_P(WebSocketJobTest, SimpleHandshake) {
1126   TestSimpleHandshake();
1127 }
1128 
TEST_P(WebSocketJobTest,SlowHandshake)1129 TEST_P(WebSocketJobTest, SlowHandshake) {
1130   TestSlowHandshake();
1131 }
1132 
TEST_P(WebSocketJobTest,HandshakeWithCookie)1133 TEST_P(WebSocketJobTest, HandshakeWithCookie) {
1134   TestHandshakeWithCookie();
1135 }
1136 
TEST_P(WebSocketJobTest,HandshakeWithCookieButNotAllowed)1137 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) {
1138   TestHandshakeWithCookieButNotAllowed();
1139 }
1140 
TEST_P(WebSocketJobTest,HSTSUpgrade)1141 TEST_P(WebSocketJobTest, HSTSUpgrade) {
1142   TestHSTSUpgrade();
1143 }
1144 
TEST_P(WebSocketJobTest,InvalidSendData)1145 TEST_P(WebSocketJobTest, InvalidSendData) {
1146   TestInvalidSendData();
1147 }
1148 
TEST_P(WebSocketJobTest,SimpleHandshakeSpdyEnabled)1149 TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) {
1150   enable_websocket_over_spdy_ = true;
1151   TestSimpleHandshake();
1152 }
1153 
TEST_P(WebSocketJobTest,SlowHandshakeSpdyEnabled)1154 TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) {
1155   enable_websocket_over_spdy_ = true;
1156   TestSlowHandshake();
1157 }
1158 
TEST_P(WebSocketJobTest,HandshakeWithCookieSpdyEnabled)1159 TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) {
1160   enable_websocket_over_spdy_ = true;
1161   TestHandshakeWithCookie();
1162 }
1163 
TEST_P(WebSocketJobTest,HandshakeWithCookieButNotAllowedSpdyEnabled)1164 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) {
1165   enable_websocket_over_spdy_ = true;
1166   TestHandshakeWithCookieButNotAllowed();
1167 }
1168 
TEST_P(WebSocketJobTest,HSTSUpgradeSpdyEnabled)1169 TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) {
1170   enable_websocket_over_spdy_ = true;
1171   TestHSTSUpgrade();
1172 }
1173 
TEST_P(WebSocketJobTest,InvalidSendDataSpdyEnabled)1174 TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) {
1175   enable_websocket_over_spdy_ = true;
1176   TestInvalidSendData();
1177 }
1178 
TEST_P(WebSocketJobTest,ConnectByWebSocket)1179 TEST_P(WebSocketJobTest, ConnectByWebSocket) {
1180   enable_websocket_over_spdy_ = true;
1181   TestConnectByWebSocket(THROTTLING_OFF);
1182 }
1183 
TEST_P(WebSocketJobTest,ConnectByWebSocketSpdyEnabled)1184 TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) {
1185   enable_websocket_over_spdy_ = true;
1186   TestConnectByWebSocket(THROTTLING_OFF);
1187 }
1188 
TEST_P(WebSocketJobTest,ConnectBySpdy)1189 TEST_P(WebSocketJobTest, ConnectBySpdy) {
1190   TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF);
1191 }
1192 
TEST_P(WebSocketJobTest,ConnectBySpdySpdyEnabled)1193 TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) {
1194   enable_websocket_over_spdy_ = true;
1195   TestConnectBySpdy(SPDY_ON, THROTTLING_OFF);
1196 }
1197 
TEST_P(WebSocketJobTest,ThrottlingWebSocket)1198 TEST_P(WebSocketJobTest, ThrottlingWebSocket) {
1199   TestConnectByWebSocket(THROTTLING_ON);
1200 }
1201 
TEST_P(WebSocketJobTest,ThrottlingMaxNumberOfThrottledJobLimit)1202 TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) {
1203   TestThrottlingLimit();
1204 }
1205 
TEST_P(WebSocketJobTest,ThrottlingWebSocketSpdyEnabled)1206 TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) {
1207   enable_websocket_over_spdy_ = true;
1208   TestConnectByWebSocket(THROTTLING_ON);
1209 }
1210 
TEST_P(WebSocketJobTest,ThrottlingSpdy)1211 TEST_P(WebSocketJobTest, ThrottlingSpdy) {
1212   TestConnectBySpdy(SPDY_OFF, THROTTLING_ON);
1213 }
1214 
TEST_P(WebSocketJobTest,ThrottlingSpdySpdyEnabled)1215 TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) {
1216   enable_websocket_over_spdy_ = true;
1217   TestConnectBySpdy(SPDY_ON, THROTTLING_ON);
1218 }
1219 
TEST_F(WebSocketJobDeleteTest,OnClose)1220 TEST_F(WebSocketJobDeleteTest, OnClose) {
1221   SetDeleteNext();
1222   job()->OnClose(socket_.get());
1223   // OnClose() sets WebSocketJob::_socket to NULL before we can detach it, so
1224   // socket_->delegate is still set at this point. Clear it to avoid hitting
1225   // DCHECK(!delegate_) in the SocketStream destructor. SocketStream::Finish()
1226   // is the only caller of this method in real code, and it also sets delegate_
1227   // to NULL.
1228   socket_->DetachDelegate();
1229   EXPECT_FALSE(job());
1230 }
1231 
TEST_F(WebSocketJobDeleteTest,OnAuthRequired)1232 TEST_F(WebSocketJobDeleteTest, OnAuthRequired) {
1233   SetDeleteNext();
1234   job()->OnAuthRequired(socket_.get(), NULL);
1235   EXPECT_FALSE(job());
1236 }
1237 
TEST_F(WebSocketJobDeleteTest,OnSSLCertificateError)1238 TEST_F(WebSocketJobDeleteTest, OnSSLCertificateError) {
1239   SSLInfo ssl_info;
1240   SetDeleteNext();
1241   job()->OnSSLCertificateError(socket_.get(), ssl_info, true);
1242   EXPECT_FALSE(job());
1243 }
1244 
TEST_F(WebSocketJobDeleteTest,OnError)1245 TEST_F(WebSocketJobDeleteTest, OnError) {
1246   SetDeleteNext();
1247   job()->OnError(socket_.get(), ERR_CONNECTION_RESET);
1248   EXPECT_FALSE(job());
1249 }
1250 
TEST_F(WebSocketJobDeleteTest,OnSentSpdyHeaders)1251 TEST_F(WebSocketJobDeleteTest, OnSentSpdyHeaders) {
1252   job()->Connect();
1253   SetDeleteNext();
1254   job()->OnSentSpdyHeaders();
1255   EXPECT_FALSE(job());
1256 }
1257 
TEST_F(WebSocketJobDeleteTest,OnSentHandshakeRequest)1258 TEST_F(WebSocketJobDeleteTest, OnSentHandshakeRequest) {
1259   static const char kMinimalRequest[] =
1260       "GET /demo HTTP/1.1\r\n"
1261       "Host: example.com\r\n"
1262       "Upgrade: WebSocket\r\n"
1263       "Connection: Upgrade\r\n"
1264       "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
1265       "Origin: http://example.com\r\n"
1266       "Sec-WebSocket-Version: 13\r\n"
1267       "\r\n";
1268   const size_t kMinimalRequestSize = arraysize(kMinimalRequest) - 1;
1269   job()->Connect();
1270   job()->SendData(kMinimalRequest, kMinimalRequestSize);
1271   SetDeleteNext();
1272   job()->OnSentData(socket_.get(), kMinimalRequestSize);
1273   EXPECT_FALSE(job());
1274 }
1275 
TEST_F(WebSocketJobDeleteTest,NotifyHeadersComplete)1276 TEST_F(WebSocketJobDeleteTest, NotifyHeadersComplete) {
1277   static const char kMinimalResponse[] =
1278       "HTTP/1.1 101 Switching Protocols\r\n"
1279       "Upgrade: websocket\r\n"
1280       "Connection: Upgrade\r\n"
1281       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1282       "\r\n";
1283   job()->Connect();
1284   SetDeleteNext();
1285   job()->OnReceivedData(
1286       socket_.get(), kMinimalResponse, arraysize(kMinimalResponse) - 1);
1287   EXPECT_FALSE(job());
1288 }
1289 
1290 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation.
1291 // TODO(toyoshim,yutak): Add tests to verify closing handshake.
1292 }  // namespace net
1293