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_http_stream.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <memory> | |
10 | |
11 #include "base/run_loop.h" | |
12 #include "base/stl_util.h" | |
13 #include "base/threading/thread_task_runner_handle.h" | |
14 #include "crypto/ec_private_key.h" | |
15 #include "crypto/ec_signature_creator.h" | |
16 #include "crypto/signature_creator.h" | |
17 #include "net/base/chunked_upload_data_stream.h" | |
18 #include "net/base/load_timing_info.h" | |
19 #include "net/base/load_timing_info_test_util.h" | |
20 #include "net/base/test_completion_callback.h" | |
21 #include "net/cert/asn1_util.h" | |
22 #include "net/http/http_request_info.h" | |
23 #include "net/http/http_response_headers.h" | |
24 #include "net/http/http_response_info.h" | |
25 #include "net/log/net_log_with_source.h" | |
26 #include "net/log/test_net_log.h" | |
27 #include "net/socket/socket_test_util.h" | |
28 #include "net/spdy/platform/api/spdy_string.h" | |
29 #include "net/spdy/spdy_http_utils.h" | |
30 #include "net/spdy/spdy_session.h" | |
31 #include "net/spdy/spdy_test_util_common.h" | |
32 #include "net/ssl/default_channel_id_store.h" | |
33 #include "net/test/cert_test_util.h" | |
34 #include "net/test/gtest_util.h" | |
35 #include "net/test/test_data_directory.h" | |
36 #include "testing/gmock/include/gmock/gmock.h" | |
37 #include "testing/gtest/include/gtest/gtest.h" | |
38 | |
39 namespace net { | |
40 | |
41 namespace test { | |
42 | |
43 namespace { | |
44 | |
45 // Tests the load timing of a stream that's connected and is not the first | |
46 // request sent on a connection. | |
47 void TestLoadTimingReused(const HttpStream& stream) { | |
48 LoadTimingInfo load_timing_info; | |
49 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); | |
50 | |
51 EXPECT_TRUE(load_timing_info.socket_reused); | |
52 EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); | |
53 | |
54 ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); | |
55 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); | |
56 } | |
57 | |
58 // Tests the load timing of a stream that's connected and using a fresh | |
59 // connection. | |
60 void TestLoadTimingNotReused(const HttpStream& stream) { | |
61 LoadTimingInfo load_timing_info; | |
62 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); | |
63 | |
64 EXPECT_FALSE(load_timing_info.socket_reused); | |
65 EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); | |
66 | |
67 ExpectConnectTimingHasTimes( | |
68 load_timing_info.connect_timing, | |
69 CONNECT_TIMING_HAS_DNS_TIMES | CONNECT_TIMING_HAS_SSL_TIMES); | |
70 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); | |
71 } | |
72 | |
73 class ReadErrorUploadDataStream : public UploadDataStream { | |
74 public: | |
75 enum class FailureMode { SYNC, ASYNC }; | |
76 | |
77 explicit ReadErrorUploadDataStream(FailureMode mode) | |
78 : UploadDataStream(true, 0), async_(mode), weak_factory_(this) {} | |
79 | |
80 private: | |
81 void CompleteRead() { UploadDataStream::OnReadCompleted(ERR_FAILED); } | |
82 | |
83 // UploadDataStream implementation: | |
84 int InitInternal(const NetLogWithSource& net_log) override { return OK; } | |
85 | |
86 int ReadInternal(IOBuffer* buf, int buf_len) override { | |
87 if (async_ == FailureMode::ASYNC) { | |
88 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
89 FROM_HERE, base::Bind(&ReadErrorUploadDataStream::CompleteRead, | |
90 weak_factory_.GetWeakPtr())); | |
91 return ERR_IO_PENDING; | |
92 } | |
93 return ERR_FAILED; | |
94 } | |
95 | |
96 void ResetInternal() override {} | |
97 | |
98 const FailureMode async_; | |
99 | |
100 base::WeakPtrFactory<ReadErrorUploadDataStream> weak_factory_; | |
101 | |
102 DISALLOW_COPY_AND_ASSIGN(ReadErrorUploadDataStream); | |
103 }; | |
104 | |
105 class CancelStreamCallback : public TestCompletionCallbackBase { | |
106 public: | |
107 explicit CancelStreamCallback(SpdyHttpStream* stream) | |
108 : stream_(stream), | |
109 callback_(base::Bind(&CancelStreamCallback::CancelStream, | |
110 base::Unretained(this))) {} | |
111 | |
112 const CompletionCallback& callback() const { return callback_; } | |
113 | |
114 private: | |
115 void CancelStream(int result) { | |
116 stream_->Cancel(); | |
117 SetResult(result); | |
118 } | |
119 | |
120 SpdyHttpStream* stream_; | |
121 CompletionCallback callback_; | |
122 }; | |
123 | |
124 } // namespace | |
125 | |
126 class SpdyHttpStreamTest : public testing::Test { | |
127 public: | |
128 SpdyHttpStreamTest() | |
129 : url_(kDefaultUrl), | |
130 host_port_pair_(HostPortPair::FromURL(url_)), | |
131 key_(host_port_pair_, ProxyServer::Direct(), PRIVACY_MODE_DISABLED), | |
132 ssl_(SYNCHRONOUS, OK) { | |
133 session_deps_.net_log = &net_log_; | |
134 } | |
135 | |
136 ~SpdyHttpStreamTest() override {} | |
137 | |
138 protected: | |
139 void TearDown() override { | |
140 crypto::ECSignatureCreator::SetFactoryForTesting(nullptr); | |
141 base::RunLoop().RunUntilIdle(); | |
142 EXPECT_TRUE(sequenced_data_->AllReadDataConsumed()); | |
143 EXPECT_TRUE(sequenced_data_->AllWriteDataConsumed()); | |
144 } | |
145 | |
146 // Initializes the session using SequencedSocketData. | |
147 void InitSession(MockRead* reads, | |
148 size_t reads_count, | |
149 MockWrite* writes, | |
150 size_t writes_count) { | |
151 sequenced_data_.reset( | |
152 new SequencedSocketData(reads, reads_count, writes, writes_count)); | |
153 session_deps_.socket_factory->AddSocketDataProvider(sequenced_data_.get()); | |
154 | |
155 ssl_.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
156 ASSERT_TRUE(ssl_.cert); | |
157 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_); | |
158 | |
159 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); | |
160 session_ = | |
161 CreateSecureSpdySession(http_session_.get(), key_, NetLogWithSource()); | |
162 } | |
163 | |
164 void TestSendCredentials(ChannelIDService* channel_id_service, | |
165 const SpdyString& cert, | |
166 const SpdyString& proof); | |
167 | |
168 SpdyTestUtil spdy_util_; | |
169 TestNetLog net_log_; | |
170 SpdySessionDependencies session_deps_; | |
171 const GURL url_; | |
172 const HostPortPair host_port_pair_; | |
173 const SpdySessionKey key_; | |
174 std::unique_ptr<SequencedSocketData> sequenced_data_; | |
175 std::unique_ptr<HttpNetworkSession> http_session_; | |
176 base::WeakPtr<SpdySession> session_; | |
177 | |
178 private: | |
179 MockECSignatureCreatorFactory ec_signature_creator_factory_; | |
180 SSLSocketDataProvider ssl_; | |
181 }; | |
182 | |
183 TEST_F(SpdyHttpStreamTest, SendRequest) { | |
184 SpdySerializedFrame req( | |
185 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
186 MockWrite writes[] = { | |
187 CreateMockWrite(req, 0), | |
188 }; | |
189 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
190 MockRead reads[] = { | |
191 CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF | |
192 }; | |
193 | |
194 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
195 | |
196 HttpRequestInfo request; | |
197 request.method = "GET"; | |
198 request.url = url_; | |
199 TestCompletionCallback callback; | |
200 HttpResponseInfo response; | |
201 HttpRequestHeaders headers; | |
202 NetLogWithSource net_log; | |
203 std::unique_ptr<SpdyHttpStream> http_stream( | |
204 new SpdyHttpStream(session_, true, net_log.source())); | |
205 // Make sure getting load timing information the stream early does not crash. | |
206 LoadTimingInfo load_timing_info; | |
207 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); | |
208 | |
209 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
210 CompletionCallback()), | |
211 IsOk()); | |
212 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); | |
213 | |
214 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
215 IsError(ERR_IO_PENDING)); | |
216 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
217 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); | |
218 | |
219 callback.WaitForResult(); | |
220 | |
221 // Can get timing information once the stream connects. | |
222 TestLoadTimingNotReused(*http_stream); | |
223 | |
224 // Because we abandoned the stream, we don't expect to find a session in the | |
225 // pool anymore. | |
226 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
227 | |
228 TestLoadTimingNotReused(*http_stream); | |
229 http_stream->Close(true); | |
230 // Test that there's no crash when trying to get the load timing after the | |
231 // stream has been closed. | |
232 TestLoadTimingNotReused(*http_stream); | |
233 | |
234 EXPECT_EQ(static_cast<int64_t>(req.size()), http_stream->GetTotalSentBytes()); | |
235 EXPECT_EQ(static_cast<int64_t>(resp.size()), | |
236 http_stream->GetTotalReceivedBytes()); | |
237 } | |
238 | |
239 TEST_F(SpdyHttpStreamTest, LoadTimingTwoRequests) { | |
240 SpdySerializedFrame req1( | |
241 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
242 SpdySerializedFrame req2( | |
243 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
244 MockWrite writes[] = { | |
245 CreateMockWrite(req1, 0), CreateMockWrite(req2, 1), | |
246 }; | |
247 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
248 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true)); | |
249 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
250 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, "", 0, true)); | |
251 MockRead reads[] = { | |
252 CreateMockRead(resp1, 2), CreateMockRead(body1, 3), | |
253 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
254 MockRead(ASYNC, 0, 6) // EOF | |
255 }; | |
256 | |
257 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
258 | |
259 HttpRequestInfo request1; | |
260 request1.method = "GET"; | |
261 request1.url = url_; | |
262 TestCompletionCallback callback1; | |
263 HttpResponseInfo response1; | |
264 HttpRequestHeaders headers1; | |
265 NetLogWithSource net_log; | |
266 std::unique_ptr<SpdyHttpStream> http_stream1( | |
267 new SpdyHttpStream(session_, true, net_log.source())); | |
268 | |
269 HttpRequestInfo request2; | |
270 request2.method = "GET"; | |
271 request2.url = url_; | |
272 TestCompletionCallback callback2; | |
273 HttpResponseInfo response2; | |
274 HttpRequestHeaders headers2; | |
275 std::unique_ptr<SpdyHttpStream> http_stream2( | |
276 new SpdyHttpStream(session_, true, net_log.source())); | |
277 | |
278 // First write. | |
279 ASSERT_THAT(http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY, | |
280 net_log, CompletionCallback()), | |
281 IsOk()); | |
282 EXPECT_THAT( | |
283 http_stream1->SendRequest(headers1, &response1, callback1.callback()), | |
284 IsError(ERR_IO_PENDING)); | |
285 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
286 | |
287 EXPECT_LE(0, callback1.WaitForResult()); | |
288 | |
289 TestLoadTimingNotReused(*http_stream1); | |
290 LoadTimingInfo load_timing_info1; | |
291 LoadTimingInfo load_timing_info2; | |
292 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1)); | |
293 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); | |
294 | |
295 // Second write. | |
296 ASSERT_THAT(http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY, | |
297 net_log, CompletionCallback()), | |
298 IsOk()); | |
299 EXPECT_THAT( | |
300 http_stream2->SendRequest(headers2, &response2, callback2.callback()), | |
301 IsError(ERR_IO_PENDING)); | |
302 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
303 | |
304 EXPECT_LE(0, callback2.WaitForResult()); | |
305 | |
306 // Perform all async reads. | |
307 base::RunLoop().RunUntilIdle(); | |
308 | |
309 TestLoadTimingReused(*http_stream2); | |
310 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); | |
311 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); | |
312 | |
313 // Read stream 1 to completion, before making sure we can still read load | |
314 // timing from both streams. | |
315 scoped_refptr<IOBuffer> buf1(new IOBuffer(1)); | |
316 ASSERT_EQ( | |
317 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback())); | |
318 | |
319 // Stream 1 has been read to completion. | |
320 TestLoadTimingNotReused(*http_stream1); | |
321 | |
322 EXPECT_EQ(static_cast<int64_t>(req1.size()), | |
323 http_stream1->GetTotalSentBytes()); | |
324 EXPECT_EQ(static_cast<int64_t>(resp1.size() + body1.size()), | |
325 http_stream1->GetTotalReceivedBytes()); | |
326 | |
327 // Stream 2 still has queued body data. | |
328 TestLoadTimingReused(*http_stream2); | |
329 | |
330 EXPECT_EQ(static_cast<int64_t>(req2.size()), | |
331 http_stream2->GetTotalSentBytes()); | |
332 EXPECT_EQ(static_cast<int64_t>(resp2.size() + body2.size()), | |
333 http_stream2->GetTotalReceivedBytes()); | |
334 } | |
335 | |
336 TEST_F(SpdyHttpStreamTest, SendChunkedPost) { | |
337 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
338 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, kUploadData, | |
339 kUploadDataSize, | |
340 /*fin=*/true)); | |
341 MockWrite writes[] = { | |
342 CreateMockWrite(req, 0), // request | |
343 CreateMockWrite(body, 1) // POST upload frame | |
344 }; | |
345 | |
346 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
347 MockRead reads[] = { | |
348 CreateMockRead(resp, 2), CreateMockRead(body, 3, SYNCHRONOUS), | |
349 MockRead(SYNCHRONOUS, 0, 4) // EOF | |
350 }; | |
351 | |
352 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
353 | |
354 ChunkedUploadDataStream upload_stream(0); | |
355 const int kFirstChunkSize = kUploadDataSize/2; | |
356 upload_stream.AppendData(kUploadData, kFirstChunkSize, false); | |
357 upload_stream.AppendData(kUploadData + kFirstChunkSize, | |
358 kUploadDataSize - kFirstChunkSize, true); | |
359 | |
360 HttpRequestInfo request; | |
361 request.method = "POST"; | |
362 request.url = url_; | |
363 request.upload_data_stream = &upload_stream; | |
364 | |
365 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
366 NetLogWithSource()), | |
367 IsOk()); | |
368 | |
369 TestCompletionCallback callback; | |
370 HttpResponseInfo response; | |
371 HttpRequestHeaders headers; | |
372 NetLogWithSource net_log; | |
373 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
374 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
375 CompletionCallback()), | |
376 IsOk()); | |
377 | |
378 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
379 IsError(ERR_IO_PENDING)); | |
380 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
381 | |
382 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
383 | |
384 EXPECT_EQ(static_cast<int64_t>(req.size() + body.size()), | |
385 http_stream.GetTotalSentBytes()); | |
386 EXPECT_EQ(static_cast<int64_t>(resp.size() + body.size()), | |
387 http_stream.GetTotalReceivedBytes()); | |
388 | |
389 // Because the server closed the connection, we there shouldn't be a session | |
390 // in the pool anymore. | |
391 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
392 } | |
393 | |
394 // This unittest tests the request callback is properly called and handled. | |
395 TEST_F(SpdyHttpStreamTest, SendChunkedPostLastEmpty) { | |
396 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
397 SpdySerializedFrame chunk( | |
398 spdy_util_.ConstructSpdyDataFrame(1, nullptr, 0, true)); | |
399 MockWrite writes[] = { | |
400 CreateMockWrite(req, 0), // request | |
401 CreateMockWrite(chunk, 1), | |
402 }; | |
403 | |
404 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
405 MockRead reads[] = { | |
406 CreateMockRead(resp, 2), CreateMockRead(chunk, 3, SYNCHRONOUS), | |
407 MockRead(SYNCHRONOUS, 0, 4) // EOF | |
408 }; | |
409 | |
410 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
411 | |
412 ChunkedUploadDataStream upload_stream(0); | |
413 upload_stream.AppendData(nullptr, 0, true); | |
414 | |
415 HttpRequestInfo request; | |
416 request.method = "POST"; | |
417 request.url = url_; | |
418 request.upload_data_stream = &upload_stream; | |
419 | |
420 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
421 NetLogWithSource()), | |
422 IsOk()); | |
423 | |
424 TestCompletionCallback callback; | |
425 HttpResponseInfo response; | |
426 HttpRequestHeaders headers; | |
427 NetLogWithSource net_log; | |
428 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
429 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
430 CompletionCallback()), | |
431 IsOk()); | |
432 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
433 IsError(ERR_IO_PENDING)); | |
434 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
435 | |
436 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
437 | |
438 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk.size()), | |
439 http_stream.GetTotalSentBytes()); | |
440 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk.size()), | |
441 http_stream.GetTotalReceivedBytes()); | |
442 | |
443 // Because the server closed the connection, there shouldn't be a session | |
444 // in the pool anymore. | |
445 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
446 } | |
447 | |
448 TEST_F(SpdyHttpStreamTest, ConnectionClosedDuringChunkedPost) { | |
449 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
450 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, kUploadData, | |
451 kUploadDataSize, | |
452 /*fin=*/false)); | |
453 MockWrite writes[] = { | |
454 CreateMockWrite(req, 0), // Request | |
455 CreateMockWrite(body, 1) // First POST upload frame | |
456 }; | |
457 | |
458 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
459 MockRead reads[] = { | |
460 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2) // Server hangs up early. | |
461 }; | |
462 | |
463 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
464 | |
465 ChunkedUploadDataStream upload_stream(0); | |
466 // Append first chunk. | |
467 upload_stream.AppendData(kUploadData, kUploadDataSize, false); | |
468 | |
469 HttpRequestInfo request; | |
470 request.method = "POST"; | |
471 request.url = url_; | |
472 request.upload_data_stream = &upload_stream; | |
473 | |
474 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
475 NetLogWithSource()), | |
476 IsOk()); | |
477 | |
478 TestCompletionCallback callback; | |
479 HttpResponseInfo response; | |
480 HttpRequestHeaders headers; | |
481 NetLogWithSource net_log; | |
482 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
483 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
484 CompletionCallback()), | |
485 IsOk()); | |
486 | |
487 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
488 IsError(ERR_IO_PENDING)); | |
489 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
490 | |
491 EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED)); | |
492 | |
493 EXPECT_EQ(static_cast<int64_t>(req.size() + body.size()), | |
494 http_stream.GetTotalSentBytes()); | |
495 EXPECT_EQ(0, http_stream.GetTotalReceivedBytes()); | |
496 | |
497 // Because the server closed the connection, we there shouldn't be a session | |
498 // in the pool anymore. | |
499 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
500 | |
501 // Appending a second chunk now should not result in a crash. | |
502 upload_stream.AppendData(kUploadData, kUploadDataSize, true); | |
503 // Appending data is currently done synchronously, but seems best to be | |
504 // paranoid. | |
505 base::RunLoop().RunUntilIdle(); | |
506 | |
507 // The total sent and received bytes should be unchanged. | |
508 EXPECT_EQ(static_cast<int64_t>(req.size() + body.size()), | |
509 http_stream.GetTotalSentBytes()); | |
510 EXPECT_EQ(0, http_stream.GetTotalReceivedBytes()); | |
511 } | |
512 | |
513 // Test to ensure the SpdyStream state machine does not get confused when a | |
514 // chunk becomes available while a write is pending. | |
515 TEST_F(SpdyHttpStreamTest, DelayedSendChunkedPost) { | |
516 const char kUploadData1[] = "12345678"; | |
517 const int kUploadData1Size = arraysize(kUploadData1)-1; | |
518 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
519 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
520 SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame( | |
521 1, kUploadData1, kUploadData1Size, false)); | |
522 SpdySerializedFrame chunk3(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
523 MockWrite writes[] = { | |
524 CreateMockWrite(req, 0), | |
525 CreateMockWrite(chunk1, 1), // POST upload frames | |
526 CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3), | |
527 }; | |
528 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
529 MockRead reads[] = { | |
530 CreateMockRead(resp, 4), CreateMockRead(chunk1, 5), | |
531 CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7), | |
532 MockRead(ASYNC, 0, 8) // EOF | |
533 }; | |
534 | |
535 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
536 | |
537 ChunkedUploadDataStream upload_stream(0); | |
538 | |
539 HttpRequestInfo request; | |
540 request.method = "POST"; | |
541 request.url = url_; | |
542 request.upload_data_stream = &upload_stream; | |
543 | |
544 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
545 NetLogWithSource()), | |
546 IsOk()); | |
547 upload_stream.AppendData(kUploadData, kUploadDataSize, false); | |
548 | |
549 NetLogWithSource net_log; | |
550 std::unique_ptr<SpdyHttpStream> http_stream( | |
551 new SpdyHttpStream(session_, true, net_log.source())); | |
552 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
553 CompletionCallback()), | |
554 IsOk()); | |
555 | |
556 TestCompletionCallback callback; | |
557 HttpRequestHeaders headers; | |
558 HttpResponseInfo response; | |
559 // This will attempt to Write() the initial request and headers, which will | |
560 // complete asynchronously. | |
561 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
562 IsError(ERR_IO_PENDING)); | |
563 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
564 | |
565 // Complete the initial request write and the first chunk. | |
566 base::RunLoop().RunUntilIdle(); | |
567 ASSERT_FALSE(callback.have_result()); | |
568 | |
569 // Now append the final two chunks which will enqueue two more writes. | |
570 upload_stream.AppendData(kUploadData1, kUploadData1Size, false); | |
571 upload_stream.AppendData(kUploadData, kUploadDataSize, true); | |
572 | |
573 // Finish writing all the chunks and do all reads. | |
574 base::RunLoop().RunUntilIdle(); | |
575 ASSERT_TRUE(callback.have_result()); | |
576 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
577 | |
578 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size() + chunk2.size() + | |
579 chunk3.size()), | |
580 http_stream->GetTotalSentBytes()); | |
581 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk1.size() + chunk2.size() + | |
582 chunk3.size()), | |
583 http_stream->GetTotalReceivedBytes()); | |
584 | |
585 // Check response headers. | |
586 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
587 | |
588 // Check |chunk1| response. | |
589 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); | |
590 ASSERT_EQ(kUploadDataSize, | |
591 http_stream->ReadResponseBody( | |
592 buf1.get(), kUploadDataSize, callback.callback())); | |
593 EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize)); | |
594 | |
595 // Check |chunk2| response. | |
596 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size)); | |
597 ASSERT_EQ(kUploadData1Size, | |
598 http_stream->ReadResponseBody( | |
599 buf2.get(), kUploadData1Size, callback.callback())); | |
600 EXPECT_EQ(kUploadData1, SpdyString(buf2->data(), kUploadData1Size)); | |
601 | |
602 // Check |chunk3| response. | |
603 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize)); | |
604 ASSERT_EQ(kUploadDataSize, | |
605 http_stream->ReadResponseBody( | |
606 buf3.get(), kUploadDataSize, callback.callback())); | |
607 EXPECT_EQ(kUploadData, SpdyString(buf3->data(), kUploadDataSize)); | |
608 | |
609 ASSERT_TRUE(response.headers.get()); | |
610 ASSERT_EQ(200, response.headers->response_code()); | |
611 } | |
612 | |
613 // Test that the SpdyStream state machine can handle sending a final empty data | |
614 // frame when uploading a chunked data stream. | |
615 TEST_F(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) { | |
616 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
617 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
618 SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true)); | |
619 MockWrite writes[] = { | |
620 CreateMockWrite(req, 0), | |
621 CreateMockWrite(chunk1, 1), // POST upload frames | |
622 CreateMockWrite(chunk2, 2), | |
623 }; | |
624 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
625 MockRead reads[] = { | |
626 CreateMockRead(resp, 3), CreateMockRead(chunk1, 4), | |
627 CreateMockRead(chunk2, 5), MockRead(ASYNC, 0, 6) // EOF | |
628 }; | |
629 | |
630 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
631 | |
632 ChunkedUploadDataStream upload_stream(0); | |
633 | |
634 HttpRequestInfo request; | |
635 request.method = "POST"; | |
636 request.url = url_; | |
637 request.upload_data_stream = &upload_stream; | |
638 | |
639 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
640 NetLogWithSource()), | |
641 IsOk()); | |
642 upload_stream.AppendData(kUploadData, kUploadDataSize, false); | |
643 | |
644 NetLogWithSource net_log; | |
645 std::unique_ptr<SpdyHttpStream> http_stream( | |
646 new SpdyHttpStream(session_, true, net_log.source())); | |
647 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
648 CompletionCallback()), | |
649 IsOk()); | |
650 | |
651 TestCompletionCallback callback; | |
652 HttpRequestHeaders headers; | |
653 HttpResponseInfo response; | |
654 // This will attempt to Write() the initial request and headers, which will | |
655 // complete asynchronously. | |
656 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
657 IsError(ERR_IO_PENDING)); | |
658 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
659 | |
660 // Complete the initial request write and the first chunk. | |
661 base::RunLoop().RunUntilIdle(); | |
662 ASSERT_FALSE(callback.have_result()); | |
663 | |
664 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size()), | |
665 http_stream->GetTotalSentBytes()); | |
666 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes()); | |
667 | |
668 // Now end the stream with an empty data frame and the FIN set. | |
669 upload_stream.AppendData(nullptr, 0, true); | |
670 | |
671 // Finish writing the final frame, and perform all reads. | |
672 base::RunLoop().RunUntilIdle(); | |
673 ASSERT_TRUE(callback.have_result()); | |
674 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
675 | |
676 // Check response headers. | |
677 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
678 | |
679 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size() + chunk2.size()), | |
680 http_stream->GetTotalSentBytes()); | |
681 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk1.size() + chunk2.size()), | |
682 http_stream->GetTotalReceivedBytes()); | |
683 | |
684 // Check |chunk1| response. | |
685 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); | |
686 ASSERT_EQ(kUploadDataSize, | |
687 http_stream->ReadResponseBody( | |
688 buf1.get(), kUploadDataSize, callback.callback())); | |
689 EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize)); | |
690 | |
691 // Check |chunk2| response. | |
692 ASSERT_EQ(0, | |
693 http_stream->ReadResponseBody( | |
694 buf1.get(), kUploadDataSize, callback.callback())); | |
695 | |
696 ASSERT_TRUE(response.headers.get()); | |
697 ASSERT_EQ(200, response.headers->response_code()); | |
698 } | |
699 | |
700 // Test that the SpdyStream state machine handles a chunked upload with no | |
701 // payload. Unclear if this is a case worth supporting. | |
702 TEST_F(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) { | |
703 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
704 SpdySerializedFrame chunk(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true)); | |
705 MockWrite writes[] = { | |
706 CreateMockWrite(req, 0), CreateMockWrite(chunk, 1), | |
707 }; | |
708 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
709 MockRead reads[] = { | |
710 CreateMockRead(resp, 2), CreateMockRead(chunk, 3), | |
711 MockRead(ASYNC, 0, 4) // EOF | |
712 }; | |
713 | |
714 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
715 | |
716 ChunkedUploadDataStream upload_stream(0); | |
717 | |
718 HttpRequestInfo request; | |
719 request.method = "POST"; | |
720 request.url = url_; | |
721 request.upload_data_stream = &upload_stream; | |
722 | |
723 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
724 NetLogWithSource()), | |
725 IsOk()); | |
726 upload_stream.AppendData("", 0, true); | |
727 | |
728 NetLogWithSource net_log; | |
729 std::unique_ptr<SpdyHttpStream> http_stream( | |
730 new SpdyHttpStream(session_, true, net_log.source())); | |
731 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
732 CompletionCallback()), | |
733 IsOk()); | |
734 | |
735 TestCompletionCallback callback; | |
736 HttpRequestHeaders headers; | |
737 HttpResponseInfo response; | |
738 // This will attempt to Write() the initial request and headers, which will | |
739 // complete asynchronously. | |
740 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
741 IsError(ERR_IO_PENDING)); | |
742 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
743 | |
744 // Complete writing request, followed by a FIN. | |
745 base::RunLoop().RunUntilIdle(); | |
746 ASSERT_TRUE(callback.have_result()); | |
747 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
748 | |
749 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk.size()), | |
750 http_stream->GetTotalSentBytes()); | |
751 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk.size()), | |
752 http_stream->GetTotalReceivedBytes()); | |
753 | |
754 // Check response headers. | |
755 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
756 | |
757 // Check |chunk| response. | |
758 scoped_refptr<IOBuffer> buf(new IOBuffer(1)); | |
759 ASSERT_EQ(0, | |
760 http_stream->ReadResponseBody( | |
761 buf.get(), 1, callback.callback())); | |
762 | |
763 ASSERT_TRUE(response.headers.get()); | |
764 ASSERT_EQ(200, response.headers->response_code()); | |
765 } | |
766 | |
767 // Test case for https://crbug.com/50058. | |
768 TEST_F(SpdyHttpStreamTest, SpdyURLTest) { | |
769 const char* const full_url = "https://www.example.org/foo?query=what#anchor"; | |
770 const char* const base_url = "https://www.example.org/foo?query=what"; | |
771 SpdySerializedFrame req(spdy_util_.ConstructSpdyGet(base_url, 1, LOWEST)); | |
772 MockWrite writes[] = { | |
773 CreateMockWrite(req, 0), | |
774 }; | |
775 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
776 MockRead reads[] = { | |
777 CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF | |
778 }; | |
779 | |
780 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
781 | |
782 HttpRequestInfo request; | |
783 request.method = "GET"; | |
784 request.url = GURL(full_url); | |
785 TestCompletionCallback callback; | |
786 HttpResponseInfo response; | |
787 HttpRequestHeaders headers; | |
788 NetLogWithSource net_log; | |
789 std::unique_ptr<SpdyHttpStream> http_stream( | |
790 new SpdyHttpStream(session_, true, net_log.source())); | |
791 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
792 CompletionCallback()), | |
793 IsOk()); | |
794 | |
795 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
796 IsError(ERR_IO_PENDING)); | |
797 | |
798 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec()); | |
799 | |
800 callback.WaitForResult(); | |
801 | |
802 EXPECT_EQ(static_cast<int64_t>(req.size()), http_stream->GetTotalSentBytes()); | |
803 EXPECT_EQ(static_cast<int64_t>(resp.size()), | |
804 http_stream->GetTotalReceivedBytes()); | |
805 | |
806 // Because we abandoned the stream, we don't expect to find a session in the | |
807 // pool anymore. | |
808 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
809 } | |
810 | |
811 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be | |
812 // made available is handled correctly. | |
813 TEST_F(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) { | |
814 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
815 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
816 MockWrite writes[] = { | |
817 CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1), | |
818 }; | |
819 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
820 SpdySerializedFrame window_update( | |
821 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize)); | |
822 MockRead reads[] = { | |
823 CreateMockRead(window_update, 2), MockRead(ASYNC, ERR_IO_PENDING, 3), | |
824 CreateMockRead(resp, 4), CreateMockRead(chunk1, 5), | |
825 MockRead(ASYNC, 0, 6) // EOF | |
826 }; | |
827 | |
828 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
829 | |
830 ChunkedUploadDataStream upload_stream(0); | |
831 | |
832 HttpRequestInfo request; | |
833 request.method = "POST"; | |
834 request.url = url_; | |
835 request.upload_data_stream = &upload_stream; | |
836 | |
837 ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), | |
838 NetLogWithSource()), | |
839 IsOk()); | |
840 | |
841 NetLogWithSource net_log; | |
842 std::unique_ptr<SpdyHttpStream> http_stream( | |
843 new SpdyHttpStream(session_, true, net_log.source())); | |
844 ASSERT_THAT(http_stream->InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
845 CompletionCallback()), | |
846 IsOk()); | |
847 | |
848 HttpRequestHeaders headers; | |
849 HttpResponseInfo response; | |
850 // This will attempt to Write() the initial request and headers, which will | |
851 // complete asynchronously. | |
852 TestCompletionCallback callback; | |
853 EXPECT_THAT(http_stream->SendRequest(headers, &response, callback.callback()), | |
854 IsError(ERR_IO_PENDING)); | |
855 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
856 | |
857 // Complete the initial request write and first chunk. | |
858 base::RunLoop().RunUntilIdle(); | |
859 ASSERT_FALSE(callback.have_result()); | |
860 | |
861 EXPECT_EQ(static_cast<int64_t>(req.size()), http_stream->GetTotalSentBytes()); | |
862 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes()); | |
863 | |
864 upload_stream.AppendData(kUploadData, kUploadDataSize, true); | |
865 | |
866 // Verify that the window size has decreased. | |
867 ASSERT_TRUE(http_stream->stream() != nullptr); | |
868 EXPECT_NE(static_cast<int>(kDefaultInitialWindowSize), | |
869 http_stream->stream()->send_window_size()); | |
870 | |
871 // Read window update. | |
872 base::RunLoop().RunUntilIdle(); | |
873 | |
874 ASSERT_TRUE(callback.have_result()); | |
875 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
876 | |
877 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size()), | |
878 http_stream->GetTotalSentBytes()); | |
879 // The window update is not counted in the total received bytes. | |
880 EXPECT_EQ(0, http_stream->GetTotalReceivedBytes()); | |
881 | |
882 // Verify the window update. | |
883 ASSERT_TRUE(http_stream->stream() != nullptr); | |
884 EXPECT_EQ(static_cast<int>(kDefaultInitialWindowSize), | |
885 http_stream->stream()->send_window_size()); | |
886 | |
887 // Read rest of data. | |
888 sequenced_data_->Resume(); | |
889 base::RunLoop().RunUntilIdle(); | |
890 | |
891 EXPECT_EQ(static_cast<int64_t>(req.size() + chunk1.size()), | |
892 http_stream->GetTotalSentBytes()); | |
893 EXPECT_EQ(static_cast<int64_t>(resp.size() + chunk1.size()), | |
894 http_stream->GetTotalReceivedBytes()); | |
895 | |
896 // Check response headers. | |
897 ASSERT_THAT(http_stream->ReadResponseHeaders(callback.callback()), IsOk()); | |
898 | |
899 // Check |chunk1| response. | |
900 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); | |
901 ASSERT_EQ(kUploadDataSize, | |
902 http_stream->ReadResponseBody( | |
903 buf1.get(), kUploadDataSize, callback.callback())); | |
904 EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize)); | |
905 | |
906 ASSERT_TRUE(response.headers.get()); | |
907 ASSERT_EQ(200, response.headers->response_code()); | |
908 } | |
909 | |
910 TEST_F(SpdyHttpStreamTest, DataReadErrorSynchronous) { | |
911 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
912 | |
913 // Server receives ERROR_CODE_INTERNAL_ERROR on client's internal failure. | |
914 // The failure is a reading error in this case caused by | |
915 // UploadDataStream::Read(). | |
916 SpdySerializedFrame rst_frame( | |
917 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_INTERNAL_ERROR)); | |
918 | |
919 MockWrite writes[] = { | |
920 CreateMockWrite(req, 0, SYNCHRONOUS), // Request | |
921 CreateMockWrite(rst_frame, 1, SYNCHRONOUS) // Reset frame | |
922 }; | |
923 | |
924 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
925 | |
926 MockRead reads[] = { | |
927 CreateMockRead(resp, 2), MockRead(SYNCHRONOUS, 0, 3), | |
928 }; | |
929 | |
930 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
931 | |
932 ReadErrorUploadDataStream upload_data_stream( | |
933 ReadErrorUploadDataStream::FailureMode::SYNC); | |
934 ASSERT_THAT(upload_data_stream.Init(TestCompletionCallback().callback(), | |
935 NetLogWithSource()), | |
936 IsOk()); | |
937 | |
938 HttpRequestInfo request; | |
939 request.method = "POST"; | |
940 request.url = url_; | |
941 request.upload_data_stream = &upload_data_stream; | |
942 | |
943 TestCompletionCallback callback; | |
944 HttpResponseInfo response; | |
945 HttpRequestHeaders headers; | |
946 NetLogWithSource net_log; | |
947 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
948 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
949 CompletionCallback()), | |
950 IsOk()); | |
951 | |
952 int result = http_stream.SendRequest(headers, &response, callback.callback()); | |
953 EXPECT_THAT(callback.GetResult(result), IsError(ERR_FAILED)); | |
954 | |
955 // Run posted SpdyHttpStream::ResetStreamInternal() task. | |
956 base::RunLoop().RunUntilIdle(); | |
957 | |
958 // Because the server has not closed the connection yet, there shouldn't be | |
959 // a stream but a session in the pool | |
960 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
961 } | |
962 | |
963 TEST_F(SpdyHttpStreamTest, DataReadErrorAsynchronous) { | |
964 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
965 | |
966 // Server receives ERROR_CODE_INTERNAL_ERROR on client's internal failure. | |
967 // The failure is a reading error in this case caused by | |
968 // UploadDataStream::Read(). | |
969 SpdySerializedFrame rst_frame( | |
970 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_INTERNAL_ERROR)); | |
971 | |
972 MockWrite writes[] = { | |
973 CreateMockWrite(req, 0), // Request | |
974 CreateMockWrite(rst_frame, 1) // Reset frame | |
975 }; | |
976 | |
977 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
978 | |
979 MockRead reads[] = { | |
980 MockRead(ASYNC, 0, 2), | |
981 }; | |
982 | |
983 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
984 | |
985 ReadErrorUploadDataStream upload_data_stream( | |
986 ReadErrorUploadDataStream::FailureMode::ASYNC); | |
987 ASSERT_THAT(upload_data_stream.Init(TestCompletionCallback().callback(), | |
988 NetLogWithSource()), | |
989 IsOk()); | |
990 | |
991 HttpRequestInfo request; | |
992 request.method = "POST"; | |
993 request.url = url_; | |
994 request.upload_data_stream = &upload_data_stream; | |
995 | |
996 TestCompletionCallback callback; | |
997 HttpResponseInfo response; | |
998 HttpRequestHeaders headers; | |
999 NetLogWithSource net_log; | |
1000 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
1001 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
1002 CompletionCallback()), | |
1003 IsOk()); | |
1004 | |
1005 int result = http_stream.SendRequest(headers, &response, callback.callback()); | |
1006 EXPECT_THAT(result, IsError(ERR_IO_PENDING)); | |
1007 EXPECT_THAT(callback.GetResult(result), IsError(ERR_FAILED)); | |
1008 | |
1009 // Run posted SpdyHttpStream::ResetStreamInternal() task. | |
1010 base::RunLoop().RunUntilIdle(); | |
1011 | |
1012 // Because the server has closed the connection, there shouldn't be a session | |
1013 // in the pool anymore. | |
1014 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
1015 } | |
1016 | |
1017 // Regression test for https://crbug.com/622447. | |
1018 TEST_F(SpdyHttpStreamTest, RequestCallbackCancelsStream) { | |
1019 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
1020 SpdySerializedFrame chunk( | |
1021 spdy_util_.ConstructSpdyDataFrame(1, nullptr, 0, true)); | |
1022 SpdySerializedFrame rst( | |
1023 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
1024 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(chunk, 1), | |
1025 CreateMockWrite(rst, 2)}; | |
1026 MockRead reads[] = {MockRead(ASYNC, 0, 3)}; | |
1027 InitSession(reads, arraysize(reads), writes, arraysize(writes)); | |
1028 | |
1029 HttpRequestInfo request; | |
1030 request.method = "POST"; | |
1031 request.url = url_; | |
1032 ChunkedUploadDataStream upload_stream(0); | |
1033 request.upload_data_stream = &upload_stream; | |
1034 | |
1035 TestCompletionCallback upload_callback; | |
1036 ASSERT_THAT( | |
1037 upload_stream.Init(upload_callback.callback(), NetLogWithSource()), | |
1038 IsOk()); | |
1039 upload_stream.AppendData("", 0, true); | |
1040 | |
1041 NetLogWithSource net_log; | |
1042 SpdyHttpStream http_stream(session_, true, net_log.source()); | |
1043 ASSERT_THAT(http_stream.InitializeStream(&request, DEFAULT_PRIORITY, net_log, | |
1044 CompletionCallback()), | |
1045 IsOk()); | |
1046 | |
1047 CancelStreamCallback callback(&http_stream); | |
1048 HttpRequestHeaders headers; | |
1049 HttpResponseInfo response; | |
1050 // This will attempt to Write() the initial request and headers, which will | |
1051 // complete asynchronously. | |
1052 EXPECT_THAT(http_stream.SendRequest(headers, &response, callback.callback()), | |
1053 IsError(ERR_IO_PENDING)); | |
1054 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
1055 | |
1056 // The callback cancels |http_stream|. | |
1057 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
1058 | |
1059 // Finish async network reads/writes. | |
1060 base::RunLoop().RunUntilIdle(); | |
1061 | |
1062 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key_)); | |
1063 } | |
1064 | |
1065 // TODO(willchan): Write a longer test for SpdyStream that exercises all | |
1066 // methods. | |
1067 | |
1068 } // namespace test | |
1069 | |
1070 } // namespace net | |
OLD | NEW |