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