OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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/http/bidirectional_stream.h" | |
6 | |
7 #include "base/macros.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/run_loop.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 #include "base/strings/string_piece.h" | |
12 #include "base/time/time.h" | |
13 #include "base/timer/mock_timer.h" | |
14 #include "net/base/net_errors.h" | |
15 #include "net/base/test_data_directory.h" | |
16 #include "net/http/bidirectional_stream_request_info.h" | |
17 #include "net/http/http_network_session.h" | |
18 #include "net/http/http_response_headers.h" | |
19 #include "net/log/net_log.h" | |
20 #include "net/socket/socket_test_util.h" | |
21 #include "net/spdy/spdy_session.h" | |
22 #include "net/spdy/spdy_test_util_common.h" | |
23 #include "net/test/cert_test_util.h" | |
24 #include "net/url_request/url_request_test_util.h" | |
25 #include "testing/gtest/include/gtest/gtest.h" | |
26 | |
27 namespace net { | |
28 | |
29 namespace { | |
30 | |
31 const char kBodyData[] = "Body data"; | |
32 const size_t kBodyDataSize = arraysize(kBodyData); | |
33 // Size of the buffer to be allocated for each read. | |
34 const size_t kReadBufferSize = 4096; | |
35 | |
36 // Delegate that reads data but does not send any data. | |
37 class TestDelegateBase : public BidirectionalStream::Delegate { | |
38 public: | |
39 TestDelegateBase(IOBuffer* read_buf, int read_buf_len) | |
40 : TestDelegateBase(read_buf, | |
41 read_buf_len, | |
42 make_scoped_ptr(new base::Timer(false, false))) {} | |
43 | |
44 TestDelegateBase(IOBuffer* read_buf, | |
45 int read_buf_len, | |
46 scoped_ptr<base::Timer> timer) | |
47 : read_buf_(read_buf), | |
48 read_buf_len_(read_buf_len), | |
49 timer_(timer.release()), | |
50 loop_(nullptr), | |
51 error_(OK), | |
52 on_data_read_count_(0), | |
53 on_data_sent_count_(0), | |
54 do_not_start_read_(false), | |
55 run_until_completion_(false), | |
56 not_expect_callback_(false) {} | |
57 | |
58 ~TestDelegateBase() override {} | |
59 | |
60 void OnHeadersSent() override { CHECK(!not_expect_callback_); } | |
61 | |
62 void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override { | |
63 CHECK(!not_expect_callback_); | |
64 | |
65 response_headers_ = response_headers; | |
66 if (!do_not_start_read_) | |
67 StartOrContinueReading(); | |
68 } | |
69 | |
70 void OnDataRead(int bytes_read) override { | |
71 CHECK(!not_expect_callback_); | |
72 | |
73 ++on_data_read_count_; | |
74 CHECK_GE(bytes_read, OK); | |
75 data_received_.append(read_buf_->data(), bytes_read); | |
76 if (!do_not_start_read_) | |
77 StartOrContinueReading(); | |
78 } | |
79 | |
80 void OnDataSent() override { | |
81 CHECK(!not_expect_callback_); | |
82 | |
83 ++on_data_sent_count_; | |
84 } | |
85 | |
86 void OnTrailersReceived(const SpdyHeaderBlock& trailers) override { | |
87 CHECK(!not_expect_callback_); | |
88 | |
89 trailers_ = trailers; | |
90 if (run_until_completion_) | |
91 loop_->Quit(); | |
92 } | |
93 | |
94 void OnFailed(int error) override { | |
95 CHECK(!not_expect_callback_); | |
96 CHECK_EQ(OK, error_); | |
97 CHECK_NE(OK, error); | |
98 | |
99 error_ = error; | |
100 if (run_until_completion_) | |
101 loop_->Quit(); | |
102 } | |
103 | |
104 void Start(const BidirectionalStreamRequestInfo* request_info, | |
105 HttpNetworkSession* session) { | |
106 stream_.reset( | |
107 new BidirectionalStream(request_info, session, this, timer_.Pass())); | |
108 if (run_until_completion_) | |
109 loop_->Run(); | |
110 } | |
111 | |
112 void SendData(IOBuffer* data, int length, bool end_of_stream) { | |
113 not_expect_callback_ = true; | |
114 stream_->SendData(data, length, end_of_stream); | |
115 not_expect_callback_ = false; | |
116 } | |
117 | |
118 // Starts or continues reading data from |stream_| until no more bytes | |
119 // can be read synchronously. | |
120 void StartOrContinueReading() { | |
121 int rv = ReadData(); | |
122 while (rv > 0) { | |
123 rv = ReadData(); | |
124 } | |
125 if (run_until_completion_ && rv == 0) | |
126 loop_->Quit(); | |
127 } | |
128 | |
129 // Calls ReadData on the |stream_| and updates internal states. | |
130 int ReadData() { | |
131 not_expect_callback_ = true; | |
132 int rv = stream_->ReadData(read_buf_.get(), read_buf_len_); | |
133 not_expect_callback_ = false; | |
134 if (rv > 0) | |
135 data_received_.append(read_buf_->data(), rv); | |
136 return rv; | |
137 } | |
138 | |
139 // Cancels |stream_|. | |
140 void CancelStream() { stream_->Cancel(); } | |
141 | |
142 NextProto GetProtocol() const { return stream_->GetProtocol(); } | |
143 | |
144 int64_t GetTotalReceivedBytes() const { | |
145 return stream_->GetTotalReceivedBytes(); | |
146 } | |
147 | |
148 int64_t GetTotalSentBytes() const { return stream_->GetTotalSentBytes(); } | |
149 | |
150 // Const getters for internal states. | |
151 const std::string& data_received() const { return data_received_; } | |
152 int error() const { return error_; } | |
153 const SpdyHeaderBlock& response_headers() const { return response_headers_; } | |
154 const SpdyHeaderBlock& trailers() const { return trailers_; } | |
155 int on_data_read_count() const { return on_data_read_count_; } | |
156 int on_data_sent_count() const { return on_data_sent_count_; } | |
157 | |
158 // Sets whether the delegate should automatically start reading. | |
159 void set_do_not_start_read(bool do_not_start_read) { | |
160 do_not_start_read_ = do_not_start_read; | |
161 } | |
162 // Sets whether the delegate should wait until the completion of the stream. | |
163 void SetRunUntilCompletion(bool run_until_completion) { | |
164 run_until_completion_ = run_until_completion; | |
165 loop_.reset(new base::RunLoop); | |
166 } | |
167 | |
168 protected: | |
169 // Quits |loop_|. | |
170 void QuitLoop() { loop_->Quit(); } | |
171 | |
172 private: | |
173 scoped_ptr<BidirectionalStream> stream_; | |
174 scoped_refptr<IOBuffer> read_buf_; | |
175 int read_buf_len_; | |
176 scoped_ptr<base::Timer> timer_; | |
177 std::string data_received_; | |
178 scoped_ptr<base::RunLoop> loop_; | |
179 SpdyHeaderBlock response_headers_; | |
180 SpdyHeaderBlock trailers_; | |
181 int error_; | |
182 int on_data_read_count_; | |
183 int on_data_sent_count_; | |
184 bool do_not_start_read_; | |
185 bool run_until_completion_; | |
186 // This is to ensure that delegate callback is not invoked synchronously when | |
187 // calling into |stream_|. | |
188 bool not_expect_callback_; | |
189 | |
190 DISALLOW_COPY_AND_ASSIGN(TestDelegateBase); | |
191 }; | |
192 | |
193 // A delegate that cancels the request after response headers are received. | |
194 class CancelStreamDelegate : public TestDelegateBase { | |
mmenke
2015/12/16 23:04:37
A bit of a pain, but if we allow cancellation in o
xunjieli
2015/12/17 18:18:55
Done. Good idea!
| |
195 public: | |
196 CancelStreamDelegate(IOBuffer* buf, int buf_len) | |
197 : TestDelegateBase(buf, buf_len) {} | |
198 ~CancelStreamDelegate() override {} | |
199 | |
200 void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override { | |
201 TestDelegateBase::OnHeadersReceived(response_headers); | |
202 CancelStream(); | |
203 QuitLoop(); | |
204 } | |
205 | |
206 void OnDataSent() override { NOTREACHED(); } | |
207 | |
208 void OnDataRead(int bytes_read) override { NOTREACHED(); } | |
209 | |
210 void OnTrailersReceived(const SpdyHeaderBlock& trailers) override { | |
211 NOTREACHED(); | |
212 } | |
213 | |
214 void OnFailed(int error) override { NOTREACHED(); } | |
215 | |
216 private: | |
217 DISALLOW_COPY_AND_ASSIGN(CancelStreamDelegate); | |
218 }; | |
219 | |
220 // A Timer that does not start a delayed task unless the timer is fired. | |
221 class MockTimer : public base::MockTimer { | |
222 public: | |
223 MockTimer() : base::MockTimer(false, false) {} | |
224 ~MockTimer() override {} | |
225 | |
226 void Start(const tracked_objects::Location& posted_from, | |
227 base::TimeDelta delay, | |
228 const base::Closure& user_task) override { | |
229 // Sets a maximum delay, so the timer does not fire unless it is told to. | |
230 base::TimeDelta infinite_delay = base::TimeDelta::Max(); | |
231 base::MockTimer::Start(posted_from, infinite_delay, user_task); | |
232 } | |
233 | |
234 private: | |
235 DISALLOW_COPY_AND_ASSIGN(MockTimer); | |
236 }; | |
237 | |
238 } // namespace | |
239 | |
240 class BidirectionalStreamTest : public testing::Test { | |
241 public: | |
242 BidirectionalStreamTest() | |
243 : spdy_util_(kProtoHTTP2, false), | |
244 session_deps_(kProtoHTTP2), | |
245 ssl_data_(SSLSocketDataProvider(ASYNC, OK)) { | |
246 ssl_data_.SetNextProto(kProtoHTTP2); | |
247 ssl_data_.cert = ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); | |
248 } | |
249 | |
250 protected: | |
251 void TearDown() override { | |
252 if (sequenced_data_) { | |
253 EXPECT_TRUE(sequenced_data_->AllReadDataConsumed()); | |
254 EXPECT_TRUE(sequenced_data_->AllWriteDataConsumed()); | |
255 } | |
256 } | |
257 | |
258 // Initializes the session using SequencedSocketData. | |
259 void InitSession(MockRead* reads, | |
260 size_t reads_count, | |
261 MockWrite* writes, | |
262 size_t writes_count, | |
263 const SpdySessionKey& key) { | |
264 ASSERT_TRUE(ssl_data_.cert.get()); | |
265 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data_); | |
266 sequenced_data_.reset( | |
267 new SequencedSocketData(reads, reads_count, writes, writes_count)); | |
268 session_deps_.socket_factory->AddSocketDataProvider(sequenced_data_.get()); | |
269 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); | |
270 session_ = CreateSecureSpdySession(http_session_.get(), key, BoundNetLog()); | |
271 } | |
272 | |
273 SpdyTestUtil spdy_util_; | |
274 SpdySessionDependencies session_deps_; | |
275 scoped_ptr<SequencedSocketData> sequenced_data_; | |
276 scoped_ptr<HttpNetworkSession> http_session_; | |
277 | |
278 private: | |
279 SSLSocketDataProvider ssl_data_; | |
280 base::WeakPtr<SpdySession> session_; | |
281 }; | |
282 | |
283 TEST_F(BidirectionalStreamTest, CreateInsecureStream) { | |
284 BidirectionalStreamRequestInfo request_info; | |
285 request_info.method = "GET"; | |
286 request_info.url = GURL("http://www.example.org/"); | |
287 | |
288 TestDelegateBase delegate(nullptr, 0); | |
289 HttpNetworkSession::Params params = | |
290 SpdySessionDependencies::CreateSessionParams(&session_deps_); | |
291 scoped_ptr<HttpNetworkSession> session(new HttpNetworkSession(params)); | |
292 delegate.SetRunUntilCompletion(true); | |
293 delegate.Start(&request_info, session.get()); | |
294 | |
295 EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, delegate.error()); | |
296 } | |
297 | |
298 // Simulates user calling ReadData after END_STREAM has been received in | |
299 // BidirectionalStreamSpdyJob. | |
300 TEST_F(BidirectionalStreamTest, TestReadDataAfterClose) { | |
301 scoped_ptr<SpdyFrame> req( | |
302 spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); | |
303 // Empty DATA frame with an END_STREAM flag. | |
304 scoped_ptr<SpdyFrame> end_stream( | |
305 spdy_util_.ConstructSpdyBodyFrame(1, nullptr, 0, true)); | |
306 MockWrite writes[] = { | |
307 CreateMockWrite(*req.get(), 0), | |
308 }; | |
309 | |
310 const char* const kExtraResponseHeaders[] = {"header-name", "header-value"}; | |
311 | |
312 scoped_ptr<SpdyFrame> resp( | |
313 spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1)); | |
314 | |
315 scoped_ptr<SpdyFrame> body_frame(spdy_util_.ConstructSpdyBodyFrame(1, false)); | |
316 // Last body frame has END_STREAM flag set. | |
317 scoped_ptr<SpdyFrame> last_body_frame( | |
318 spdy_util_.ConstructSpdyBodyFrame(1, true)); | |
319 | |
320 MockRead reads[] = { | |
321 CreateMockRead(*resp, 1), | |
322 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause. | |
323 CreateMockRead(*body_frame, 3), | |
324 MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause. | |
325 CreateMockRead(*body_frame, 5), | |
326 CreateMockRead(*last_body_frame, 6), | |
327 MockRead(SYNCHRONOUS, 0, 7), | |
328 }; | |
329 | |
330 HostPortPair host_port_pair("www.example.org", 443); | |
331 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
332 PRIVACY_MODE_DISABLED); | |
333 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
334 | |
335 BidirectionalStreamRequestInfo request_info; | |
336 request_info.method = "GET"; | |
337 request_info.url = GURL("https://www.example.org/"); | |
338 request_info.end_stream_on_headers = true; | |
339 request_info.priority = LOWEST; | |
340 | |
341 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
342 // Create a MockTimer. Retain a raw pointer since the underlying | |
343 // BidirectionalStreamJob owns it. | |
344 MockTimer* timer = new MockTimer(); | |
345 scoped_ptr<TestDelegateBase> delegate(new TestDelegateBase( | |
346 read_buffer.get(), kReadBufferSize, make_scoped_ptr(timer))); | |
347 delegate->set_do_not_start_read(true); | |
348 | |
349 delegate->Start(&request_info, http_session_.get()); | |
350 | |
351 // Write request, and deliver response headers. | |
352 base::RunLoop().RunUntilIdle(); | |
353 EXPECT_FALSE(timer->IsRunning()); | |
354 // ReadData returns asynchronously because no data is buffered. | |
355 int rv = delegate->ReadData(); | |
356 EXPECT_EQ(ERR_IO_PENDING, rv); | |
357 // Deliver a DATA frame. | |
358 sequenced_data_->CompleteRead(); | |
359 base::RunLoop().RunUntilIdle(); | |
360 timer->Fire(); | |
361 // Asynchronous completion callback is invoke. | |
362 EXPECT_EQ(1, delegate->on_data_read_count()); | |
363 EXPECT_EQ(kUploadDataSize * 1, | |
364 static_cast<int>(delegate->data_received().size())); | |
365 | |
366 // Deliver the rest. Note that user has not called a second ReadData. | |
367 sequenced_data_->CompleteRead(); | |
368 base::RunLoop().RunUntilIdle(); | |
369 // ReadData now. Read should complete synchronously. | |
370 rv = delegate->ReadData(); | |
371 EXPECT_EQ(kUploadDataSize * 2, rv); | |
372 rv = delegate->ReadData(); | |
373 EXPECT_EQ(OK, rv); // EOF. | |
374 | |
375 const SpdyHeaderBlock response_headers = delegate->response_headers(); | |
376 EXPECT_EQ("200", response_headers.find(":status")->second); | |
377 EXPECT_EQ("header-value", response_headers.find("header-name")->second); | |
378 EXPECT_EQ(1, delegate->on_data_read_count()); | |
379 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
380 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
381 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), | |
382 delegate->GetTotalSentBytes()); | |
383 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), | |
384 delegate->GetTotalReceivedBytes()); | |
385 } | |
386 | |
387 TEST_F(BidirectionalStreamTest, TestInterleaveReadDataAndSendData) { | |
388 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); | |
389 | |
390 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyPost( | |
391 "https://www.example.org", 1, kBodyDataSize * 3, LOWEST, nullptr, 0)); | |
392 scoped_ptr<SpdyFrame> data_frame1( | |
393 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_NONE)); | |
394 scoped_ptr<SpdyFrame> data_frame2( | |
395 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_NONE)); | |
396 scoped_ptr<SpdyFrame> data_frame3( | |
397 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_FIN)); | |
398 MockWrite writes[] = { | |
399 CreateMockWrite(*req, 0), CreateMockWrite(*data_frame1, 3), | |
400 CreateMockWrite(*data_frame2, 6), CreateMockWrite(*data_frame3, 9), | |
401 }; | |
402 | |
403 scoped_ptr<SpdyFrame> resp( | |
404 spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1)); | |
405 scoped_ptr<SpdyFrame> response_body_frame1( | |
406 spdy_util_.ConstructSpdyBodyFrame(1, false)); | |
407 scoped_ptr<SpdyFrame> response_body_frame2( | |
408 spdy_util_.ConstructSpdyBodyFrame(1, true)); | |
409 | |
410 MockRead reads[] = { | |
411 CreateMockRead(*resp, 1), | |
412 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause. | |
413 CreateMockRead(*response_body_frame1, 4), | |
414 MockRead(ASYNC, ERR_IO_PENDING, 5), // Force a pause. | |
415 CreateMockRead(*response_body_frame2, 7), | |
416 MockRead(ASYNC, ERR_IO_PENDING, 8), // Force a pause. | |
417 MockRead(ASYNC, 0, 10), | |
418 }; | |
419 | |
420 HostPortPair host_port_pair("www.example.org", 443); | |
421 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
422 PRIVACY_MODE_DISABLED); | |
423 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
424 | |
425 BidirectionalStreamRequestInfo request_info; | |
426 request_info.method = "POST"; | |
427 request_info.url = GURL("https://www.example.org/"); | |
428 request_info.priority = LOWEST; | |
429 request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kContentLength, | |
430 base::SizeTToString(kBodyDataSize * 3)); | |
431 | |
432 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
433 MockTimer* timer = new MockTimer(); | |
434 scoped_ptr<TestDelegateBase> delegate(new TestDelegateBase( | |
435 read_buffer.get(), kReadBufferSize, make_scoped_ptr(timer))); | |
436 delegate->set_do_not_start_read(true); | |
437 delegate->Start(&request_info, http_session_.get()); | |
438 // Send the request and receive response headers. | |
439 base::RunLoop().RunUntilIdle(); | |
440 EXPECT_FALSE(timer->IsRunning()); | |
441 | |
442 // Send a DATA frame. | |
443 scoped_refptr<StringIOBuffer> buf( | |
444 new StringIOBuffer(std::string(kBodyData, kBodyDataSize))); | |
445 delegate->SendData(buf.get(), buf->size(), false); | |
446 base::RunLoop().RunUntilIdle(); | |
447 EXPECT_EQ(1, delegate->on_data_sent_count()); | |
448 | |
449 // ReadData and it should return asynchronously because no data is buffered. | |
450 int rv = delegate->ReadData(); | |
451 EXPECT_EQ(ERR_IO_PENDING, rv); | |
452 // Deliver a DATA frame, and fire the timer. | |
453 sequenced_data_->CompleteRead(); | |
454 timer->Fire(); | |
455 base::RunLoop().RunUntilIdle(); | |
456 EXPECT_EQ(1, delegate->on_data_read_count()); | |
457 | |
458 // Send a DATA frame. | |
459 delegate->SendData(buf.get(), buf->size(), false); | |
460 base::RunLoop().RunUntilIdle(); | |
461 EXPECT_EQ(2, delegate->on_data_sent_count()); | |
462 | |
463 // ReadData and it should return asynchronously because no data is buffered. | |
464 rv = delegate->ReadData(); | |
465 EXPECT_EQ(ERR_IO_PENDING, rv); | |
466 // Deliver a DATA frame, and fire the timer. | |
467 sequenced_data_->CompleteRead(); | |
468 timer->Fire(); | |
469 base::RunLoop().RunUntilIdle(); | |
470 // Last DATA frame is read. Server half closes. | |
471 EXPECT_EQ(2, delegate->on_data_read_count()); | |
472 | |
473 // Send the last body frame. Client half closes. | |
474 delegate->SendData(buf.get(), buf->size(), true); | |
475 base::RunLoop().RunUntilIdle(); | |
476 EXPECT_EQ(3, delegate->on_data_sent_count()); | |
477 sequenced_data_->CompleteRead(); | |
478 | |
479 // OnClose is invoked since both sides are closed. | |
480 rv = delegate->ReadData(); | |
481 EXPECT_EQ(OK, rv); | |
482 | |
483 EXPECT_EQ("200", delegate->response_headers().find(":status")->second); | |
484 EXPECT_EQ(2, delegate->on_data_read_count()); | |
485 EXPECT_EQ(3, delegate->on_data_sent_count()); | |
486 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
487 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), | |
488 delegate->GetTotalSentBytes()); | |
489 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), | |
490 delegate->GetTotalReceivedBytes()); | |
491 } | |
492 | |
493 // Tests that BidirectionalStreamSpdyJob::OnClose will complete any remaining | |
494 // read even if the read queue is empty. | |
495 TEST_F(BidirectionalStreamTest, TestCompleteAsyncRead) { | |
496 scoped_ptr<SpdyFrame> req( | |
497 spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); | |
498 // Empty DATA frame with an END_STREAM flag. | |
499 scoped_ptr<SpdyFrame> end_stream( | |
500 spdy_util_.ConstructSpdyBodyFrame(1, nullptr, 0, true)); | |
501 | |
502 MockWrite writes[] = {CreateMockWrite(*req.get(), 0)}; | |
503 | |
504 scoped_ptr<SpdyFrame> resp( | |
505 spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1)); | |
506 | |
507 scoped_ptr<SpdyFrame> response_body_frame( | |
508 spdy_util_.ConstructSpdyBodyFrame(1, nullptr, 0, true)); | |
509 | |
510 MockRead reads[] = { | |
511 CreateMockRead(*resp, 1), | |
512 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause. | |
513 CreateMockRead(*response_body_frame, 3), MockRead(SYNCHRONOUS, 0, 4), | |
514 }; | |
515 | |
516 HostPortPair host_port_pair("www.example.org", 443); | |
517 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
518 PRIVACY_MODE_DISABLED); | |
519 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
520 | |
521 BidirectionalStreamRequestInfo request_info; | |
522 request_info.method = "GET"; | |
523 request_info.url = GURL("https://www.example.org/"); | |
524 request_info.priority = LOWEST; | |
525 request_info.end_stream_on_headers = true; | |
526 | |
527 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
528 MockTimer* timer = new MockTimer(); | |
529 scoped_ptr<TestDelegateBase> delegate(new TestDelegateBase( | |
530 read_buffer.get(), kReadBufferSize, make_scoped_ptr(timer))); | |
531 delegate->set_do_not_start_read(true); | |
532 delegate->Start(&request_info, http_session_.get()); | |
533 // Write request, and deliver response headers. | |
534 base::RunLoop().RunUntilIdle(); | |
535 EXPECT_FALSE(timer->IsRunning()); | |
536 | |
537 // ReadData should return asynchronously because no data is buffered. | |
538 int rv = delegate->ReadData(); | |
539 EXPECT_EQ(ERR_IO_PENDING, rv); | |
540 // Deliver END_STREAM. | |
541 // OnClose should trigger completion of the remaining read. | |
542 sequenced_data_->CompleteRead(); | |
543 base::RunLoop().RunUntilIdle(); | |
544 | |
545 EXPECT_EQ("200", delegate->response_headers().find(":status")->second); | |
546 EXPECT_EQ(1, delegate->on_data_read_count()); | |
547 EXPECT_EQ(0u, delegate->data_received().size()); | |
548 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
549 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
550 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), | |
551 delegate->GetTotalSentBytes()); | |
552 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), | |
553 delegate->GetTotalReceivedBytes()); | |
554 } | |
555 | |
556 TEST_F(BidirectionalStreamTest, TestBuffering) { | |
557 scoped_ptr<SpdyFrame> req( | |
558 spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); | |
559 // Empty DATA frame with an END_STREAM flag. | |
560 scoped_ptr<SpdyFrame> end_stream( | |
561 spdy_util_.ConstructSpdyBodyFrame(1, nullptr, 0, true)); | |
562 | |
563 MockWrite writes[] = {CreateMockWrite(*req.get(), 0)}; | |
564 | |
565 const char* const kExtraResponseHeaders[] = {"header-name", "header-value"}; | |
566 | |
567 scoped_ptr<SpdyFrame> resp( | |
568 spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1)); | |
569 | |
570 scoped_ptr<SpdyFrame> body_frame(spdy_util_.ConstructSpdyBodyFrame(1, false)); | |
571 // Last body frame has END_STREAM flag set. | |
572 scoped_ptr<SpdyFrame> last_body_frame( | |
573 spdy_util_.ConstructSpdyBodyFrame(1, true)); | |
574 | |
575 MockRead reads[] = { | |
576 CreateMockRead(*resp, 1), | |
577 CreateMockRead(*body_frame, 2), | |
578 CreateMockRead(*body_frame, 3), | |
579 MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause. | |
580 CreateMockRead(*last_body_frame, 5), | |
581 MockRead(SYNCHRONOUS, 0, 6), | |
582 }; | |
583 | |
584 HostPortPair host_port_pair("www.example.org", 443); | |
585 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
586 PRIVACY_MODE_DISABLED); | |
587 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
588 | |
589 BidirectionalStreamRequestInfo request_info; | |
590 request_info.method = "GET"; | |
591 request_info.url = GURL("https://www.example.org/"); | |
592 request_info.priority = LOWEST; | |
593 request_info.end_stream_on_headers = true; | |
594 | |
595 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
596 MockTimer* timer = new MockTimer(); | |
597 scoped_ptr<TestDelegateBase> delegate(new TestDelegateBase( | |
598 read_buffer.get(), kReadBufferSize, make_scoped_ptr(timer))); | |
599 delegate->Start(&request_info, http_session_.get()); | |
600 // Deliver two DATA frames together. | |
601 base::RunLoop().RunUntilIdle(); | |
602 EXPECT_TRUE(timer->IsRunning()); | |
603 timer->Fire(); | |
604 base::RunLoop().RunUntilIdle(); | |
605 // This should trigger |more_read_data_pending_| to execute the task at a | |
606 // later time, and Delegate::OnReadComplete should not have been called. | |
607 EXPECT_TRUE(timer->IsRunning()); | |
608 EXPECT_EQ(0, delegate->on_data_read_count()); | |
609 | |
610 // Fire the timer now, the two DATA frame should be combined into one | |
611 // single Delegate::OnReadComplete callback. | |
612 timer->Fire(); | |
613 base::RunLoop().RunUntilIdle(); | |
614 EXPECT_EQ(1, delegate->on_data_read_count()); | |
615 EXPECT_EQ(kUploadDataSize * 2, | |
616 static_cast<int>(delegate->data_received().size())); | |
617 | |
618 // Deliver last DATA frame and EOF. There will be an additional | |
619 // Delegate::OnReadComplete callback. | |
620 sequenced_data_->CompleteRead(); | |
621 EXPECT_EQ(2, delegate->on_data_read_count()); | |
622 EXPECT_EQ(kUploadDataSize * 3, | |
623 static_cast<int>(delegate->data_received().size())); | |
624 | |
625 const SpdyHeaderBlock response_headers = delegate->response_headers(); | |
626 EXPECT_EQ("200", response_headers.find(":status")->second); | |
627 EXPECT_EQ("header-value", response_headers.find("header-name")->second); | |
628 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
629 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
630 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), | |
631 delegate->GetTotalSentBytes()); | |
632 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), | |
633 delegate->GetTotalReceivedBytes()); | |
634 } | |
635 | |
636 TEST_F(BidirectionalStreamTest, TestBufferingWithTrailers) { | |
637 scoped_ptr<SpdyFrame> req( | |
638 spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); | |
639 // Empty DATA frame with an END_STREAM flag. | |
640 scoped_ptr<SpdyFrame> end_stream( | |
641 spdy_util_.ConstructSpdyBodyFrame(1, nullptr, 0, true)); | |
642 | |
643 MockWrite writes[] = { | |
644 CreateMockWrite(*req.get(), 0), | |
645 }; | |
646 | |
647 const char* const kExtraResponseHeaders[] = {"header-name", "header-value"}; | |
648 | |
649 scoped_ptr<SpdyFrame> resp( | |
650 spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1)); | |
651 | |
652 scoped_ptr<SpdyFrame> body_frame(spdy_util_.ConstructSpdyBodyFrame(1, false)); | |
653 | |
654 const char* const kTrailers[] = {"foo", "bar"}; | |
655 scoped_ptr<SpdyFrame> trailers( | |
656 spdy_util_.ConstructSpdyHeaderFrame(1, kTrailers, 1, true)); | |
657 | |
658 MockRead reads[] = { | |
659 CreateMockRead(*resp, 1), CreateMockRead(*body_frame, 2), | |
660 CreateMockRead(*body_frame, 3), CreateMockRead(*body_frame, 4), | |
661 MockRead(ASYNC, ERR_IO_PENDING, 5), // Force a pause. | |
662 CreateMockRead(*trailers, 6), MockRead(SYNCHRONOUS, 0, 7), | |
663 }; | |
664 | |
665 HostPortPair host_port_pair("www.example.org", 443); | |
666 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
667 PRIVACY_MODE_DISABLED); | |
668 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
669 | |
670 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
671 MockTimer* timer = new MockTimer(); | |
672 scoped_ptr<TestDelegateBase> delegate(new TestDelegateBase( | |
673 read_buffer.get(), kReadBufferSize, make_scoped_ptr(timer))); | |
674 | |
675 BidirectionalStreamRequestInfo request_info; | |
676 request_info.method = "GET"; | |
677 request_info.url = GURL("https://www.example.org/"); | |
678 request_info.priority = LOWEST; | |
679 request_info.end_stream_on_headers = true; | |
680 | |
681 delegate->Start(&request_info, http_session_.get()); | |
682 // Deliver all three DATA frames together. | |
683 base::RunLoop().RunUntilIdle(); | |
684 EXPECT_TRUE(timer->IsRunning()); | |
685 timer->Fire(); | |
686 base::RunLoop().RunUntilIdle(); | |
687 // This should trigger |more_read_data_pending_| to execute the task at a | |
688 // later time, and Delegate::OnReadComplete should not have been called. | |
689 EXPECT_TRUE(timer->IsRunning()); | |
690 EXPECT_EQ(0, delegate->on_data_read_count()); | |
691 | |
692 // Deliver trailers. Remaining read should be completed, since OnClose is | |
693 // called right after OnTrailersReceived. The three DATA frames should be | |
694 // delivered in a single OnReadCompleted callback. | |
695 sequenced_data_->CompleteRead(); | |
696 EXPECT_EQ(1, delegate->on_data_read_count()); | |
697 EXPECT_EQ(kUploadDataSize * 3, | |
698 static_cast<int>(delegate->data_received().size())); | |
699 const SpdyHeaderBlock response_headers = delegate->response_headers(); | |
700 EXPECT_EQ("200", response_headers.find(":status")->second); | |
701 EXPECT_EQ("header-value", response_headers.find("header-name")->second); | |
702 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
703 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
704 EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), | |
705 delegate->GetTotalSentBytes()); | |
706 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), | |
707 delegate->GetTotalReceivedBytes()); | |
708 } | |
709 | |
710 TEST_F(BidirectionalStreamTest, CancelStream) { | |
711 scoped_ptr<SpdyFrame> req( | |
712 spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); | |
713 | |
714 scoped_ptr<SpdyFrame> rst( | |
715 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); | |
716 MockWrite writes[] = { | |
717 CreateMockWrite(*req, 0), CreateMockWrite(*rst, 2), | |
718 }; | |
719 | |
720 const char* const kExtraResponseHeaders[] = {"header-name", "header-value"}; | |
721 | |
722 scoped_ptr<SpdyFrame> resp( | |
723 spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1)); | |
724 | |
725 MockRead reads[] = { | |
726 CreateMockRead(*resp, 1), MockRead(ASYNC, 0, 3), | |
727 }; | |
728 | |
729 HostPortPair host_port_pair("www.example.org", 443); | |
730 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
731 PRIVACY_MODE_DISABLED); | |
732 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
733 | |
734 BidirectionalStreamRequestInfo request_info; | |
735 request_info.method = "GET"; | |
736 request_info.url = GURL("https://www.example.org/"); | |
737 request_info.priority = LOWEST; | |
738 request_info.end_stream_on_headers = true; | |
739 | |
740 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
741 scoped_ptr<CancelStreamDelegate> delegate( | |
742 new CancelStreamDelegate(read_buffer.get(), kReadBufferSize)); | |
743 delegate->SetRunUntilCompletion(true); | |
744 delegate->Start(&request_info, http_session_.get()); | |
745 // Makes sure delegate does not get called. | |
746 base::RunLoop().RunUntilIdle(); | |
747 const SpdyHeaderBlock response_headers = delegate->response_headers(); | |
748 EXPECT_EQ("200", response_headers.find(":status")->second); | |
749 EXPECT_EQ("header-value", response_headers.find("header-name")->second); | |
750 EXPECT_EQ(0u, delegate->data_received().size()); | |
751 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
752 | |
753 // Stream is canceled. The total received and sent bytes should be 0. | |
754 EXPECT_EQ(0, delegate->GetTotalSentBytes()); | |
755 EXPECT_EQ(0, delegate->GetTotalReceivedBytes()); | |
756 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
757 EXPECT_EQ(0, delegate->on_data_read_count()); | |
758 } | |
759 | |
760 TEST_F(BidirectionalStreamTest, CancelStreamAfterSendData) { | |
761 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); | |
762 | |
763 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyPost( | |
764 "https://www.example.org", 1, kBodyDataSize * 3, LOWEST, nullptr, 0)); | |
765 scoped_ptr<SpdyFrame> data_frame( | |
766 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_NONE)); | |
767 scoped_ptr<SpdyFrame> rst( | |
768 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); | |
769 | |
770 MockWrite writes[] = { | |
771 CreateMockWrite(*req, 0), CreateMockWrite(*data_frame, 3), | |
772 CreateMockWrite(*rst, 4), | |
773 }; | |
774 | |
775 scoped_ptr<SpdyFrame> resp( | |
776 spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1)); | |
777 scoped_ptr<SpdyFrame> response_body_frame( | |
778 spdy_util_.ConstructSpdyBodyFrame(1, false)); | |
779 | |
780 MockRead reads[] = { | |
781 CreateMockRead(*resp, 1), | |
782 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause. | |
783 MockRead(ASYNC, 0, 5), | |
784 }; | |
785 | |
786 HostPortPair host_port_pair("www.example.org", 443); | |
787 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
788 PRIVACY_MODE_DISABLED); | |
789 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
790 | |
791 BidirectionalStreamRequestInfo request_info; | |
792 request_info.method = "POST"; | |
793 request_info.url = GURL("https://www.example.org/"); | |
794 request_info.priority = LOWEST; | |
795 request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kContentLength, | |
796 base::SizeTToString(kBodyDataSize * 3)); | |
797 | |
798 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
799 scoped_ptr<TestDelegateBase> delegate( | |
800 new TestDelegateBase(read_buffer.get(), kReadBufferSize)); | |
801 delegate->set_do_not_start_read(true); | |
802 delegate->Start(&request_info, http_session_.get()); | |
803 // Send the request and receive response headers. | |
804 base::RunLoop().RunUntilIdle(); | |
805 | |
806 // Send a DATA frame. | |
807 scoped_refptr<StringIOBuffer> buf( | |
808 new StringIOBuffer(std::string(kBodyData, kBodyDataSize))); | |
809 delegate->SendData(buf.get(), buf->size(), false); | |
810 base::RunLoop().RunUntilIdle(); | |
811 EXPECT_EQ(1, delegate->on_data_sent_count()); | |
812 // Cancel the stream. | |
813 delegate->CancelStream(); | |
814 base::RunLoop().RunUntilIdle(); | |
815 sequenced_data_->CompleteRead(); | |
816 | |
817 base::RunLoop().RunUntilIdle(); | |
818 | |
819 EXPECT_EQ("200", delegate->response_headers().find(":status")->second); | |
820 EXPECT_EQ(0, delegate->on_data_read_count()); | |
821 EXPECT_EQ(1, delegate->on_data_sent_count()); | |
822 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
823 EXPECT_EQ(0, delegate->GetTotalSentBytes()); | |
824 EXPECT_EQ(0, delegate->GetTotalReceivedBytes()); | |
825 } | |
826 | |
827 TEST_F(BidirectionalStreamTest, CancelStreamDuringReadData) { | |
828 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); | |
829 | |
830 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyPost( | |
831 "https://www.example.org", 1, kBodyDataSize * 3, LOWEST, nullptr, 0)); | |
832 scoped_ptr<SpdyFrame> data_frame( | |
833 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_NONE)); | |
834 scoped_ptr<SpdyFrame> rst( | |
835 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); | |
836 | |
837 MockWrite writes[] = { | |
838 CreateMockWrite(*req, 0), CreateMockWrite(*rst, 4), | |
839 }; | |
840 | |
841 scoped_ptr<SpdyFrame> resp( | |
842 spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1)); | |
843 scoped_ptr<SpdyFrame> response_body_frame( | |
844 spdy_util_.ConstructSpdyBodyFrame(1, false)); | |
845 | |
846 MockRead reads[] = { | |
847 CreateMockRead(*resp, 1), | |
848 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause. | |
849 CreateMockRead(*response_body_frame, 3), MockRead(ASYNC, 0, 5), | |
850 }; | |
851 | |
852 HostPortPair host_port_pair("www.example.org", 443); | |
853 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
854 PRIVACY_MODE_DISABLED); | |
855 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
856 | |
857 BidirectionalStreamRequestInfo request_info; | |
858 request_info.method = "POST"; | |
859 request_info.url = GURL("https://www.example.org/"); | |
860 request_info.priority = LOWEST; | |
861 request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kContentLength, | |
862 base::SizeTToString(kBodyDataSize * 3)); | |
863 | |
864 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
865 scoped_ptr<TestDelegateBase> delegate( | |
866 new TestDelegateBase(read_buffer.get(), kReadBufferSize)); | |
867 delegate->set_do_not_start_read(true); | |
868 delegate->Start(&request_info, http_session_.get()); | |
869 // Send the request and receive response headers. | |
870 base::RunLoop().RunUntilIdle(); | |
871 | |
872 EXPECT_EQ("200", delegate->response_headers().find(":status")->second); | |
873 // Cancel the stream after ReadData returns ERR_IO_PENDING. | |
874 int rv = delegate->ReadData(); | |
875 EXPECT_EQ(ERR_IO_PENDING, rv); | |
876 delegate->CancelStream(); | |
877 sequenced_data_->CompleteRead(); | |
878 base::RunLoop().RunUntilIdle(); | |
879 | |
880 EXPECT_EQ(0, delegate->on_data_read_count()); | |
881 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
882 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
883 EXPECT_EQ(0, delegate->GetTotalSentBytes()); | |
884 EXPECT_EQ(0, delegate->GetTotalReceivedBytes()); | |
885 } | |
886 | |
887 // Receiving a header with uppercase ASCII will result in a protocol error, | |
888 // which should be propagated via Delegate::OnFailed. | |
889 TEST_F(BidirectionalStreamTest, PropagateProtocolError) { | |
890 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyPost( | |
891 "https://www.example.org", 1, kBodyDataSize * 3, LOW, nullptr, 0)); | |
892 scoped_ptr<SpdyFrame> rst( | |
893 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_PROTOCOL_ERROR)); | |
894 | |
895 MockWrite writes[] = { | |
896 CreateMockWrite(*req, 0), CreateMockWrite(*rst, 2), | |
897 }; | |
898 | |
899 const char* const kExtraHeaders[] = {"X-UpperCase", "yes"}; | |
900 scoped_ptr<SpdyFrame> resp( | |
901 spdy_util_.ConstructSpdyGetSynReply(kExtraHeaders, 1, 1)); | |
902 | |
903 MockRead reads[] = { | |
904 CreateMockRead(*resp, 1), MockRead(ASYNC, 0, 3), | |
905 }; | |
906 | |
907 HostPortPair host_port_pair("www.example.org", 443); | |
908 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), | |
909 PRIVACY_MODE_DISABLED); | |
910 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); | |
911 | |
912 BidirectionalStreamRequestInfo request_info; | |
913 request_info.method = "POST"; | |
914 request_info.url = GURL("https://www.example.org/"); | |
915 request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kContentLength, | |
916 base::SizeTToString(kBodyDataSize * 3)); | |
917 | |
918 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); | |
919 scoped_ptr<TestDelegateBase> delegate( | |
920 new TestDelegateBase(read_buffer.get(), kReadBufferSize)); | |
921 delegate->SetRunUntilCompletion(true); | |
922 delegate->Start(&request_info, http_session_.get()); | |
923 | |
924 base::RunLoop().RunUntilIdle(); | |
925 EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, delegate->error()); | |
926 EXPECT_EQ(delegate->response_headers().end(), | |
927 delegate->response_headers().find(":status")); | |
928 EXPECT_EQ(0, delegate->on_data_read_count()); | |
929 EXPECT_EQ(0, delegate->on_data_sent_count()); | |
930 EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); | |
931 // BidirectionalStreamSpdyStreamJob does not count the bytes sent for |rst| | |
932 // because it is sent after SpdyStream::Delegate::OnClose is called. | |
933 EXPECT_EQ(CountWriteBytes(writes, 1), delegate->GetTotalSentBytes()); | |
934 EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), | |
935 delegate->GetTotalReceivedBytes()); | |
936 } | |
937 | |
938 } // namespace net | |
OLD | NEW |