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 |