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(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, 291 kProtoHTTP2Draft04)); 292 293 // TODO(toyoshim): Replace old framing data to new one, then use HEADERS and 294 // data frames. 295 const char SpdyWebSocketStreamTest::kMessageFrame[] = "\x81\x05hello"; 296 const char SpdyWebSocketStreamTest::kClosingFrame[] = "\x88\0"; 297 const size_t SpdyWebSocketStreamTest::kMessageFrameLength = 298 arraysize(SpdyWebSocketStreamTest::kMessageFrame) - 1; 299 const size_t SpdyWebSocketStreamTest::kClosingFrameLength = 300 arraysize(SpdyWebSocketStreamTest::kClosingFrame) - 1; 301 302 TEST_P(SpdyWebSocketStreamTest, Basic) { 303 Prepare(1); 304 MockWrite writes[] = { 305 CreateMockWrite(*request_frame_.get(), 1), 306 CreateMockWrite(*message_frame_.get(), 3), 307 CreateMockWrite(*closing_frame_.get(), 5) 308 }; 309 310 MockRead reads[] = { 311 CreateMockRead(*response_frame_.get(), 2), 312 CreateMockRead(*message_frame_.get(), 4), 313 // Skip sequence 6 to notify closing has been sent. 314 CreateMockRead(*closing_frame_.get(), 7), 315 MockRead(SYNCHRONOUS, 0, 8) // EOF cause OnCloseSpdyStream event. 316 }; 317 318 InitSession(reads, arraysize(reads), writes, arraysize(writes)); 319 320 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); 321 delegate.SetOnReceivedHeader( 322 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, 323 base::Unretained(this))); 324 delegate.SetOnReceivedData( 325 base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, 326 base::Unretained(this))); 327 328 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 329 330 BoundNetLog net_log; 331 GURL url("ws://example.com/echo"); 332 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); 333 334 ASSERT_TRUE(websocket_stream_->stream_.get()); 335 336 SendRequest(); 337 338 completion_callback_.WaitForResult(); 339 340 EXPECT_EQ(stream_id_, created_stream_id_); 341 342 websocket_stream_.reset(); 343 344 const std::vector<SpdyWebSocketStreamEvent>& events = 345 delegate.GetSeenEvents(); 346 ASSERT_EQ(7U, events.size()); 347 348 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, 349 events[0].event_type); 350 EXPECT_EQ(OK, events[0].result); 351 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, 352 events[1].event_type); 353 EXPECT_EQ(OK, events[1].result); 354 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 355 events[2].event_type); 356 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); 357 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 358 events[3].event_type); 359 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); 360 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 361 events[4].event_type); 362 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[4].result); 363 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 364 events[5].event_type); 365 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result); 366 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, 367 events[6].event_type); 368 EXPECT_EQ(OK, events[6].result); 369 370 // EOF close SPDY session. 371 EXPECT_FALSE( 372 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 373 EXPECT_TRUE(data()->at_read_eof()); 374 EXPECT_TRUE(data()->at_write_eof()); 375 } 376 377 TEST_P(SpdyWebSocketStreamTest, DestructionBeforeClose) { 378 Prepare(1); 379 MockWrite writes[] = { 380 CreateMockWrite(*request_frame_.get(), 1), 381 CreateMockWrite(*message_frame_.get(), 3) 382 }; 383 384 MockRead reads[] = { 385 CreateMockRead(*response_frame_.get(), 2), 386 CreateMockRead(*message_frame_.get(), 4), 387 MockRead(ASYNC, ERR_IO_PENDING, 5) 388 }; 389 390 InitSession(reads, arraysize(reads), writes, arraysize(writes)); 391 392 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); 393 delegate.SetOnReceivedHeader( 394 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, 395 base::Unretained(this))); 396 delegate.SetOnReceivedData( 397 base::Bind(&SpdyWebSocketStreamTest::DoSync, 398 base::Unretained(this))); 399 400 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 401 402 BoundNetLog net_log; 403 GURL url("ws://example.com/echo"); 404 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); 405 406 SendRequest(); 407 408 sync_callback_.WaitForResult(); 409 410 // WebSocketStream destruction remove its SPDY stream from the session. 411 EXPECT_TRUE(session_->IsStreamActive(stream_id_)); 412 websocket_stream_.reset(); 413 EXPECT_FALSE(session_->IsStreamActive(stream_id_)); 414 415 const std::vector<SpdyWebSocketStreamEvent>& events = 416 delegate.GetSeenEvents(); 417 ASSERT_GE(4U, events.size()); 418 419 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, 420 events[0].event_type); 421 EXPECT_EQ(OK, events[0].result); 422 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, 423 events[1].event_type); 424 EXPECT_EQ(OK, events[1].result); 425 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 426 events[2].event_type); 427 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); 428 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 429 events[3].event_type); 430 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); 431 432 EXPECT_TRUE( 433 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 434 EXPECT_TRUE(data()->at_read_eof()); 435 EXPECT_TRUE(data()->at_write_eof()); 436 } 437 438 TEST_P(SpdyWebSocketStreamTest, DestructionAfterExplicitClose) { 439 Prepare(1); 440 MockWrite writes[] = { 441 CreateMockWrite(*request_frame_.get(), 1), 442 CreateMockWrite(*message_frame_.get(), 3), 443 CreateMockWrite(*closing_frame_.get(), 5) 444 }; 445 446 MockRead reads[] = { 447 CreateMockRead(*response_frame_.get(), 2), 448 CreateMockRead(*message_frame_.get(), 4), 449 MockRead(ASYNC, ERR_IO_PENDING, 6) 450 }; 451 452 InitSession(reads, arraysize(reads), writes, arraysize(writes)); 453 454 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); 455 delegate.SetOnReceivedHeader( 456 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, 457 base::Unretained(this))); 458 delegate.SetOnReceivedData( 459 base::Bind(&SpdyWebSocketStreamTest::DoClose, 460 base::Unretained(this))); 461 462 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 463 464 BoundNetLog net_log; 465 GURL url("ws://example.com/echo"); 466 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); 467 468 SendRequest(); 469 470 completion_callback_.WaitForResult(); 471 472 // SPDY stream has already been removed from the session by Close(). 473 EXPECT_FALSE(session_->IsStreamActive(stream_id_)); 474 websocket_stream_.reset(); 475 476 const std::vector<SpdyWebSocketStreamEvent>& events = 477 delegate.GetSeenEvents(); 478 ASSERT_EQ(5U, events.size()); 479 480 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, 481 events[0].event_type); 482 EXPECT_EQ(OK, events[0].result); 483 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, 484 events[1].event_type); 485 EXPECT_EQ(OK, events[1].result); 486 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 487 events[2].event_type); 488 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); 489 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 490 events[3].event_type); 491 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); 492 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, events[4].event_type); 493 494 EXPECT_TRUE( 495 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 496 } 497 498 TEST_P(SpdyWebSocketStreamTest, IOPending) { 499 Prepare(1); 500 scoped_ptr<SpdyFrame> settings_frame( 501 spdy_util_.ConstructSpdySettings(spdy_settings_to_send_)); 502 MockWrite writes[] = { 503 CreateMockWrite(*request_frame_.get(), 1), 504 CreateMockWrite(*message_frame_.get(), 3), 505 CreateMockWrite(*closing_frame_.get(), 5) 506 }; 507 508 MockRead reads[] = { 509 CreateMockRead(*settings_frame.get(), 0), 510 CreateMockRead(*response_frame_.get(), 2), 511 CreateMockRead(*message_frame_.get(), 4), 512 CreateMockRead(*closing_frame_.get(), 6), 513 MockRead(SYNCHRONOUS, 0, 7) // EOF cause OnCloseSpdyStream event. 514 }; 515 516 DeterministicSocketData data(reads, arraysize(reads), 517 writes, arraysize(writes)); 518 session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); 519 http_session_ = 520 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_); 521 522 session_ = CreateInsecureSpdySession( 523 http_session_, spdy_session_key_, BoundNetLog()); 524 525 // Create a dummy WebSocketStream which cause ERR_IO_PENDING to another 526 // WebSocketStream under test. 527 SpdyWebSocketStreamEventRecorder block_delegate((CompletionCallback())); 528 529 scoped_ptr<SpdyWebSocketStream> block_stream( 530 new SpdyWebSocketStream(session_, &block_delegate)); 531 BoundNetLog block_net_log; 532 GURL block_url("ws://example.com/block"); 533 ASSERT_EQ(OK, 534 block_stream->InitializeStream(block_url, HIGHEST, block_net_log)); 535 536 data.RunFor(1); 537 538 // Create a WebSocketStream under test. 539 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); 540 delegate.SetOnCreated( 541 base::Bind(&SpdyWebSocketStreamTest::DoSync, 542 base::Unretained(this))); 543 delegate.SetOnReceivedHeader( 544 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, 545 base::Unretained(this))); 546 delegate.SetOnReceivedData( 547 base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, 548 base::Unretained(this))); 549 550 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); 551 BoundNetLog net_log; 552 GURL url("ws://example.com/echo"); 553 ASSERT_EQ(ERR_IO_PENDING, websocket_stream_->InitializeStream( 554 url, HIGHEST, net_log)); 555 556 // Delete the fist stream to allow create the second stream. 557 block_stream.reset(); 558 ASSERT_EQ(OK, sync_callback_.WaitForResult()); 559 560 SendRequest(); 561 562 data.RunFor(7); 563 completion_callback_.WaitForResult(); 564 565 websocket_stream_.reset(); 566 567 const std::vector<SpdyWebSocketStreamEvent>& block_events = 568 block_delegate.GetSeenEvents(); 569 ASSERT_EQ(0U, block_events.size()); 570 571 const std::vector<SpdyWebSocketStreamEvent>& events = 572 delegate.GetSeenEvents(); 573 ASSERT_EQ(8U, events.size()); 574 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED, 575 events[0].event_type); 576 EXPECT_EQ(0, events[0].result); 577 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, 578 events[1].event_type); 579 EXPECT_EQ(OK, events[1].result); 580 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, 581 events[2].event_type); 582 EXPECT_EQ(OK, events[2].result); 583 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 584 events[3].event_type); 585 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); 586 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 587 events[4].event_type); 588 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[4].result); 589 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, 590 events[5].event_type); 591 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result); 592 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, 593 events[6].event_type); 594 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[6].result); 595 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, 596 events[7].event_type); 597 EXPECT_EQ(OK, events[7].result); 598 599 // EOF close SPDY session. 600 EXPECT_FALSE( 601 HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); 602 EXPECT_TRUE(data.at_read_eof()); 603 EXPECT_TRUE(data.at_write_eof()); 604 } 605 606 } // namespace net 607