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