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/spdy/spdy_websocket_stream.h" 6 7 #include <string> 8 #include <vector> 9 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" 21 22 namespace net { 23 24 namespace { 25 26 struct SpdyWebSocketStreamEvent { 27 enum EventType { 28 EVENT_CREATED, 29 EVENT_SENT_HEADERS, 30 EVENT_RECEIVED_HEADER, 31 EVENT_SENT_DATA, 32 EVENT_RECEIVED_DATA, 33 EVENT_CLOSE, 34 }; 35 SpdyWebSocketStreamEvent(EventType type, 36 const SpdyHeaderBlock& headers, 37 int result, 38 const std::string& data) 39 : event_type(type), 40 headers(headers), 41 result(result), 42 data(data) {} 43 44 EventType event_type; 45 SpdyHeaderBlock headers; 46 int result; 47 std::string data; 48 }; 49 50 class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStream::Delegate { 51 public: 52 explicit SpdyWebSocketStreamEventRecorder(const CompletionCallback& callback) 53 : callback_(callback) {} 54 virtual ~SpdyWebSocketStreamEventRecorder() {} 55 56 typedef base::Callback<void(SpdyWebSocketStreamEvent*)> StreamEventCallback; 57 58 void SetOnCreated(const StreamEventCallback& callback) { 59 on_created_ = callback; 60 } 61 void SetOnSentHeaders(const StreamEventCallback& callback) { 62 on_sent_headers_ = callback; 63 } 64 void SetOnReceivedHeader(const StreamEventCallback& callback) { 65 on_received_header_ = callback; 66 } 67 void SetOnSentData(const StreamEventCallback& callback) { 68 on_sent_data_ = callback; 69 } 70 void SetOnReceivedData(const StreamEventCallback& callback) { 71 on_received_data_ = callback; 72 } 73 void SetOnClose(const StreamEventCallback& callback) { 74 on_close_ = callback; 75 } 76 77 virtual void OnCreatedSpdyStream(int result) OVERRIDE { 78 events_.push_back( 79 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED, 80 SpdyHeaderBlock(), 81 result, 82 std::string())); 83 if (!on_created_.is_null()) 84 on_created_.Run(&events_.back()); 85 } 86 virtual void OnSentSpdyHeaders() OVERRIDE { 87 events_.push_back( 88 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, 89 SpdyHeaderBlock(), 90 OK, 91 std::string())); 92 if (!on_sent_data_.is_null()) 93 on_sent_data_.Run(&events_.back()); 94 } 95 virtual void OnSpdyResponseHeadersUpdated( 96 const SpdyHeaderBlock& response_headers) OVERRIDE { 97 events_.push_back( 98 SpdyWebSocketStreamEvent( 99 SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, 100 response_headers, 101 OK, 102 std::string())); 103 if (!on_received_header_.is_null()) 104 on_received_header_.Run(&events_.back()); 105 } 106 virtual void OnSentSpdyData(size_t bytes_sent) OVERRIDE { 107 events_.push_back( 108 SpdyWebSocketStreamEvent( 109 SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 110 SpdyHeaderBlock(), 111 static_cast<int>(bytes_sent), 112 std::string())); 113 if (!on_sent_data_.is_null()) 114 on_sent_data_.Run(&events_.back()); 115 } 116 virtual void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) OVERRIDE { 117 std::string buffer_data; 118 size_t buffer_len = 0; 119 if (buffer) { 120 buffer_len = buffer->GetRemainingSize(); 121 buffer_data.append(buffer->GetRemainingData(), buffer_len); 122 } 123 events_.push_back( 124 SpdyWebSocketStreamEvent( 125 SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 126 SpdyHeaderBlock(), 127 buffer_len, 128 buffer_data)); 129 if (!on_received_data_.is_null()) 130 on_received_data_.Run(&events_.back()); 131 } 132 virtual void OnCloseSpdyStream() OVERRIDE { 133 events_.push_back( 134 SpdyWebSocketStreamEvent( 135 SpdyWebSocketStreamEvent::EVENT_CLOSE, 136 SpdyHeaderBlock(), 137 OK, 138 std::string())); 139 if (!on_close_.is_null()) 140 on_close_.Run(&events_.back()); 141 if (!callback_.is_null()) 142 callback_.Run(OK); 143 } 144 145 const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const { 146 return events_; 147 } 148 149 private: 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_; 158 159 DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder); 160 }; 161 162 } // namespace 163 164 class SpdyWebSocketStreamTest 165 : public ::testing::Test, 166 public ::testing::WithParamInterface<NextProto> { 167 public: 168 OrderedSocketData* data() { return data_.get(); } 169 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); 174 } 175 176 void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) { 177 websocket_stream_->SendData(kClosingFrame, kClosingFrameLength); 178 } 179 180 void DoClose(SpdyWebSocketStreamEvent* event) { 181 websocket_stream_->Close(); 182 } 183 184 void DoSync(SpdyWebSocketStreamEvent* event) { 185 sync_callback_.callback().Run(OK); 186 } 187 188 protected: 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()), 195 stream_id_(0), 196 created_stream_id_(0) {} 197 virtual ~SpdyWebSocketStreamTest() {} 198 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); 205 206 spdy_settings_to_send_[spdy_settings_id_to_set_] = 207 SettingsFlagsAndValue( 208 SETTINGS_FLAG_PERSISTED, spdy_settings_value_to_set_); 209 } 210 211 virtual void TearDown() { 212 base::MessageLoop::current()->RunUntilIdle(); 213 } 214 215 void Prepare(SpdyStreamId stream_id) { 216 stream_id_ = stream_id; 217 218 request_frame_.reset(spdy_util_.ConstructSpdyWebSocketSynStream( 219 stream_id_, 220 "/echo", 221 "example.com", 222 "http://example.com/wsdemo")); 223 224 response_frame_.reset( 225 spdy_util_.ConstructSpdyWebSocketSynReply(stream_id_)); 226 227 message_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame( 228 kMessageFrame, 229 kMessageFrameLength, 230 stream_id_, 231 false)); 232 233 closing_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame( 234 kClosingFrame, 235 kClosingFrameLength, 236 stream_id_, 237 false)); 238 } 239 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()); 248 } 249 250 void SendRequest() { 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()); 258 } 259 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_; 280 281 static const char kMessageFrame[]; 282 static const char kClosingFrame[]; 283 static const size_t kMessageFrameLength; 284 static const size_t kClosingFrameLength; 285 }; 286 287 INSTANTIATE_TEST_CASE_P( 288 NextProto, 289 SpdyWebSocketStreamTest, 290 testing::Values(kProtoDeprecatedSPDY2, 291 kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, 292 kProtoHTTP2Draft04)); 293 294 // TODO(toyoshim): Replace old framing data to new one, then use HEADERS and 295 // data frames. 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; 302 303 TEST_P(SpdyWebSocketStreamTest, Basic) { 304 Prepare(1); 305 MockWrite writes[] = { 306 CreateMockWrite(*request_frame_.get(), 1), 307 CreateMockWrite(*message_frame_.get(), 3), 308 CreateMockWrite(*closing_frame_.get(), 5) 309 }; 310 311 MockRead reads[] = { 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. 317 }; 318 319 InitSession(reads, arraysize(reads), writes, arraysize(writes)); 320 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))); 328 329 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 330 331 BoundNetLog net_log; 332 GURL url("ws://example.com/echo"); 333 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); 334 335 ASSERT_TRUE(websocket_stream_->stream_.get()); 336 337 SendRequest(); 338 339 completion_callback_.WaitForResult(); 340 341 EXPECT_EQ(stream_id_, created_stream_id_); 342 343 websocket_stream_.reset(); 344 345 const std::vector<SpdyWebSocketStreamEvent>& events = 346 delegate.GetSeenEvents(); 347 ASSERT_EQ(7U, events.size()); 348 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); 370 371 // EOF close SPDY session. 372 EXPECT_FALSE( 373 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 374 EXPECT_TRUE(data()->at_read_eof()); 375 EXPECT_TRUE(data()->at_write_eof()); 376 } 377 378 TEST_P(SpdyWebSocketStreamTest, DestructionBeforeClose) { 379 Prepare(1); 380 MockWrite writes[] = { 381 CreateMockWrite(*request_frame_.get(), 1), 382 CreateMockWrite(*message_frame_.get(), 3) 383 }; 384 385 MockRead reads[] = { 386 CreateMockRead(*response_frame_.get(), 2), 387 CreateMockRead(*message_frame_.get(), 4), 388 MockRead(ASYNC, ERR_IO_PENDING, 5) 389 }; 390 391 InitSession(reads, arraysize(reads), writes, arraysize(writes)); 392 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))); 400 401 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 402 403 BoundNetLog net_log; 404 GURL url("ws://example.com/echo"); 405 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); 406 407 SendRequest(); 408 409 sync_callback_.WaitForResult(); 410 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_)); 415 416 const std::vector<SpdyWebSocketStreamEvent>& events = 417 delegate.GetSeenEvents(); 418 ASSERT_GE(4U, events.size()); 419 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); 432 433 EXPECT_TRUE( 434 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 435 EXPECT_TRUE(data()->at_read_eof()); 436 EXPECT_TRUE(data()->at_write_eof()); 437 } 438 439 TEST_P(SpdyWebSocketStreamTest, DestructionAfterExplicitClose) { 440 Prepare(1); 441 MockWrite writes[] = { 442 CreateMockWrite(*request_frame_.get(), 1), 443 CreateMockWrite(*message_frame_.get(), 3), 444 CreateMockWrite(*closing_frame_.get(), 5) 445 }; 446 447 MockRead reads[] = { 448 CreateMockRead(*response_frame_.get(), 2), 449 CreateMockRead(*message_frame_.get(), 4), 450 MockRead(ASYNC, ERR_IO_PENDING, 6) 451 }; 452 453 InitSession(reads, arraysize(reads), writes, arraysize(writes)); 454 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))); 462 463 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 464 465 BoundNetLog net_log; 466 GURL url("ws://example.com/echo"); 467 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); 468 469 SendRequest(); 470 471 completion_callback_.WaitForResult(); 472 473 // SPDY stream has already been removed from the session by Close(). 474 EXPECT_FALSE(session_->IsStreamActive(stream_id_)); 475 websocket_stream_.reset(); 476 477 const std::vector<SpdyWebSocketStreamEvent>& events = 478 delegate.GetSeenEvents(); 479 ASSERT_EQ(5U, events.size()); 480 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); 494 495 EXPECT_TRUE( 496 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 497 } 498 499 TEST_P(SpdyWebSocketStreamTest, IOPending) { 500 Prepare(1); 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) 507 }; 508 509 MockRead reads[] = { 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. 515 }; 516 517 DeterministicSocketData data(reads, arraysize(reads), 518 writes, arraysize(writes)); 519 session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); 520 http_session_ = 521 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_); 522 523 session_ = CreateInsecureSpdySession( 524 http_session_, spdy_session_key_, BoundNetLog()); 525 526 // Create a dummy WebSocketStream which cause ERR_IO_PENDING to another 527 // WebSocketStream under test. 528 SpdyWebSocketStreamEventRecorder block_delegate((CompletionCallback())); 529 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"); 534 ASSERT_EQ(OK, 535 block_stream->InitializeStream(block_url, HIGHEST, block_net_log)); 536 537 data.RunFor(1); 538 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))); 550 551 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 552 BoundNetLog net_log; 553 GURL url("ws://example.com/echo"); 554 ASSERT_EQ(ERR_IO_PENDING, websocket_stream_->InitializeStream( 555 url, HIGHEST, net_log)); 556 557 // Delete the fist stream to allow create the second stream. 558 block_stream.reset(); 559 ASSERT_EQ(OK, sync_callback_.WaitForResult()); 560 561 SendRequest(); 562 563 data.RunFor(7); 564 completion_callback_.WaitForResult(); 565 566 websocket_stream_.reset(); 567 568 const std::vector<SpdyWebSocketStreamEvent>& block_events = 569 block_delegate.GetSeenEvents(); 570 ASSERT_EQ(0U, block_events.size()); 571 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); 599 600 // EOF close SPDY session. 601 EXPECT_FALSE( 602 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 603 EXPECT_TRUE(data.at_read_eof()); 604 EXPECT_TRUE(data.at_write_eof()); 605 } 606 607 } // namespace net 608