Add test for stream reset before body is sent.

Add QuicHttpStreamTest to verify that RESET_STREAM with H3_NO_ERROR
before request data is sent is handled properly.  From
https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-http-message-exchanges:
"A server can send a complete response prior to the client sending an
entire request [...]  The error code H3_NO_ERROR SHOULD be used when
requesting that the client stop sending on the request stream. Clients
MUST NOT discard complete responses as a result of having their request
terminated abruptly [...]"

Change-Id: I4ff90a8b75af312ae207c5adfcc6a0b8e83caedf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2185394
Reviewed-by: David Schinazi <[email protected]>
Commit-Queue: Bence Béky <[email protected]>
Cr-Commit-Position: refs/heads/master@{#766675}
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 7be5dadf..bcb1def7 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -1601,6 +1601,105 @@
             stream_->GetTotalReceivedBytes());
 }
 
+TEST_P(QuicHttpStreamTest, SendChunkedPostRequestAbortedByResetStream) {
+  SetRequest("POST", "/", DEFAULT_PRIORITY);
+  size_t chunk_size = strlen(kUploadData);
+  size_t spdy_request_headers_frame_length;
+  int packet_number = 1;
+
+  if (version_.UsesHttp3()) {
+    AddWrite(ConstructInitialSettingsPacket(packet_number++));
+  }
+
+  std::string header = ConstructDataHeader(chunk_size);
+  if (version_.HasIetfQuicFrames()) {
+    AddWrite(ConstructRequestHeadersAndDataFramesPacket(
+        packet_number++, GetNthClientInitiatedBidirectionalStreamId(0),
+        kIncludeVersion, !kFin, DEFAULT_PRIORITY, 0,
+        &spdy_request_headers_frame_length, {header, kUploadData}));
+    AddWrite(ConstructClientAckPacket(packet_number++, 3, 1, 2));
+    AddWrite(client_maker_.MakeRstPacket(
+        packet_number++,
+        /* include_version = */ true, stream_id_, quic::QUIC_STREAM_NO_ERROR,
+        /* include_stop_sending_if_v99 = */ false));
+  } else {
+    AddWrite(ConstructRequestHeadersAndDataFramesPacket(
+        packet_number++, GetNthClientInitiatedBidirectionalStreamId(0),
+        kIncludeVersion, !kFin, DEFAULT_PRIORITY, 0,
+        &spdy_request_headers_frame_length, {kUploadData}));
+    AddWrite(ConstructClientAckPacket(packet_number++, 3, 1, 2));
+    AddWrite(client_maker_.MakeAckAndRstPacket(
+        packet_number++,
+        /* include_version = */ false, stream_id_,
+        quic::QUIC_RST_ACKNOWLEDGEMENT, 4, 1, 1,
+        /* send_feedback = */ false,
+        /* include_stop_sending_if_v99 = */ false));
+  }
+
+  Initialize();
+
+  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
+  auto* chunked_upload_stream =
+      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);
+
+  request_.method = "POST";
+  request_.url = GURL("https://www.example.org/");
+  request_.upload_data_stream = upload_data_stream_.get();
+  ASSERT_THAT(request_.upload_data_stream->Init(
+                  TestCompletionCallback().callback(), NetLogWithSource()),
+              IsOk());
+  ASSERT_THAT(stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
+                                        net_log_.bound(), callback_.callback()),
+              IsOk());
+  ASSERT_THAT(stream_->SendRequest(headers_, &response_, callback_.callback()),
+              IsError(ERR_IO_PENDING));
+
+  // Ack both packets in the request.
+  ProcessPacket(ConstructServerAckPacket(1, 1, 1, 1));
+
+  // Send the response headers (but not the body).
+  SetResponse("200 OK", string());
+  size_t spdy_response_headers_frame_length;
+  ProcessPacket(ConstructResponseHeadersPacket(
+      2, !kFin, &spdy_response_headers_frame_length));
+
+  // Send the response body.
+  const char kResponseBody[] = "Hello world!";
+  std::string header2 = ConstructDataHeader(strlen(kResponseBody));
+  ProcessPacket(
+      ConstructServerDataPacket(3, false, kFin, header2 + kResponseBody));
+
+  // Server resets stream with H3_NO_ERROR before request body is complete.
+  ProcessPacket(server_maker_.MakeRstPacket(4, /* include_version = */ false,
+                                            stream_id_,
+                                            quic::QUIC_STREAM_NO_ERROR));
+
+  // Finish feeding request body to QuicHttpStream.  Data will be discarded.
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+
+  // Verify response.
+  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), IsOk());
+  ASSERT_TRUE(response_.headers.get());
+  EXPECT_EQ(200, response_.headers->response_code());
+  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+  ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
+            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
+                                      callback_.callback()));
+  EXPECT_TRUE(stream_->IsResponseBodyComplete());
+  EXPECT_TRUE(AtEof());
+
+  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
+  // headers and payload.
+  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
+                                 strlen(kUploadData) + header.length()),
+            stream_->GetTotalSentBytes());
+  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
+                                 strlen(kResponseBody) + header2.length()),
+            stream_->GetTotalReceivedBytes());
+}
+
 TEST_P(QuicHttpStreamTest, DestroyedEarly) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;