e12ddae81c587432b4a6df41a6ce4ce75e9aebad
[platform/framework/web/crosswalk.git] / src / net / websockets / websocket_job_test.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/websockets/websocket_job.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "net/base/completion_callback.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/cookies/cookie_store.h"
20 #include "net/cookies/cookie_store_test_helpers.h"
21 #include "net/dns/mock_host_resolver.h"
22 #include "net/http/http_transaction_factory.h"
23 #include "net/http/transport_security_state.h"
24 #include "net/proxy/proxy_service.h"
25 #include "net/socket/next_proto.h"
26 #include "net/socket/socket_test_util.h"
27 #include "net/socket_stream/socket_stream.h"
28 #include "net/spdy/spdy_session.h"
29 #include "net/spdy/spdy_websocket_test_util.h"
30 #include "net/ssl/ssl_config_service.h"
31 #include "net/url_request/url_request_context.h"
32 #include "net/websockets/websocket_throttle.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "testing/platform_test.h"
36 #include "url/gurl.h"
37
38 namespace net {
39
40 namespace {
41
42 class MockSocketStream : public SocketStream {
43  public:
44   MockSocketStream(const GURL& url, SocketStream::Delegate* delegate,
45                    URLRequestContext* context, CookieStore* cookie_store)
46       : SocketStream(url, delegate, context, cookie_store) {}
47
48   virtual void Connect() OVERRIDE {}
49   virtual bool SendData(const char* data, int len) OVERRIDE {
50     sent_data_ += std::string(data, len);
51     return true;
52   }
53
54   virtual void Close() OVERRIDE {}
55   virtual void RestartWithAuth(
56       const AuthCredentials& credentials) OVERRIDE {
57   }
58
59   virtual void DetachDelegate() OVERRIDE {
60     delegate_ = NULL;
61   }
62
63   const std::string& sent_data() const {
64     return sent_data_;
65   }
66
67  protected:
68   virtual ~MockSocketStream() {}
69
70  private:
71   std::string sent_data_;
72 };
73
74 class MockSocketStreamDelegate : public SocketStream::Delegate {
75  public:
76   MockSocketStreamDelegate()
77       : amount_sent_(0), allow_all_cookies_(true) {}
78   void set_allow_all_cookies(bool allow_all_cookies) {
79     allow_all_cookies_ = allow_all_cookies;
80   }
81   virtual ~MockSocketStreamDelegate() {}
82
83   void SetOnStartOpenConnection(const base::Closure& callback) {
84     on_start_open_connection_ = callback;
85   }
86   void SetOnConnected(const base::Closure& callback) {
87     on_connected_ = callback;
88   }
89   void SetOnSentData(const base::Closure& callback) {
90     on_sent_data_ = callback;
91   }
92   void SetOnReceivedData(const base::Closure& callback) {
93     on_received_data_ = callback;
94   }
95   void SetOnClose(const base::Closure& callback) {
96     on_close_ = callback;
97   }
98
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   }
106   virtual void OnConnected(SocketStream* socket,
107                            int max_pending_send_allowed) OVERRIDE {
108     if (!on_connected_.is_null())
109       on_connected_.Run();
110   }
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   }
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   }
123   virtual void OnClose(SocketStream* socket) OVERRIDE {
124     if (!on_close_.is_null())
125       on_close_.Run();
126   }
127   virtual bool CanGetCookies(SocketStream* socket,
128                              const GURL& url) OVERRIDE {
129     return allow_all_cookies_;
130   }
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
138   size_t amount_sent() const { return amount_sent_; }
139   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
160   MockCookieStore() {}
161
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
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:
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
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
207   virtual void GetAllCookiesForURLAsync(
208       const GURL& url,
209       const GetCookieListCallback& callback) OVERRIDE {
210     ADD_FAILURE();
211   }
212
213   virtual void DeleteCookieAsync(const GURL& url,
214                                  const std::string& cookie_name,
215                                  const base::Closure& callback) OVERRIDE {
216     ADD_FAILURE();
217   }
218
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
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
234   virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE {
235     ADD_FAILURE();
236   }
237
238   virtual CookieMonster* GetCookieMonster() OVERRIDE { return NULL; }
239
240   const std::vector<Entry>& entries() const { return entries_; }
241
242  private:
243   friend class base::RefCountedThreadSafe<MockCookieStore>;
244   virtual ~MockCookieStore() {}
245
246   std::vector<Entry> entries_;
247 };
248
249 class MockSSLConfigService : public SSLConfigService {
250  public:
251   virtual void GetSSLConfig(SSLConfig* config) OVERRIDE {}
252
253  protected:
254   virtual ~MockSSLConfigService() {}
255 };
256
257 class MockURLRequestContext : public URLRequestContext {
258  public:
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
269   virtual ~MockURLRequestContext() {}
270
271  private:
272   TransportSecurityState transport_security_state_;
273 };
274
275 class MockHttpTransactionFactory : public HttpTransactionFactory {
276  public:
277   MockHttpTransactionFactory(NextProto next_proto, OrderedSocketData* data) {
278     data_ = data;
279     MockConnect connect_data(SYNCHRONOUS, OK);
280     data_->set_connect_data(connect_data);
281     session_deps_.reset(new SpdySessionDependencies(next_proto));
282     session_deps_->socket_factory->AddSocketDataProvider(data_);
283     http_session_ =
284         SpdySessionDependencies::SpdyCreateSession(session_deps_.get());
285     host_port_pair_.set_host("example.com");
286     host_port_pair_.set_port(80);
287     spdy_session_key_ = SpdySessionKey(host_port_pair_,
288                                             ProxyServer::Direct(),
289                                             kPrivacyModeDisabled);
290     session_ = CreateInsecureSpdySession(
291         http_session_, spdy_session_key_, BoundNetLog());
292   }
293
294   virtual int CreateTransaction(
295       RequestPriority priority,
296       scoped_ptr<HttpTransaction>* trans) OVERRIDE {
297     NOTREACHED();
298     return ERR_UNEXPECTED;
299   }
300
301   virtual HttpCache* GetCache() OVERRIDE {
302     NOTREACHED();
303     return NULL;
304   }
305
306   virtual HttpNetworkSession* GetSession() OVERRIDE {
307     return http_session_.get();
308   }
309
310  private:
311   OrderedSocketData* data_;
312   scoped_ptr<SpdySessionDependencies> session_deps_;
313   scoped_refptr<HttpNetworkSession> http_session_;
314   base::WeakPtr<SpdySession> session_;
315   HostPortPair host_port_pair_;
316   SpdySessionKey spdy_session_key_;
317 };
318
319 }  // namespace
320
321 class WebSocketJobTest : public PlatformTest,
322                          public ::testing::WithParamInterface<NextProto> {
323  public:
324   WebSocketJobTest() : spdy_util_(GetParam()) {}
325
326   virtual void SetUp() OVERRIDE {
327     stream_type_ = STREAM_INVALID;
328     cookie_store_ = new MockCookieStore;
329     context_.reset(new MockURLRequestContext(cookie_store_.get()));
330   }
331   virtual void TearDown() OVERRIDE {
332     cookie_store_ = NULL;
333     context_.reset();
334     websocket_ = NULL;
335     socket_ = NULL;
336   }
337   void DoSendRequest() {
338     EXPECT_TRUE(websocket_->SendData(kHandshakeRequestWithoutCookie,
339                                      kHandshakeRequestWithoutCookieLength));
340   }
341   void DoSendData() {
342     if (received_data().size() == kHandshakeResponseWithoutCookieLength)
343       websocket_->SendData(kDataHello, kDataHelloLength);
344   }
345   void DoSync() {
346     sync_test_callback_.callback().Run(OK);
347   }
348   int WaitForResult() {
349     return sync_test_callback_.WaitForResult();
350   }
351  protected:
352   enum StreamType {
353     STREAM_INVALID,
354     STREAM_MOCK_SOCKET,
355     STREAM_SOCKET,
356     STREAM_SPDY_WEBSOCKET,
357   };
358   enum ThrottlingOption {
359     THROTTLING_OFF,
360     THROTTLING_ON,
361   };
362   enum SpdyOption {
363     SPDY_OFF,
364     SPDY_ON,
365   };
366   void InitWebSocketJob(const GURL& url,
367                         MockSocketStreamDelegate* delegate,
368                         StreamType stream_type) {
369     DCHECK_NE(STREAM_INVALID, stream_type);
370     stream_type_ = stream_type;
371     websocket_ = new WebSocketJob(delegate);
372
373     if (stream_type == STREAM_MOCK_SOCKET)
374       socket_ = new MockSocketStream(url, websocket_.get(), context_.get(),
375                                      NULL);
376
377     if (stream_type == STREAM_SOCKET || stream_type == STREAM_SPDY_WEBSOCKET) {
378       if (stream_type == STREAM_SPDY_WEBSOCKET) {
379         http_factory_.reset(
380             new MockHttpTransactionFactory(GetParam(), data_.get()));
381         context_->set_http_transaction_factory(http_factory_.get());
382       }
383
384       ssl_config_service_ = new MockSSLConfigService();
385       context_->set_ssl_config_service(ssl_config_service_.get());
386       proxy_service_.reset(ProxyService::CreateDirect());
387       context_->set_proxy_service(proxy_service_.get());
388       host_resolver_.reset(new MockHostResolver);
389       context_->set_host_resolver(host_resolver_.get());
390
391       socket_ = new SocketStream(url, websocket_.get(), context_.get(), NULL);
392       socket_factory_.reset(new MockClientSocketFactory);
393       DCHECK(data_.get());
394       socket_factory_->AddSocketDataProvider(data_.get());
395       socket_->SetClientSocketFactory(socket_factory_.get());
396     }
397
398     websocket_->InitSocketStream(socket_.get());
399     // MockHostResolver resolves all hosts to 127.0.0.1; however, when we create
400     // a WebSocketJob purely to block another one in a throttling test, we don't
401     // perform a real connect. In that case, the following address is used
402     // instead.
403     IPAddressNumber ip;
404     ParseIPLiteralToNumber("127.0.0.1", &ip);
405     websocket_->addresses_ = AddressList::CreateFromIPAddress(ip, 80);
406   }
407   void SkipToConnecting() {
408     websocket_->state_ = WebSocketJob::CONNECTING;
409     ASSERT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(websocket_.get()));
410   }
411   WebSocketJob::State GetWebSocketJobState() {
412     return websocket_->state_;
413   }
414   void CloseWebSocketJob() {
415     if (websocket_->socket_.get()) {
416       websocket_->socket_->DetachDelegate();
417       WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_.get());
418     }
419     websocket_->state_ = WebSocketJob::CLOSED;
420     websocket_->delegate_ = NULL;
421     websocket_->socket_ = NULL;
422   }
423   SocketStream* GetSocket(SocketStreamJob* job) {
424     return job->socket_.get();
425   }
426   const std::string& sent_data() const {
427     DCHECK_EQ(STREAM_MOCK_SOCKET, stream_type_);
428     MockSocketStream* socket =
429         static_cast<MockSocketStream*>(socket_.get());
430     DCHECK(socket);
431     return socket->sent_data();
432   }
433   const std::string& received_data() const {
434     DCHECK_NE(STREAM_INVALID, stream_type_);
435     MockSocketStreamDelegate* delegate =
436         static_cast<MockSocketStreamDelegate*>(websocket_->delegate_);
437     DCHECK(delegate);
438     return delegate->received_data();
439   }
440
441   void TestSimpleHandshake();
442   void TestSlowHandshake();
443   void TestHandshakeWithCookie();
444   void TestHandshakeWithCookieButNotAllowed();
445   void TestHSTSUpgrade();
446   void TestInvalidSendData();
447   void TestConnectByWebSocket(ThrottlingOption throttling);
448   void TestConnectBySpdy(SpdyOption spdy, ThrottlingOption throttling);
449   void TestThrottlingLimit();
450
451   SpdyWebSocketTestUtil spdy_util_;
452   StreamType stream_type_;
453   scoped_refptr<MockCookieStore> cookie_store_;
454   scoped_ptr<MockURLRequestContext> context_;
455   scoped_refptr<WebSocketJob> websocket_;
456   scoped_refptr<SocketStream> socket_;
457   scoped_ptr<MockClientSocketFactory> socket_factory_;
458   scoped_ptr<OrderedSocketData> data_;
459   TestCompletionCallback sync_test_callback_;
460   scoped_refptr<MockSSLConfigService> ssl_config_service_;
461   scoped_ptr<ProxyService> proxy_service_;
462   scoped_ptr<MockHostResolver> host_resolver_;
463   scoped_ptr<MockHttpTransactionFactory> http_factory_;
464
465   static const char kHandshakeRequestWithoutCookie[];
466   static const char kHandshakeRequestWithCookie[];
467   static const char kHandshakeRequestWithFilteredCookie[];
468   static const char kHandshakeResponseWithoutCookie[];
469   static const char kHandshakeResponseWithCookie[];
470   static const char kDataHello[];
471   static const char kDataWorld[];
472   static const char* const kHandshakeRequestForSpdy[];
473   static const char* const kHandshakeResponseForSpdy[];
474   static const size_t kHandshakeRequestWithoutCookieLength;
475   static const size_t kHandshakeRequestWithCookieLength;
476   static const size_t kHandshakeRequestWithFilteredCookieLength;
477   static const size_t kHandshakeResponseWithoutCookieLength;
478   static const size_t kHandshakeResponseWithCookieLength;
479   static const size_t kDataHelloLength;
480   static const size_t kDataWorldLength;
481 };
482
483 const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] =
484     "GET /demo HTTP/1.1\r\n"
485     "Host: example.com\r\n"
486     "Upgrade: WebSocket\r\n"
487     "Connection: Upgrade\r\n"
488     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
489     "Origin: http://example.com\r\n"
490     "Sec-WebSocket-Protocol: sample\r\n"
491     "Sec-WebSocket-Version: 13\r\n"
492     "\r\n";
493
494 const char WebSocketJobTest::kHandshakeRequestWithCookie[] =
495     "GET /demo HTTP/1.1\r\n"
496     "Host: example.com\r\n"
497     "Upgrade: WebSocket\r\n"
498     "Connection: Upgrade\r\n"
499     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
500     "Origin: http://example.com\r\n"
501     "Sec-WebSocket-Protocol: sample\r\n"
502     "Sec-WebSocket-Version: 13\r\n"
503     "Cookie: WK-test=1\r\n"
504     "\r\n";
505
506 const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie[] =
507     "GET /demo HTTP/1.1\r\n"
508     "Host: example.com\r\n"
509     "Upgrade: WebSocket\r\n"
510     "Connection: Upgrade\r\n"
511     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
512     "Origin: http://example.com\r\n"
513     "Sec-WebSocket-Protocol: sample\r\n"
514     "Sec-WebSocket-Version: 13\r\n"
515     "Cookie: CR-test=1; CR-test-httponly=1\r\n"
516     "\r\n";
517
518 const char WebSocketJobTest::kHandshakeResponseWithoutCookie[] =
519     "HTTP/1.1 101 Switching Protocols\r\n"
520     "Upgrade: websocket\r\n"
521     "Connection: Upgrade\r\n"
522     "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
523     "Sec-WebSocket-Protocol: sample\r\n"
524     "\r\n";
525
526 const char WebSocketJobTest::kHandshakeResponseWithCookie[] =
527     "HTTP/1.1 101 Switching Protocols\r\n"
528     "Upgrade: websocket\r\n"
529     "Connection: Upgrade\r\n"
530     "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
531     "Sec-WebSocket-Protocol: sample\r\n"
532     "Set-Cookie: CR-set-test=1\r\n"
533     "\r\n";
534
535 const char WebSocketJobTest::kDataHello[] = "Hello, ";
536
537 const char WebSocketJobTest::kDataWorld[] = "World!\n";
538
539 const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength =
540     arraysize(kHandshakeRequestWithoutCookie) - 1;
541 const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength =
542     arraysize(kHandshakeRequestWithCookie) - 1;
543 const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength =
544     arraysize(kHandshakeRequestWithFilteredCookie) - 1;
545 const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength =
546     arraysize(kHandshakeResponseWithoutCookie) - 1;
547 const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength =
548     arraysize(kHandshakeResponseWithCookie) - 1;
549 const size_t WebSocketJobTest::kDataHelloLength =
550     arraysize(kDataHello) - 1;
551 const size_t WebSocketJobTest::kDataWorldLength =
552     arraysize(kDataWorld) - 1;
553
554 void WebSocketJobTest::TestSimpleHandshake() {
555   GURL url("ws://example.com/demo");
556   MockSocketStreamDelegate delegate;
557   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
558   SkipToConnecting();
559
560   DoSendRequest();
561   base::MessageLoop::current()->RunUntilIdle();
562   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
563   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
564   websocket_->OnSentData(socket_.get(),
565                          kHandshakeRequestWithoutCookieLength);
566   EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
567
568   websocket_->OnReceivedData(socket_.get(),
569                              kHandshakeResponseWithoutCookie,
570                              kHandshakeResponseWithoutCookieLength);
571   base::MessageLoop::current()->RunUntilIdle();
572   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
573   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
574   CloseWebSocketJob();
575 }
576
577 void WebSocketJobTest::TestSlowHandshake() {
578   GURL url("ws://example.com/demo");
579   MockSocketStreamDelegate delegate;
580   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
581   SkipToConnecting();
582
583   DoSendRequest();
584   // We assume request is sent in one data chunk (from WebKit)
585   // We don't support streaming request.
586   base::MessageLoop::current()->RunUntilIdle();
587   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
588   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
589   websocket_->OnSentData(socket_.get(),
590                          kHandshakeRequestWithoutCookieLength);
591   EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
592
593   std::vector<std::string> lines;
594   base::SplitString(kHandshakeResponseWithoutCookie, '\n', &lines);
595   for (size_t i = 0; i < lines.size() - 2; i++) {
596     std::string line = lines[i] + "\r\n";
597     SCOPED_TRACE("Line: " + line);
598     websocket_->OnReceivedData(socket_.get(), line.c_str(), line.size());
599     base::MessageLoop::current()->RunUntilIdle();
600     EXPECT_TRUE(delegate.received_data().empty());
601     EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
602   }
603   websocket_->OnReceivedData(socket_.get(), "\r\n", 2);
604   base::MessageLoop::current()->RunUntilIdle();
605   EXPECT_FALSE(delegate.received_data().empty());
606   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
607   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
608   CloseWebSocketJob();
609 }
610
611 INSTANTIATE_TEST_CASE_P(
612     NextProto,
613     WebSocketJobTest,
614     testing::Values(kProtoDeprecatedSPDY2,
615                     kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
616                     kProtoHTTP2Draft04));
617
618 TEST_P(WebSocketJobTest, DelayedCookies) {
619   WebSocketJob::set_websocket_over_spdy_enabled(true);
620   GURL url("ws://example.com/demo");
621   GURL cookieUrl("http://example.com/demo");
622   CookieOptions cookie_options;
623   scoped_refptr<DelayedCookieMonster> cookie_store = new DelayedCookieMonster();
624   context_->set_cookie_store(cookie_store.get());
625   cookie_store->SetCookieWithOptionsAsync(cookieUrl,
626                                           "CR-test=1",
627                                           cookie_options,
628                                           CookieMonster::SetCookiesCallback());
629   cookie_options.set_include_httponly();
630   cookie_store->SetCookieWithOptionsAsync(
631       cookieUrl, "CR-test-httponly=1", cookie_options,
632       CookieMonster::SetCookiesCallback());
633
634   MockSocketStreamDelegate delegate;
635   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
636   SkipToConnecting();
637
638   bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
639                                    kHandshakeRequestWithCookieLength);
640   EXPECT_TRUE(sent);
641   base::MessageLoop::current()->RunUntilIdle();
642   EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data());
643   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
644   websocket_->OnSentData(socket_.get(),
645                          kHandshakeRequestWithFilteredCookieLength);
646   EXPECT_EQ(kHandshakeRequestWithCookieLength,
647             delegate.amount_sent());
648
649   websocket_->OnReceivedData(socket_.get(),
650                              kHandshakeResponseWithCookie,
651                              kHandshakeResponseWithCookieLength);
652   base::MessageLoop::current()->RunUntilIdle();
653   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
654   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
655
656   CloseWebSocketJob();
657 }
658
659 void WebSocketJobTest::TestHandshakeWithCookie() {
660   GURL url("ws://example.com/demo");
661   GURL cookieUrl("http://example.com/demo");
662   CookieOptions cookie_options;
663   cookie_store_->SetCookieWithOptions(
664       cookieUrl, "CR-test=1", cookie_options);
665   cookie_options.set_include_httponly();
666   cookie_store_->SetCookieWithOptions(
667       cookieUrl, "CR-test-httponly=1", cookie_options);
668
669   MockSocketStreamDelegate delegate;
670   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
671   SkipToConnecting();
672
673   bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
674                                    kHandshakeRequestWithCookieLength);
675   EXPECT_TRUE(sent);
676   base::MessageLoop::current()->RunUntilIdle();
677   EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data());
678   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
679   websocket_->OnSentData(socket_.get(),
680                          kHandshakeRequestWithFilteredCookieLength);
681   EXPECT_EQ(kHandshakeRequestWithCookieLength,
682             delegate.amount_sent());
683
684   websocket_->OnReceivedData(socket_.get(),
685                              kHandshakeResponseWithCookie,
686                              kHandshakeResponseWithCookieLength);
687   base::MessageLoop::current()->RunUntilIdle();
688   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
689   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
690
691   EXPECT_EQ(3U, cookie_store_->entries().size());
692   EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
693   EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
694   EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
695   EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
696   EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url);
697   EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line);
698
699   CloseWebSocketJob();
700 }
701
702 void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() {
703   GURL url("ws://example.com/demo");
704   GURL cookieUrl("http://example.com/demo");
705   CookieOptions cookie_options;
706   cookie_store_->SetCookieWithOptions(
707       cookieUrl, "CR-test=1", cookie_options);
708   cookie_options.set_include_httponly();
709   cookie_store_->SetCookieWithOptions(
710       cookieUrl, "CR-test-httponly=1", cookie_options);
711
712   MockSocketStreamDelegate delegate;
713   delegate.set_allow_all_cookies(false);
714   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
715   SkipToConnecting();
716
717   bool sent = websocket_->SendData(kHandshakeRequestWithCookie,
718                                    kHandshakeRequestWithCookieLength);
719   EXPECT_TRUE(sent);
720   base::MessageLoop::current()->RunUntilIdle();
721   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
722   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
723   websocket_->OnSentData(socket_.get(), kHandshakeRequestWithoutCookieLength);
724   EXPECT_EQ(kHandshakeRequestWithCookieLength, delegate.amount_sent());
725
726   websocket_->OnReceivedData(socket_.get(),
727                              kHandshakeResponseWithCookie,
728                              kHandshakeResponseWithCookieLength);
729   base::MessageLoop::current()->RunUntilIdle();
730   EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data());
731   EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
732
733   EXPECT_EQ(2U, cookie_store_->entries().size());
734   EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
735   EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
736   EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
737   EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
738
739   CloseWebSocketJob();
740 }
741
742 void WebSocketJobTest::TestHSTSUpgrade() {
743   GURL url("ws://upgrademe.com/");
744   MockSocketStreamDelegate delegate;
745   scoped_refptr<SocketStreamJob> job =
746       SocketStreamJob::CreateSocketStreamJob(
747           url, &delegate, context_->transport_security_state(),
748           context_->ssl_config_service(), NULL, NULL);
749   EXPECT_TRUE(GetSocket(job.get())->is_secure());
750   job->DetachDelegate();
751
752   url = GURL("ws://donotupgrademe.com/");
753   job = SocketStreamJob::CreateSocketStreamJob(
754       url, &delegate, context_->transport_security_state(),
755       context_->ssl_config_service(), NULL, NULL);
756   EXPECT_FALSE(GetSocket(job.get())->is_secure());
757   job->DetachDelegate();
758 }
759
760 void WebSocketJobTest::TestInvalidSendData() {
761   GURL url("ws://example.com/demo");
762   MockSocketStreamDelegate delegate;
763   InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET);
764   SkipToConnecting();
765
766   DoSendRequest();
767   // We assume request is sent in one data chunk (from WebKit)
768   // We don't support streaming request.
769   base::MessageLoop::current()->RunUntilIdle();
770   EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data());
771   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
772   websocket_->OnSentData(socket_.get(),
773                          kHandshakeRequestWithoutCookieLength);
774   EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent());
775
776   // We could not send any data until connection is established.
777   bool sent = websocket_->SendData(kHandshakeRequestWithoutCookie,
778                                    kHandshakeRequestWithoutCookieLength);
779   EXPECT_FALSE(sent);
780   EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
781   CloseWebSocketJob();
782 }
783
784 // Following tests verify cooperation between WebSocketJob and SocketStream.
785 // Other former tests use MockSocketStream as SocketStream, so we could not
786 // check SocketStream behavior.
787 // OrderedSocketData provide socket level verifiation by checking out-going
788 // packets in comparison with the MockWrite array and emulating in-coming
789 // packets with MockRead array.
790
791 void WebSocketJobTest::TestConnectByWebSocket(
792     ThrottlingOption throttling) {
793   // This is a test for verifying cooperation between WebSocketJob and
794   // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic
795   // situation. If |throttling| was |THROTTLING_ON|, throttling limits the
796   // latter connection.
797   MockWrite writes[] = {
798     MockWrite(ASYNC,
799               kHandshakeRequestWithoutCookie,
800               kHandshakeRequestWithoutCookieLength,
801               1),
802     MockWrite(ASYNC,
803               kDataHello,
804               kDataHelloLength,
805               3)
806   };
807   MockRead reads[] = {
808     MockRead(ASYNC,
809              kHandshakeResponseWithoutCookie,
810              kHandshakeResponseWithoutCookieLength,
811              2),
812     MockRead(ASYNC,
813              kDataWorld,
814              kDataWorldLength,
815              4),
816     MockRead(SYNCHRONOUS, 0, 5)  // EOF
817   };
818   data_.reset(new OrderedSocketData(
819       reads, arraysize(reads), writes, arraysize(writes)));
820
821   GURL url("ws://example.com/demo");
822   MockSocketStreamDelegate delegate;
823   WebSocketJobTest* test = this;
824   if (throttling == THROTTLING_ON)
825     delegate.SetOnStartOpenConnection(
826         base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
827   delegate.SetOnConnected(
828       base::Bind(&WebSocketJobTest::DoSendRequest,
829                  base::Unretained(test)));
830   delegate.SetOnReceivedData(
831       base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test)));
832   delegate.SetOnClose(
833       base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
834   InitWebSocketJob(url, &delegate, STREAM_SOCKET);
835
836   scoped_refptr<WebSocketJob> block_websocket;
837   if (throttling == THROTTLING_ON) {
838     // Create former WebSocket object which obstructs the latter one.
839     block_websocket = new WebSocketJob(NULL);
840     block_websocket->addresses_ = AddressList(websocket_->address_list());
841     ASSERT_TRUE(
842         WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get()));
843   }
844
845   websocket_->Connect();
846
847   if (throttling == THROTTLING_ON) {
848     EXPECT_EQ(OK, WaitForResult());
849     EXPECT_TRUE(websocket_->IsWaiting());
850
851     // Remove the former WebSocket object from throttling queue to unblock the
852     // latter.
853     block_websocket->state_ = WebSocketJob::CLOSED;
854     WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get());
855     block_websocket = NULL;
856   }
857
858   EXPECT_EQ(OK, WaitForResult());
859   EXPECT_TRUE(data_->at_read_eof());
860   EXPECT_TRUE(data_->at_write_eof());
861   EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState());
862 }
863
864 void WebSocketJobTest::TestConnectBySpdy(
865     SpdyOption spdy, ThrottlingOption throttling) {
866   // This is a test for verifying cooperation between WebSocketJob and
867   // SocketStream in the situation we have SPDY session to the server. If
868   // |throttling| was |THROTTLING_ON|, throttling limits the latter connection.
869   // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected
870   // results depend on its configuration.
871   MockWrite writes_websocket[] = {
872     MockWrite(ASYNC,
873               kHandshakeRequestWithoutCookie,
874               kHandshakeRequestWithoutCookieLength,
875               1),
876     MockWrite(ASYNC,
877               kDataHello,
878               kDataHelloLength,
879               3)
880   };
881   MockRead reads_websocket[] = {
882     MockRead(ASYNC,
883              kHandshakeResponseWithoutCookie,
884              kHandshakeResponseWithoutCookieLength,
885              2),
886     MockRead(ASYNC,
887              kDataWorld,
888              kDataWorldLength,
889              4),
890     MockRead(SYNCHRONOUS, 0, 5)  // EOF
891   };
892
893   scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock());
894   spdy_util_.SetHeader("path", "/demo", request_headers.get());
895   spdy_util_.SetHeader("version", "WebSocket/13", request_headers.get());
896   spdy_util_.SetHeader("scheme", "ws", request_headers.get());
897   spdy_util_.SetHeader("host", "example.com", request_headers.get());
898   spdy_util_.SetHeader("origin", "http://example.com", request_headers.get());
899   spdy_util_.SetHeader("sec-websocket-protocol", "sample",
900                        request_headers.get());
901
902   scoped_ptr<SpdyHeaderBlock> response_headers(new SpdyHeaderBlock());
903   spdy_util_.SetHeader("status", "101 Switching Protocols",
904                        response_headers.get());
905   spdy_util_.SetHeader("sec-websocket-protocol", "sample",
906                        response_headers.get());
907
908   const SpdyStreamId kStreamId = 1;
909   scoped_ptr<SpdyFrame> request_frame(
910       spdy_util_.ConstructSpdyWebSocketHandshakeRequestFrame(
911           request_headers.Pass(),
912           kStreamId,
913           MEDIUM));
914   scoped_ptr<SpdyFrame> response_frame(
915       spdy_util_.ConstructSpdyWebSocketHandshakeResponseFrame(
916           response_headers.Pass(),
917           kStreamId,
918           MEDIUM));
919   scoped_ptr<SpdyFrame> data_hello_frame(
920       spdy_util_.ConstructSpdyWebSocketDataFrame(
921           kDataHello,
922           kDataHelloLength,
923           kStreamId,
924           false));
925   scoped_ptr<SpdyFrame> data_world_frame(
926       spdy_util_.ConstructSpdyWebSocketDataFrame(
927           kDataWorld,
928           kDataWorldLength,
929           kStreamId,
930           false));
931   MockWrite writes_spdy[] = {
932     CreateMockWrite(*request_frame.get(), 1),
933     CreateMockWrite(*data_hello_frame.get(), 3),
934   };
935   MockRead reads_spdy[] = {
936     CreateMockRead(*response_frame.get(), 2),
937     CreateMockRead(*data_world_frame.get(), 4),
938     MockRead(SYNCHRONOUS, 0, 5)  // EOF
939   };
940
941   if (spdy == SPDY_ON)
942     data_.reset(new OrderedSocketData(
943         reads_spdy, arraysize(reads_spdy),
944         writes_spdy, arraysize(writes_spdy)));
945   else
946     data_.reset(new OrderedSocketData(
947         reads_websocket, arraysize(reads_websocket),
948         writes_websocket, arraysize(writes_websocket)));
949
950   GURL url("ws://example.com/demo");
951   MockSocketStreamDelegate delegate;
952   WebSocketJobTest* test = this;
953   if (throttling == THROTTLING_ON)
954     delegate.SetOnStartOpenConnection(
955         base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
956   delegate.SetOnConnected(
957       base::Bind(&WebSocketJobTest::DoSendRequest,
958                  base::Unretained(test)));
959   delegate.SetOnReceivedData(
960       base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test)));
961   delegate.SetOnClose(
962       base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test)));
963   InitWebSocketJob(url, &delegate, STREAM_SPDY_WEBSOCKET);
964
965   scoped_refptr<WebSocketJob> block_websocket;
966   if (throttling == THROTTLING_ON) {
967     // Create former WebSocket object which obstructs the latter one.
968     block_websocket = new WebSocketJob(NULL);
969     block_websocket->addresses_ = AddressList(websocket_->address_list());
970     ASSERT_TRUE(
971         WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get()));
972   }
973
974   websocket_->Connect();
975
976   if (throttling == THROTTLING_ON) {
977     EXPECT_EQ(OK, WaitForResult());
978     EXPECT_TRUE(websocket_->IsWaiting());
979
980     // Remove the former WebSocket object from throttling queue to unblock the
981     // latter.
982     block_websocket->state_ = WebSocketJob::CLOSED;
983     WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get());
984     block_websocket = NULL;
985   }
986
987   EXPECT_EQ(OK, WaitForResult());
988   EXPECT_TRUE(data_->at_read_eof());
989   EXPECT_TRUE(data_->at_write_eof());
990   EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState());
991 }
992
993 void WebSocketJobTest::TestThrottlingLimit() {
994   std::vector<scoped_refptr<WebSocketJob> > jobs;
995   const int kMaxWebSocketJobsThrottled = 1024;
996   IPAddressNumber ip;
997   ParseIPLiteralToNumber("127.0.0.1", &ip);
998   for (int i = 0; i < kMaxWebSocketJobsThrottled + 1; ++i) {
999     scoped_refptr<WebSocketJob> job = new WebSocketJob(NULL);
1000     job->addresses_ = AddressList(AddressList::CreateFromIPAddress(ip, 80));
1001     if (i >= kMaxWebSocketJobsThrottled)
1002       EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job));
1003     else
1004       EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job));
1005     jobs.push_back(job);
1006   }
1007
1008   // Close the jobs in reverse order. Otherwise, We need to make them prepared
1009   // for Wakeup call.
1010   for (std::vector<scoped_refptr<WebSocketJob> >::reverse_iterator iter =
1011            jobs.rbegin();
1012        iter != jobs.rend();
1013        ++iter) {
1014     WebSocketJob* job = (*iter).get();
1015     job->state_ = WebSocketJob::CLOSED;
1016     WebSocketThrottle::GetInstance()->RemoveFromQueue(job);
1017   }
1018 }
1019
1020 // Execute tests in both spdy-disabled mode and spdy-enabled mode.
1021 TEST_P(WebSocketJobTest, SimpleHandshake) {
1022   WebSocketJob::set_websocket_over_spdy_enabled(false);
1023   TestSimpleHandshake();
1024 }
1025
1026 TEST_P(WebSocketJobTest, SlowHandshake) {
1027   WebSocketJob::set_websocket_over_spdy_enabled(false);
1028   TestSlowHandshake();
1029 }
1030
1031 TEST_P(WebSocketJobTest, HandshakeWithCookie) {
1032   WebSocketJob::set_websocket_over_spdy_enabled(false);
1033   TestHandshakeWithCookie();
1034 }
1035
1036 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) {
1037   WebSocketJob::set_websocket_over_spdy_enabled(false);
1038   TestHandshakeWithCookieButNotAllowed();
1039 }
1040
1041 TEST_P(WebSocketJobTest, HSTSUpgrade) {
1042   WebSocketJob::set_websocket_over_spdy_enabled(false);
1043   TestHSTSUpgrade();
1044 }
1045
1046 TEST_P(WebSocketJobTest, InvalidSendData) {
1047   WebSocketJob::set_websocket_over_spdy_enabled(false);
1048   TestInvalidSendData();
1049 }
1050
1051 TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) {
1052   WebSocketJob::set_websocket_over_spdy_enabled(true);
1053   TestSimpleHandshake();
1054 }
1055
1056 TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) {
1057   WebSocketJob::set_websocket_over_spdy_enabled(true);
1058   TestSlowHandshake();
1059 }
1060
1061 TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) {
1062   WebSocketJob::set_websocket_over_spdy_enabled(true);
1063   TestHandshakeWithCookie();
1064 }
1065
1066 TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) {
1067   WebSocketJob::set_websocket_over_spdy_enabled(true);
1068   TestHandshakeWithCookieButNotAllowed();
1069 }
1070
1071 TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) {
1072   WebSocketJob::set_websocket_over_spdy_enabled(true);
1073   TestHSTSUpgrade();
1074 }
1075
1076 TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) {
1077   WebSocketJob::set_websocket_over_spdy_enabled(true);
1078   TestInvalidSendData();
1079 }
1080
1081 TEST_P(WebSocketJobTest, ConnectByWebSocket) {
1082   WebSocketJob::set_websocket_over_spdy_enabled(false);
1083   TestConnectByWebSocket(THROTTLING_OFF);
1084 }
1085
1086 TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) {
1087   WebSocketJob::set_websocket_over_spdy_enabled(true);
1088   TestConnectByWebSocket(THROTTLING_OFF);
1089 }
1090
1091 TEST_P(WebSocketJobTest, ConnectBySpdy) {
1092   WebSocketJob::set_websocket_over_spdy_enabled(false);
1093   TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF);
1094 }
1095
1096 TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) {
1097   WebSocketJob::set_websocket_over_spdy_enabled(true);
1098   TestConnectBySpdy(SPDY_ON, THROTTLING_OFF);
1099 }
1100
1101 TEST_P(WebSocketJobTest, ThrottlingWebSocket) {
1102   WebSocketJob::set_websocket_over_spdy_enabled(false);
1103   TestConnectByWebSocket(THROTTLING_ON);
1104 }
1105
1106 TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) {
1107   TestThrottlingLimit();
1108 }
1109
1110 TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) {
1111   WebSocketJob::set_websocket_over_spdy_enabled(true);
1112   TestConnectByWebSocket(THROTTLING_ON);
1113 }
1114
1115 TEST_P(WebSocketJobTest, ThrottlingSpdy) {
1116   WebSocketJob::set_websocket_over_spdy_enabled(false);
1117   TestConnectBySpdy(SPDY_OFF, THROTTLING_ON);
1118 }
1119
1120 TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) {
1121   WebSocketJob::set_websocket_over_spdy_enabled(true);
1122   TestConnectBySpdy(SPDY_ON, THROTTLING_ON);
1123 }
1124
1125 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation.
1126 // TODO(toyoshim,yutak): Add tests to verify closing handshake.
1127 }  // namespace net