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