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