| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/spdy/spdy_http_utils.h" | |
| 15 #include "net/spdy/spdy_protocol.h" | |
| 16 #include "net/spdy/spdy_session.h" | |
| 17 #include "net/spdy/spdy_test_util.h" | |
| 18 #include "net/spdy/spdy_websocket_test_util.h" | |
| 19 #include "testing/gtest/include/gtest/gtest.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 struct SpdyWebSocketStreamEvent { | |
| 24 enum EventType { | |
| 25 EVENT_CREATED, | |
| 26 EVENT_SENT_HEADERS, | |
| 27 EVENT_RECEIVED_HEADER, | |
| 28 EVENT_SENT_DATA, | |
| 29 EVENT_RECEIVED_DATA, | |
| 30 EVENT_CLOSE, | |
| 31 }; | |
| 32 SpdyWebSocketStreamEvent(EventType type, | |
| 33 const spdy::SpdyHeaderBlock& headers, | |
| 34 int result, | |
| 35 const std::string& data) | |
| 36 : event_type(type), | |
| 37 headers(headers), | |
| 38 result(result), | |
| 39 data(data) {} | |
| 40 | |
| 41 EventType event_type; | |
| 42 spdy::SpdyHeaderBlock headers; | |
| 43 int result; | |
| 44 std::string data; | |
| 45 }; | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 namespace net { | |
| 50 | |
| 51 class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStream::Delegate { | |
| 52 public: | |
| 53 explicit SpdyWebSocketStreamEventRecorder(const CompletionCallback& callback) | |
| 54 : callback_(callback) {} | |
| 55 virtual ~SpdyWebSocketStreamEventRecorder() {} | |
| 56 | |
| 57 typedef base::Callback<void(SpdyWebSocketStreamEvent*)> StreamEventCallback; | |
| 58 | |
| 59 void SetOnCreated(const StreamEventCallback& callback) { | |
| 60 on_created_ = callback; | |
| 61 } | |
| 62 void SetOnSentHeaders(const StreamEventCallback& callback) { | |
| 63 on_sent_headers_ = callback; | |
| 64 } | |
| 65 void SetOnReceivedHeader( | |
| 66 const StreamEventCallback& callback) { | |
| 67 on_received_header_ = callback; | |
| 68 } | |
| 69 void SetOnSentData(const StreamEventCallback& callback) { | |
| 70 on_sent_data_ = callback; | |
| 71 } | |
| 72 void SetOnReceivedData( | |
| 73 const StreamEventCallback& callback) { | |
| 74 on_received_data_ = callback; | |
| 75 } | |
| 76 void SetOnClose(const StreamEventCallback& callback) { | |
| 77 on_close_ = callback; | |
| 78 } | |
| 79 | |
| 80 virtual void OnCreatedSpdyStream(int result) { | |
| 81 events_.push_back( | |
| 82 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED, | |
| 83 spdy::SpdyHeaderBlock(), | |
| 84 result, | |
| 85 std::string())); | |
| 86 if (!on_created_.is_null()) | |
| 87 on_created_.Run(&events_.back()); | |
| 88 } | |
| 89 virtual void OnSentSpdyHeaders(int result) { | |
| 90 events_.push_back( | |
| 91 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, | |
| 92 spdy::SpdyHeaderBlock(), | |
| 93 result, | |
| 94 std::string())); | |
| 95 if (!on_sent_data_.is_null()) | |
| 96 on_sent_data_.Run(&events_.back()); | |
| 97 } | |
| 98 virtual int OnReceivedSpdyResponseHeader( | |
| 99 const spdy::SpdyHeaderBlock& headers, int status) { | |
| 100 events_.push_back( | |
| 101 SpdyWebSocketStreamEvent( | |
| 102 SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, | |
| 103 headers, | |
| 104 status, | |
| 105 std::string())); | |
| 106 if (!on_received_header_.is_null()) | |
| 107 on_received_header_.Run(&events_.back()); | |
| 108 return status; | |
| 109 } | |
| 110 virtual void OnSentSpdyData(int amount_sent) { | |
| 111 events_.push_back( | |
| 112 SpdyWebSocketStreamEvent( | |
| 113 SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 114 spdy::SpdyHeaderBlock(), | |
| 115 amount_sent, | |
| 116 std::string())); | |
| 117 if (!on_sent_data_.is_null()) | |
| 118 on_sent_data_.Run(&events_.back()); | |
| 119 } | |
| 120 virtual void OnReceivedSpdyData(const char* data, int length) { | |
| 121 events_.push_back( | |
| 122 SpdyWebSocketStreamEvent( | |
| 123 SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 124 spdy::SpdyHeaderBlock(), | |
| 125 length, | |
| 126 std::string(data, length))); | |
| 127 if (!on_received_data_.is_null()) | |
| 128 on_received_data_.Run(&events_.back()); | |
| 129 } | |
| 130 virtual void OnCloseSpdyStream() { | |
| 131 events_.push_back( | |
| 132 SpdyWebSocketStreamEvent( | |
| 133 SpdyWebSocketStreamEvent::EVENT_CLOSE, | |
| 134 spdy::SpdyHeaderBlock(), | |
| 135 OK, | |
| 136 std::string())); | |
| 137 if (!on_close_.is_null()) | |
| 138 on_close_.Run(&events_.back()); | |
| 139 if (!callback_.is_null()) | |
| 140 callback_.Run(OK); | |
| 141 } | |
| 142 | |
| 143 const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const { | |
| 144 return events_; | |
| 145 } | |
| 146 | |
| 147 private: | |
| 148 std::vector<SpdyWebSocketStreamEvent> events_; | |
| 149 StreamEventCallback on_created_; | |
| 150 StreamEventCallback on_sent_headers_; | |
| 151 StreamEventCallback on_received_header_; | |
| 152 StreamEventCallback on_sent_data_; | |
| 153 StreamEventCallback on_received_data_; | |
| 154 StreamEventCallback on_close_; | |
| 155 CompletionCallback callback_; | |
| 156 | |
| 157 DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder); | |
| 158 }; | |
| 159 | |
| 160 class SpdyWebSocketStreamTest : public testing::Test { | |
| 161 public: | |
| 162 OrderedSocketData* data() { return data_.get(); } | |
| 163 | |
| 164 void DoSendHelloFrame(SpdyWebSocketStreamEvent* event) { | |
| 165 websocket_stream_->SendData(kMessageFrame, kMessageFrameLength); | |
| 166 } | |
| 167 | |
| 168 void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) { | |
| 169 websocket_stream_->SendData(kClosingFrame, kClosingFrameLength); | |
| 170 } | |
| 171 | |
| 172 void DoClose(SpdyWebSocketStreamEvent* event) { | |
| 173 websocket_stream_->Close(); | |
| 174 } | |
| 175 | |
| 176 void DoSync(SpdyWebSocketStreamEvent* event) { | |
| 177 sync_callback_.SetResult(OK); | |
| 178 } | |
| 179 | |
| 180 protected: | |
| 181 SpdyWebSocketStreamTest() {} | |
| 182 virtual ~SpdyWebSocketStreamTest() {} | |
| 183 | |
| 184 virtual void SetUp() { | |
| 185 EnableCompression(false); | |
| 186 SpdySession::SetSSLMode(false); | |
| 187 | |
| 188 host_port_pair_.set_host("example.com"); | |
| 189 host_port_pair_.set_port(80); | |
| 190 host_port_proxy_pair_.first = host_port_pair_; | |
| 191 host_port_proxy_pair_.second = ProxyServer::Direct(); | |
| 192 | |
| 193 const size_t max_concurrent_streams = 1; | |
| 194 spdy::SettingsFlagsAndId id(0); | |
| 195 id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS); | |
| 196 | |
| 197 id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST); | |
| 198 spdy_settings_to_set_.push_back( | |
| 199 spdy::SpdySetting(id, max_concurrent_streams)); | |
| 200 | |
| 201 id.set_flags(spdy::SETTINGS_FLAG_PERSISTED); | |
| 202 spdy_settings_to_send_.push_back( | |
| 203 spdy::SpdySetting(id, max_concurrent_streams)); | |
| 204 } | |
| 205 virtual void TearDown() { | |
| 206 MessageLoop::current()->RunAllPending(); | |
| 207 } | |
| 208 | |
| 209 void EnableCompression(bool enabled) { | |
| 210 spdy::SpdyFramer::set_enable_compression_default(enabled); | |
| 211 } | |
| 212 void Prepare(spdy::SpdyStreamId stream_id) { | |
| 213 stream_id_ = stream_id; | |
| 214 | |
| 215 const char* const request_headers[] = { | |
| 216 "url", "ws://example.com/echo", | |
| 217 "origin", "http://example.com/wsdemo", | |
| 218 }; | |
| 219 | |
| 220 int request_header_count = arraysize(request_headers) / 2; | |
| 221 | |
| 222 const char* const response_headers[] = { | |
| 223 "sec-websocket-location", "ws://example.com/echo", | |
| 224 "sec-websocket-origin", "http://example.com/wsdemo", | |
| 225 }; | |
| 226 | |
| 227 int response_header_count = arraysize(response_headers) / 2; | |
| 228 | |
| 229 request_frame_.reset(ConstructSpdyWebSocketHandshakeRequestFrame( | |
| 230 request_headers, | |
| 231 request_header_count, | |
| 232 stream_id_, | |
| 233 HIGHEST)); | |
| 234 response_frame_.reset(ConstructSpdyWebSocketHandshakeResponseFrame( | |
| 235 response_headers, | |
| 236 response_header_count, | |
| 237 stream_id_, | |
| 238 HIGHEST)); | |
| 239 | |
| 240 message_frame_.reset(ConstructSpdyWebSocketDataFrame( | |
| 241 kMessageFrame, | |
| 242 kMessageFrameLength, | |
| 243 stream_id_, | |
| 244 false)); | |
| 245 | |
| 246 closing_frame_.reset(ConstructSpdyWebSocketDataFrame( | |
| 247 kClosingFrame, | |
| 248 kClosingFrameLength, | |
| 249 stream_id_, | |
| 250 false)); | |
| 251 } | |
| 252 int InitSession(MockRead* reads, size_t reads_count, | |
| 253 MockWrite* writes, size_t writes_count, | |
| 254 bool throttling) { | |
| 255 data_.reset(new OrderedSocketData(reads, reads_count, | |
| 256 writes, writes_count)); | |
| 257 session_deps_.socket_factory->AddSocketDataProvider(data_.get()); | |
| 258 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); | |
| 259 SpdySessionPool* spdy_session_pool(http_session_->spdy_session_pool()); | |
| 260 | |
| 261 if (throttling) { | |
| 262 // Set max concurrent streams to 1. | |
| 263 spdy_session_pool->http_server_properties()->SetSpdySettings( | |
| 264 host_port_pair_, spdy_settings_to_set_); | |
| 265 } | |
| 266 | |
| 267 EXPECT_FALSE(spdy_session_pool->HasSession(host_port_proxy_pair_)); | |
| 268 session_ = spdy_session_pool->Get(host_port_proxy_pair_, BoundNetLog()); | |
| 269 EXPECT_TRUE(spdy_session_pool->HasSession(host_port_proxy_pair_)); | |
| 270 transport_params_ = new TransportSocketParams(host_port_pair_, MEDIUM, | |
| 271 false, false); | |
| 272 TestCompletionCallback callback; | |
| 273 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); | |
| 274 EXPECT_EQ(ERR_IO_PENDING, | |
| 275 connection->Init(host_port_pair_.ToString(), transport_params_, | |
| 276 MEDIUM, callback.callback(), | |
| 277 http_session_->GetTransportSocketPool(), | |
| 278 BoundNetLog())); | |
| 279 EXPECT_EQ(OK, callback.WaitForResult()); | |
| 280 return session_->InitializeWithSocket(connection.release(), false, OK); | |
| 281 } | |
| 282 void SendRequest() { | |
| 283 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); | |
| 284 (*headers)["url"] = "ws://example.com/echo"; | |
| 285 (*headers)["origin"] = "http://example.com/wsdemo"; | |
| 286 | |
| 287 websocket_stream_->SendRequest(headers); | |
| 288 } | |
| 289 | |
| 290 spdy::SpdySettings spdy_settings_to_set_; | |
| 291 spdy::SpdySettings spdy_settings_to_send_; | |
| 292 SpdySessionDependencies session_deps_; | |
| 293 scoped_ptr<OrderedSocketData> data_; | |
| 294 scoped_refptr<HttpNetworkSession> http_session_; | |
| 295 scoped_refptr<SpdySession> session_; | |
| 296 scoped_refptr<TransportSocketParams> transport_params_; | |
| 297 scoped_ptr<SpdyWebSocketStream> websocket_stream_; | |
| 298 spdy::SpdyStreamId stream_id_; | |
| 299 scoped_ptr<spdy::SpdyFrame> request_frame_; | |
| 300 scoped_ptr<spdy::SpdyFrame> response_frame_; | |
| 301 scoped_ptr<spdy::SpdyFrame> message_frame_; | |
| 302 scoped_ptr<spdy::SpdyFrame> closing_frame_; | |
| 303 HostPortPair host_port_pair_; | |
| 304 HostPortProxyPair host_port_proxy_pair_; | |
| 305 TestCompletionCallback completion_callback_; | |
| 306 TestCompletionCallback sync_callback_; | |
| 307 | |
| 308 static const char kMessageFrame[]; | |
| 309 static const char kClosingFrame[]; | |
| 310 static const size_t kMessageFrameLength; | |
| 311 static const size_t kClosingFrameLength; | |
| 312 }; | |
| 313 | |
| 314 const char SpdyWebSocketStreamTest::kMessageFrame[] = "\0hello\xff"; | |
| 315 const char SpdyWebSocketStreamTest::kClosingFrame[] = "\xff\0"; | |
| 316 const size_t SpdyWebSocketStreamTest::kMessageFrameLength = | |
| 317 arraysize(SpdyWebSocketStreamTest::kMessageFrame) - 1; | |
| 318 const size_t SpdyWebSocketStreamTest::kClosingFrameLength = | |
| 319 arraysize(SpdyWebSocketStreamTest::kClosingFrame) - 1; | |
| 320 | |
| 321 TEST_F(SpdyWebSocketStreamTest, Basic) { | |
| 322 Prepare(1); | |
| 323 MockWrite writes[] = { | |
| 324 CreateMockWrite(*request_frame_.get(), 1), | |
| 325 CreateMockWrite(*message_frame_.get(), 3), | |
| 326 CreateMockWrite(*closing_frame_.get(), 5) | |
| 327 }; | |
| 328 | |
| 329 MockRead reads[] = { | |
| 330 CreateMockRead(*response_frame_.get(), 2), | |
| 331 CreateMockRead(*message_frame_.get(), 4), | |
| 332 // Skip sequence 6 to notify closing has been sent. | |
| 333 CreateMockRead(*closing_frame_.get(), 7), | |
| 334 MockRead(SYNCHRONOUS, 0, 8) // EOF cause OnCloseSpdyStream event. | |
| 335 }; | |
| 336 | |
| 337 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), | |
| 338 writes, arraysize(writes), false)); | |
| 339 | |
| 340 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); | |
| 341 delegate.SetOnReceivedHeader( | |
| 342 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, | |
| 343 base::Unretained(this))); | |
| 344 delegate.SetOnReceivedData( | |
| 345 base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, | |
| 346 base::Unretained(this))); | |
| 347 | |
| 348 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); | |
| 349 | |
| 350 BoundNetLog net_log; | |
| 351 GURL url("ws://example.com/echo"); | |
| 352 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); | |
| 353 | |
| 354 ASSERT_TRUE(websocket_stream_->stream_); | |
| 355 EXPECT_EQ(stream_id_, websocket_stream_->stream_->stream_id()); | |
| 356 | |
| 357 SendRequest(); | |
| 358 | |
| 359 completion_callback_.WaitForResult(); | |
| 360 | |
| 361 websocket_stream_.reset(); | |
| 362 | |
| 363 const std::vector<SpdyWebSocketStreamEvent>& events = | |
| 364 delegate.GetSeenEvents(); | |
| 365 ASSERT_EQ(7U, events.size()); | |
| 366 | |
| 367 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, | |
| 368 events[0].event_type); | |
| 369 EXPECT_LT(0, events[0].result); | |
| 370 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, | |
| 371 events[1].event_type); | |
| 372 EXPECT_EQ(OK, events[1].result); | |
| 373 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 374 events[2].event_type); | |
| 375 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); | |
| 376 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 377 events[3].event_type); | |
| 378 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); | |
| 379 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 380 events[4].event_type); | |
| 381 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[4].result); | |
| 382 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 383 events[5].event_type); | |
| 384 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result); | |
| 385 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, | |
| 386 events[6].event_type); | |
| 387 EXPECT_EQ(OK, events[6].result); | |
| 388 | |
| 389 // EOF close SPDY session. | |
| 390 EXPECT_TRUE(!http_session_->spdy_session_pool()->HasSession( | |
| 391 host_port_proxy_pair_)); | |
| 392 EXPECT_TRUE(data()->at_read_eof()); | |
| 393 EXPECT_TRUE(data()->at_write_eof()); | |
| 394 } | |
| 395 | |
| 396 TEST_F(SpdyWebSocketStreamTest, DestructionBeforeClose) { | |
| 397 Prepare(1); | |
| 398 MockWrite writes[] = { | |
| 399 CreateMockWrite(*request_frame_.get(), 1), | |
| 400 CreateMockWrite(*message_frame_.get(), 3) | |
| 401 }; | |
| 402 | |
| 403 MockRead reads[] = { | |
| 404 CreateMockRead(*response_frame_.get(), 2), | |
| 405 CreateMockRead(*message_frame_.get(), 4), | |
| 406 MockRead(ASYNC, ERR_IO_PENDING, 5) | |
| 407 }; | |
| 408 | |
| 409 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), | |
| 410 writes, arraysize(writes), false)); | |
| 411 | |
| 412 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); | |
| 413 delegate.SetOnReceivedHeader( | |
| 414 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, | |
| 415 base::Unretained(this))); | |
| 416 delegate.SetOnReceivedData( | |
| 417 base::Bind(&SpdyWebSocketStreamTest::DoSync, base::Unretained(this))); | |
| 418 | |
| 419 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); | |
| 420 | |
| 421 BoundNetLog net_log; | |
| 422 GURL url("ws://example.com/echo"); | |
| 423 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); | |
| 424 | |
| 425 SendRequest(); | |
| 426 | |
| 427 sync_callback_.WaitForResult(); | |
| 428 | |
| 429 // WebSocketStream destruction remove its SPDY stream from the session. | |
| 430 EXPECT_TRUE(session_->IsStreamActive(stream_id_)); | |
| 431 websocket_stream_.reset(); | |
| 432 EXPECT_FALSE(session_->IsStreamActive(stream_id_)); | |
| 433 | |
| 434 const std::vector<SpdyWebSocketStreamEvent>& events = | |
| 435 delegate.GetSeenEvents(); | |
| 436 ASSERT_GE(4U, events.size()); | |
| 437 | |
| 438 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, | |
| 439 events[0].event_type); | |
| 440 EXPECT_LT(0, events[0].result); | |
| 441 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, | |
| 442 events[1].event_type); | |
| 443 EXPECT_EQ(OK, events[1].result); | |
| 444 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 445 events[2].event_type); | |
| 446 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); | |
| 447 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 448 events[3].event_type); | |
| 449 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); | |
| 450 | |
| 451 EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession( | |
| 452 host_port_proxy_pair_)); | |
| 453 EXPECT_TRUE(data()->at_read_eof()); | |
| 454 EXPECT_TRUE(data()->at_write_eof()); | |
| 455 } | |
| 456 | |
| 457 TEST_F(SpdyWebSocketStreamTest, DestructionAfterExplicitClose) { | |
| 458 Prepare(1); | |
| 459 MockWrite writes[] = { | |
| 460 CreateMockWrite(*request_frame_.get(), 1), | |
| 461 CreateMockWrite(*message_frame_.get(), 3), | |
| 462 CreateMockWrite(*closing_frame_.get(), 5) | |
| 463 }; | |
| 464 | |
| 465 MockRead reads[] = { | |
| 466 CreateMockRead(*response_frame_.get(), 2), | |
| 467 CreateMockRead(*message_frame_.get(), 4), | |
| 468 MockRead(ASYNC, ERR_IO_PENDING, 6) | |
| 469 }; | |
| 470 | |
| 471 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), | |
| 472 writes, arraysize(writes), false)); | |
| 473 | |
| 474 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); | |
| 475 delegate.SetOnReceivedHeader( | |
| 476 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, | |
| 477 base::Unretained(this))); | |
| 478 delegate.SetOnReceivedData( | |
| 479 base::Bind(&SpdyWebSocketStreamTest::DoClose, base::Unretained(this))); | |
| 480 | |
| 481 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); | |
| 482 | |
| 483 BoundNetLog net_log; | |
| 484 GURL url("ws://example.com/echo"); | |
| 485 ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); | |
| 486 | |
| 487 SendRequest(); | |
| 488 | |
| 489 completion_callback_.WaitForResult(); | |
| 490 | |
| 491 // SPDY stream has already been removed from the session by Close(). | |
| 492 EXPECT_FALSE(session_->IsStreamActive(stream_id_)); | |
| 493 websocket_stream_.reset(); | |
| 494 | |
| 495 const std::vector<SpdyWebSocketStreamEvent>& events = | |
| 496 delegate.GetSeenEvents(); | |
| 497 ASSERT_EQ(5U, events.size()); | |
| 498 | |
| 499 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, | |
| 500 events[0].event_type); | |
| 501 EXPECT_LT(0, events[0].result); | |
| 502 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, | |
| 503 events[1].event_type); | |
| 504 EXPECT_EQ(OK, events[1].result); | |
| 505 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 506 events[2].event_type); | |
| 507 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); | |
| 508 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 509 events[3].event_type); | |
| 510 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); | |
| 511 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, events[4].event_type); | |
| 512 | |
| 513 EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession( | |
| 514 host_port_proxy_pair_)); | |
| 515 } | |
| 516 | |
| 517 TEST_F(SpdyWebSocketStreamTest, IOPending) { | |
| 518 Prepare(3); | |
| 519 scoped_ptr<spdy::SpdyFrame> settings_frame( | |
| 520 ConstructSpdySettings(spdy_settings_to_send_)); | |
| 521 MockWrite writes[] = { | |
| 522 // Setting throttling make SpdySession send settings frame automatically. | |
| 523 CreateMockWrite(*settings_frame.get(), 1), | |
| 524 CreateMockWrite(*request_frame_.get(), 3), | |
| 525 CreateMockWrite(*message_frame_.get(), 6), | |
| 526 CreateMockWrite(*closing_frame_.get(), 9) | |
| 527 }; | |
| 528 | |
| 529 MockRead reads[] = { | |
| 530 CreateMockRead(*settings_frame.get(), 2), | |
| 531 CreateMockRead(*response_frame_.get(), 4), | |
| 532 // Skip sequence 5 (I/O Pending) | |
| 533 CreateMockRead(*message_frame_.get(), 7), | |
| 534 // Skip sequence 8 (I/O Pending) | |
| 535 CreateMockRead(*closing_frame_.get(), 10), | |
| 536 MockRead(SYNCHRONOUS, 0, 11) // EOF cause OnCloseSpdyStream event. | |
| 537 }; | |
| 538 | |
| 539 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), | |
| 540 writes, arraysize(writes), true)); | |
| 541 | |
| 542 // Create a dummy WebSocketStream which cause ERR_IO_PENDING to another | |
| 543 // WebSocketStream under test. | |
| 544 SpdyWebSocketStreamEventRecorder block_delegate((CompletionCallback())); | |
| 545 | |
| 546 scoped_ptr<SpdyWebSocketStream> block_stream( | |
| 547 new SpdyWebSocketStream(session_, &block_delegate)); | |
| 548 BoundNetLog block_net_log; | |
| 549 GURL block_url("ws://example.com/block"); | |
| 550 ASSERT_EQ(OK, | |
| 551 block_stream->InitializeStream(block_url, HIGHEST, block_net_log)); | |
| 552 | |
| 553 // Create a WebSocketStream under test. | |
| 554 SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); | |
| 555 delegate.SetOnCreated( | |
| 556 base::Bind(&SpdyWebSocketStreamTest::DoSync, base::Unretained(this))); | |
| 557 delegate.SetOnReceivedHeader( | |
| 558 base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, | |
| 559 base::Unretained(this))); | |
| 560 delegate.SetOnReceivedData( | |
| 561 base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, | |
| 562 base::Unretained(this))); | |
| 563 | |
| 564 websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); | |
| 565 BoundNetLog net_log; | |
| 566 GURL url("ws://example.com/echo"); | |
| 567 ASSERT_EQ(ERR_IO_PENDING, websocket_stream_->InitializeStream( | |
| 568 url, HIGHEST, net_log)); | |
| 569 | |
| 570 // Delete the fist stream to allow create the second stream. | |
| 571 block_stream.reset(); | |
| 572 ASSERT_EQ(OK, sync_callback_.WaitForResult()); | |
| 573 | |
| 574 SendRequest(); | |
| 575 | |
| 576 completion_callback_.WaitForResult(); | |
| 577 | |
| 578 websocket_stream_.reset(); | |
| 579 | |
| 580 const std::vector<SpdyWebSocketStreamEvent>& block_events = | |
| 581 block_delegate.GetSeenEvents(); | |
| 582 ASSERT_EQ(0U, block_events.size()); | |
| 583 | |
| 584 const std::vector<SpdyWebSocketStreamEvent>& events = | |
| 585 delegate.GetSeenEvents(); | |
| 586 ASSERT_EQ(8U, events.size()); | |
| 587 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED, | |
| 588 events[0].event_type); | |
| 589 EXPECT_EQ(0, events[0].result); | |
| 590 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, | |
| 591 events[1].event_type); | |
| 592 EXPECT_LT(0, events[1].result); | |
| 593 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, | |
| 594 events[2].event_type); | |
| 595 EXPECT_EQ(OK, events[2].result); | |
| 596 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 597 events[3].event_type); | |
| 598 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); | |
| 599 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 600 events[4].event_type); | |
| 601 EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[4].result); | |
| 602 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, | |
| 603 events[5].event_type); | |
| 604 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result); | |
| 605 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, | |
| 606 events[6].event_type); | |
| 607 EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[6].result); | |
| 608 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, | |
| 609 events[7].event_type); | |
| 610 EXPECT_EQ(OK, events[7].result); | |
| 611 | |
| 612 // EOF close SPDY session. | |
| 613 EXPECT_TRUE(!http_session_->spdy_session_pool()->HasSession( | |
| 614 host_port_proxy_pair_)); | |
| 615 EXPECT_TRUE(data()->at_read_eof()); | |
| 616 EXPECT_TRUE(data()->at_write_eof()); | |
| 617 } | |
| 618 | |
| 619 } // namespace net | |
| OLD | NEW |