1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/spdy/spdy_websocket_stream.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "net/base/completion_callback.h"
13 #include "net/proxy/proxy_server.h"
14 #include "net/socket/next_proto.h"
15 #include "net/socket/ssl_client_socket.h"
16 #include "net/spdy/spdy_http_utils.h"
17 #include "net/spdy/spdy_protocol.h"
18 #include "net/spdy/spdy_session.h"
19 #include "net/spdy/spdy_websocket_test_util.h"
20 #include "testing/gtest/include/gtest/gtest.h"
26 struct SpdyWebSocketStreamEvent {
30 EVENT_RECEIVED_HEADER,
35 SpdyWebSocketStreamEvent(EventType type,
36 const SpdyHeaderBlock& headers,
38 const std::string& data)
45 SpdyHeaderBlock headers;
50 class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStream::Delegate {
52 explicit SpdyWebSocketStreamEventRecorder(const CompletionCallback& callback)
53 : callback_(callback) {}
54 virtual ~SpdyWebSocketStreamEventRecorder() {}
56 typedef base::Callback<void(SpdyWebSocketStreamEvent*)> StreamEventCallback;
58 void SetOnCreated(const StreamEventCallback& callback) {
59 on_created_ = callback;
61 void SetOnSentHeaders(const StreamEventCallback& callback) {
62 on_sent_headers_ = callback;
64 void SetOnReceivedHeader(const StreamEventCallback& callback) {
65 on_received_header_ = callback;
67 void SetOnSentData(const StreamEventCallback& callback) {
68 on_sent_data_ = callback;
70 void SetOnReceivedData(const StreamEventCallback& callback) {
71 on_received_data_ = callback;
73 void SetOnClose(const StreamEventCallback& callback) {
77 virtual void OnCreatedSpdyStream(int result) OVERRIDE {
79 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED,
83 if (!on_created_.is_null())
84 on_created_.Run(&events_.back());
86 virtual void OnSentSpdyHeaders() OVERRIDE {
88 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
92 if (!on_sent_data_.is_null())
93 on_sent_data_.Run(&events_.back());
95 virtual void OnSpdyResponseHeadersUpdated(
96 const SpdyHeaderBlock& response_headers) OVERRIDE {
98 SpdyWebSocketStreamEvent(
99 SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
103 if (!on_received_header_.is_null())
104 on_received_header_.Run(&events_.back());
106 virtual void OnSentSpdyData(size_t bytes_sent) OVERRIDE {
108 SpdyWebSocketStreamEvent(
109 SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
111 static_cast<int>(bytes_sent),
113 if (!on_sent_data_.is_null())
114 on_sent_data_.Run(&events_.back());
116 virtual void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) OVERRIDE {
117 std::string buffer_data;
118 size_t buffer_len = 0;
120 buffer_len = buffer->GetRemainingSize();
121 buffer_data.append(buffer->GetRemainingData(), buffer_len);
124 SpdyWebSocketStreamEvent(
125 SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
129 if (!on_received_data_.is_null())
130 on_received_data_.Run(&events_.back());
132 virtual void OnCloseSpdyStream() OVERRIDE {
134 SpdyWebSocketStreamEvent(
135 SpdyWebSocketStreamEvent::EVENT_CLOSE,
139 if (!on_close_.is_null())
140 on_close_.Run(&events_.back());
141 if (!callback_.is_null())
145 const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const {
150 std::vector<SpdyWebSocketStreamEvent> events_;
151 StreamEventCallback on_created_;
152 StreamEventCallback on_sent_headers_;
153 StreamEventCallback on_received_header_;
154 StreamEventCallback on_sent_data_;
155 StreamEventCallback on_received_data_;
156 StreamEventCallback on_close_;
157 CompletionCallback callback_;
159 DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder);
164 class SpdyWebSocketStreamTest
165 : public ::testing::Test,
166 public ::testing::WithParamInterface<NextProto> {
168 OrderedSocketData* data() { return data_.get(); }
170 void DoSendHelloFrame(SpdyWebSocketStreamEvent* event) {
171 // Record the actual stream_id.
172 created_stream_id_ = websocket_stream_->stream_->stream_id();
173 websocket_stream_->SendData(kMessageFrame, kMessageFrameLength);
176 void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) {
177 websocket_stream_->SendData(kClosingFrame, kClosingFrameLength);
180 void DoClose(SpdyWebSocketStreamEvent* event) {
181 websocket_stream_->Close();
184 void DoSync(SpdyWebSocketStreamEvent* event) {
185 sync_callback_.callback().Run(OK);
189 SpdyWebSocketStreamTest()
190 : spdy_util_(GetParam()),
191 spdy_settings_id_to_set_(SETTINGS_MAX_CONCURRENT_STREAMS),
192 spdy_settings_flags_to_set_(SETTINGS_FLAG_PLEASE_PERSIST),
193 spdy_settings_value_to_set_(1),
194 session_deps_(GetParam()),
196 created_stream_id_(0) {}
197 virtual ~SpdyWebSocketStreamTest() {}
199 virtual void SetUp() {
200 host_port_pair_.set_host("example.com");
201 host_port_pair_.set_port(80);
202 spdy_session_key_ = SpdySessionKey(host_port_pair_,
203 ProxyServer::Direct(),
204 kPrivacyModeDisabled);
206 spdy_settings_to_send_[spdy_settings_id_to_set_] =
207 SettingsFlagsAndValue(
208 SETTINGS_FLAG_PERSISTED, spdy_settings_value_to_set_);
211 virtual void TearDown() {
212 base::MessageLoop::current()->RunUntilIdle();
215 void Prepare(SpdyStreamId stream_id) {
216 stream_id_ = stream_id;
218 request_frame_.reset(spdy_util_.ConstructSpdyWebSocketSynStream(
222 "http://example.com/wsdemo"));
224 response_frame_.reset(
225 spdy_util_.ConstructSpdyWebSocketSynReply(stream_id_));
227 message_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame(
233 closing_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame(
240 void InitSession(MockRead* reads, size_t reads_count,
241 MockWrite* writes, size_t writes_count) {
242 data_.reset(new OrderedSocketData(reads, reads_count,
243 writes, writes_count));
244 session_deps_.socket_factory->AddSocketDataProvider(data_.get());
245 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
246 session_ = CreateInsecureSpdySession(
247 http_session_, spdy_session_key_, BoundNetLog());
251 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
252 spdy_util_.SetHeader("path", "/echo", headers.get());
253 spdy_util_.SetHeader("host", "example.com", headers.get());
254 spdy_util_.SetHeader("version", "WebSocket/13", headers.get());
255 spdy_util_.SetHeader("scheme", "ws", headers.get());
256 spdy_util_.SetHeader("origin", "http://example.com/wsdemo", headers.get());
257 websocket_stream_->SendRequest(headers.Pass());
260 SpdyWebSocketTestUtil spdy_util_;
261 SpdySettingsIds spdy_settings_id_to_set_;
262 SpdySettingsFlags spdy_settings_flags_to_set_;
263 uint32 spdy_settings_value_to_set_;
264 SettingsMap spdy_settings_to_send_;
265 SpdySessionDependencies session_deps_;
266 scoped_ptr<OrderedSocketData> data_;
267 scoped_refptr<HttpNetworkSession> http_session_;
268 base::WeakPtr<SpdySession> session_;
269 scoped_ptr<SpdyWebSocketStream> websocket_stream_;
270 SpdyStreamId stream_id_;
271 SpdyStreamId created_stream_id_;
272 scoped_ptr<SpdyFrame> request_frame_;
273 scoped_ptr<SpdyFrame> response_frame_;
274 scoped_ptr<SpdyFrame> message_frame_;
275 scoped_ptr<SpdyFrame> closing_frame_;
276 HostPortPair host_port_pair_;
277 SpdySessionKey spdy_session_key_;
278 TestCompletionCallback completion_callback_;
279 TestCompletionCallback sync_callback_;
281 static const char kMessageFrame[];
282 static const char kClosingFrame[];
283 static const size_t kMessageFrameLength;
284 static const size_t kClosingFrameLength;
287 INSTANTIATE_TEST_CASE_P(
289 SpdyWebSocketStreamTest,
290 testing::Values(kProtoDeprecatedSPDY2,
291 kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
292 kProtoHTTP2Draft04));
294 // TODO(toyoshim): Replace old framing data to new one, then use HEADERS and
296 const char SpdyWebSocketStreamTest::kMessageFrame[] = "\x81\x05hello";
297 const char SpdyWebSocketStreamTest::kClosingFrame[] = "\x88\0";
298 const size_t SpdyWebSocketStreamTest::kMessageFrameLength =
299 arraysize(SpdyWebSocketStreamTest::kMessageFrame) - 1;
300 const size_t SpdyWebSocketStreamTest::kClosingFrameLength =
301 arraysize(SpdyWebSocketStreamTest::kClosingFrame) - 1;
303 TEST_P(SpdyWebSocketStreamTest, Basic) {
305 MockWrite writes[] = {
306 CreateMockWrite(*request_frame_.get(), 1),
307 CreateMockWrite(*message_frame_.get(), 3),
308 CreateMockWrite(*closing_frame_.get(), 5)
312 CreateMockRead(*response_frame_.get(), 2),
313 CreateMockRead(*message_frame_.get(), 4),
314 // Skip sequence 6 to notify closing has been sent.
315 CreateMockRead(*closing_frame_.get(), 7),
316 MockRead(SYNCHRONOUS, 0, 8) // EOF cause OnCloseSpdyStream event.
319 InitSession(reads, arraysize(reads), writes, arraysize(writes));
321 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
322 delegate.SetOnReceivedHeader(
323 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
324 base::Unretained(this)));
325 delegate.SetOnReceivedData(
326 base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame,
327 base::Unretained(this)));
329 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
332 GURL url("ws://example.com/echo");
333 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
335 ASSERT_TRUE(websocket_stream_->stream_.get());
339 completion_callback_.WaitForResult();
341 EXPECT_EQ(stream_id_, created_stream_id_);
343 websocket_stream_.reset();
345 const std::vector<SpdyWebSocketStreamEvent>& events =
346 delegate.GetSeenEvents();
347 ASSERT_EQ(7U, events.size());
349 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
350 events[0].event_type);
351 EXPECT_EQ(OK, events[0].result);
352 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
353 events[1].event_type);
354 EXPECT_EQ(OK, events[1].result);
355 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
356 events[2].event_type);
357 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result);
358 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
359 events[3].event_type);
360 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
361 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
362 events[4].event_type);
363 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[4].result);
364 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
365 events[5].event_type);
366 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result);
367 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
368 events[6].event_type);
369 EXPECT_EQ(OK, events[6].result);
371 // EOF close SPDY session.
373 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
374 EXPECT_TRUE(data()->at_read_eof());
375 EXPECT_TRUE(data()->at_write_eof());
378 TEST_P(SpdyWebSocketStreamTest, DestructionBeforeClose) {
380 MockWrite writes[] = {
381 CreateMockWrite(*request_frame_.get(), 1),
382 CreateMockWrite(*message_frame_.get(), 3)
386 CreateMockRead(*response_frame_.get(), 2),
387 CreateMockRead(*message_frame_.get(), 4),
388 MockRead(ASYNC, ERR_IO_PENDING, 5)
391 InitSession(reads, arraysize(reads), writes, arraysize(writes));
393 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
394 delegate.SetOnReceivedHeader(
395 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
396 base::Unretained(this)));
397 delegate.SetOnReceivedData(
398 base::Bind(&SpdyWebSocketStreamTest::DoSync,
399 base::Unretained(this)));
401 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
404 GURL url("ws://example.com/echo");
405 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
409 sync_callback_.WaitForResult();
411 // WebSocketStream destruction remove its SPDY stream from the session.
412 EXPECT_TRUE(session_->IsStreamActive(stream_id_));
413 websocket_stream_.reset();
414 EXPECT_FALSE(session_->IsStreamActive(stream_id_));
416 const std::vector<SpdyWebSocketStreamEvent>& events =
417 delegate.GetSeenEvents();
418 ASSERT_GE(4U, events.size());
420 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
421 events[0].event_type);
422 EXPECT_EQ(OK, events[0].result);
423 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
424 events[1].event_type);
425 EXPECT_EQ(OK, events[1].result);
426 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
427 events[2].event_type);
428 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result);
429 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
430 events[3].event_type);
431 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
434 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
435 EXPECT_TRUE(data()->at_read_eof());
436 EXPECT_TRUE(data()->at_write_eof());
439 TEST_P(SpdyWebSocketStreamTest, DestructionAfterExplicitClose) {
441 MockWrite writes[] = {
442 CreateMockWrite(*request_frame_.get(), 1),
443 CreateMockWrite(*message_frame_.get(), 3),
444 CreateMockWrite(*closing_frame_.get(), 5)
448 CreateMockRead(*response_frame_.get(), 2),
449 CreateMockRead(*message_frame_.get(), 4),
450 MockRead(ASYNC, ERR_IO_PENDING, 6)
453 InitSession(reads, arraysize(reads), writes, arraysize(writes));
455 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
456 delegate.SetOnReceivedHeader(
457 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
458 base::Unretained(this)));
459 delegate.SetOnReceivedData(
460 base::Bind(&SpdyWebSocketStreamTest::DoClose,
461 base::Unretained(this)));
463 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
466 GURL url("ws://example.com/echo");
467 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
471 completion_callback_.WaitForResult();
473 // SPDY stream has already been removed from the session by Close().
474 EXPECT_FALSE(session_->IsStreamActive(stream_id_));
475 websocket_stream_.reset();
477 const std::vector<SpdyWebSocketStreamEvent>& events =
478 delegate.GetSeenEvents();
479 ASSERT_EQ(5U, events.size());
481 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
482 events[0].event_type);
483 EXPECT_EQ(OK, events[0].result);
484 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
485 events[1].event_type);
486 EXPECT_EQ(OK, events[1].result);
487 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
488 events[2].event_type);
489 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result);
490 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
491 events[3].event_type);
492 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
493 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, events[4].event_type);
496 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
499 TEST_P(SpdyWebSocketStreamTest, IOPending) {
501 scoped_ptr<SpdyFrame> settings_frame(
502 spdy_util_.ConstructSpdySettings(spdy_settings_to_send_));
503 MockWrite writes[] = {
504 CreateMockWrite(*request_frame_.get(), 1),
505 CreateMockWrite(*message_frame_.get(), 3),
506 CreateMockWrite(*closing_frame_.get(), 5)
510 CreateMockRead(*settings_frame.get(), 0),
511 CreateMockRead(*response_frame_.get(), 2),
512 CreateMockRead(*message_frame_.get(), 4),
513 CreateMockRead(*closing_frame_.get(), 6),
514 MockRead(SYNCHRONOUS, 0, 7) // EOF cause OnCloseSpdyStream event.
517 DeterministicSocketData data(reads, arraysize(reads),
518 writes, arraysize(writes));
519 session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
521 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_);
523 session_ = CreateInsecureSpdySession(
524 http_session_, spdy_session_key_, BoundNetLog());
526 // Create a dummy WebSocketStream which cause ERR_IO_PENDING to another
527 // WebSocketStream under test.
528 SpdyWebSocketStreamEventRecorder block_delegate((CompletionCallback()));
530 scoped_ptr<SpdyWebSocketStream> block_stream(
531 new SpdyWebSocketStream(session_, &block_delegate));
532 BoundNetLog block_net_log;
533 GURL block_url("ws://example.com/block");
535 block_stream->InitializeStream(block_url, HIGHEST, block_net_log));
539 // Create a WebSocketStream under test.
540 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
541 delegate.SetOnCreated(
542 base::Bind(&SpdyWebSocketStreamTest::DoSync,
543 base::Unretained(this)));
544 delegate.SetOnReceivedHeader(
545 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
546 base::Unretained(this)));
547 delegate.SetOnReceivedData(
548 base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame,
549 base::Unretained(this)));
551 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
553 GURL url("ws://example.com/echo");
554 ASSERT_EQ(ERR_IO_PENDING, websocket_stream_->InitializeStream(
555 url, HIGHEST, net_log));
557 // Delete the fist stream to allow create the second stream.
558 block_stream.reset();
559 ASSERT_EQ(OK, sync_callback_.WaitForResult());
564 completion_callback_.WaitForResult();
566 websocket_stream_.reset();
568 const std::vector<SpdyWebSocketStreamEvent>& block_events =
569 block_delegate.GetSeenEvents();
570 ASSERT_EQ(0U, block_events.size());
572 const std::vector<SpdyWebSocketStreamEvent>& events =
573 delegate.GetSeenEvents();
574 ASSERT_EQ(8U, events.size());
575 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED,
576 events[0].event_type);
577 EXPECT_EQ(0, events[0].result);
578 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
579 events[1].event_type);
580 EXPECT_EQ(OK, events[1].result);
581 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
582 events[2].event_type);
583 EXPECT_EQ(OK, events[2].result);
584 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
585 events[3].event_type);
586 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
587 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
588 events[4].event_type);
589 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[4].result);
590 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
591 events[5].event_type);
592 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result);
593 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
594 events[6].event_type);
595 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[6].result);
596 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
597 events[7].event_type);
598 EXPECT_EQ(OK, events[7].result);
600 // EOF close SPDY session.
602 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
603 EXPECT_TRUE(data.at_read_eof());
604 EXPECT_TRUE(data.at_write_eof());