Cloned from CL 231494986 by 'g4 patch'.
Original change by bnc@bnc:majom-bnc-chromium-google3-headers-git5:2181:citc on 2019/01/29 16:21:02.

Send HEADERS on the request stream.

In QUIC version 99, send HEADERS (including trailers) on the request stream
instead of the headers stream, compressed with QPACK instead of HPACK.

gfe-relnote: Send HEADERS on the request stream in QUIC version 99 only.  Not flag protected.
PiperOrigin-RevId: 249121660
Change-Id: I933f9433da8bfffc8c2979aea742d485639b33c5
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index c45a899..eb9a0b3 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -78,6 +78,11 @@
                               ack_listener) override {
     saved_headers_ = std::move(header_block);
     WriteHeadersMock(fin);
+    if (VersionUsesQpack(transport_version())) {
+      // In this case, call QuicSpdyStream::WriteHeadersImpl() that does the
+      // actual work of closing the stream.
+      QuicSpdyStream::WriteHeadersImpl(saved_headers_.Clone(), fin, nullptr);
+    }
     return 0;
   }
 
@@ -212,10 +217,23 @@
   QuicHeaderList headers;
   stream_->OnStreamHeadersPriority(kV3HighestPriority);
 
-  EXPECT_CALL(*session_,
-              SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0));
+  const bool version_uses_qpack =
+      VersionUsesQpack(connection_->transport_version());
+
+  if (version_uses_qpack) {
+    EXPECT_CALL(*connection_,
+                CloseConnection(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE,
+                                "Too large headers received on stream 4", _));
+  } else {
+    EXPECT_CALL(*session_,
+                SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0));
+  }
+
   stream_->OnStreamHeaderList(false, 1 << 20, headers);
-  EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, stream_->stream_error());
+
+  if (!version_uses_qpack) {
+    EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, stream_->stream_error());
+  }
 }
 
 TEST_P(QuicSpdyStreamTest, ProcessHeaderListWithFin) {
@@ -919,7 +937,11 @@
   trailers_block["key2"] = "value2";
   trailers_block["key3"] = "value3";
   SpdyHeaderBlock trailers_block_with_final_offset = trailers_block.Clone();
-  trailers_block_with_final_offset[kFinalOffsetHeaderKey] = "0";
+  if (!VersionUsesQpack(GetParam().transport_version)) {
+    // :final-offset pseudo-header is only added if trailers are sent
+    // on the headers stream.
+    trailers_block_with_final_offset[kFinalOffsetHeaderKey] = "0";
+  }
   total_bytes = 0;
   QuicHeaderList trailers;
   for (const auto& p : trailers_block_with_final_offset) {
@@ -943,6 +965,12 @@
   // body, stream is closed at the right offset.
   Initialize(kShouldProcessData);
 
+  // kFinalOffsetHeaderKey is not used when HEADERS are sent on the
+  // request/response stream.
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
   // Receive initial headers.
   QuicHeaderList headers = ProcessHeaders(false, headers_);
   stream_->ConsumeHeaderList();
@@ -988,6 +1016,12 @@
   // Test that receiving trailers without a final offset field is an error.
   Initialize(kShouldProcessData);
 
+  // kFinalOffsetHeaderKey is not used when HEADERS are sent on the
+  // request/response stream.
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
   // Receive initial headers.
   ProcessHeaders(false, headers_);
   stream_->ConsumeHeaderList();
@@ -1010,10 +1044,60 @@
                               trailers.uncompressed_header_bytes(), trailers);
 }
 
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersOnRequestStream) {
+  Initialize(kShouldProcessData);
+
+  if (!VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
+  // Receive initial headers.
+  QuicHeaderList headers = ProcessHeaders(false, headers_);
+  stream_->ConsumeHeaderList();
+
+  const std::string body = "this is the body";
+  std::unique_ptr<char[]> buf;
+  QuicByteCount header_length =
+      encoder_.SerializeDataFrameHeader(body.length(), &buf);
+  std::string header = std::string(buf.get(), header_length);
+  std::string data = HasFrameHeader() ? header + body : body;
+
+  // Receive trailing headers.
+  SpdyHeaderBlock trailers_block;
+  trailers_block["key1"] = "value1";
+  trailers_block["key2"] = "value2";
+  trailers_block["key3"] = "value3";
+
+  QuicHeaderList trailers = ProcessHeaders(true, trailers_block);
+
+  // The trailers should be decompressed, and readable from the stream.
+  EXPECT_TRUE(stream_->trailers_decompressed());
+
+  EXPECT_EQ(trailers_block, stream_->received_trailers());
+
+  // Consuming the trailers erases them from the stream.
+  stream_->MarkTrailersConsumed();
+  EXPECT_TRUE(stream_->FinishedReadingTrailers());
+  EXPECT_TRUE(stream_->IsDoneReading());
+
+  // Receive and consume body.
+  QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/false,
+                        0, data);
+  stream_->OnStreamFrame(frame);
+  EXPECT_EQ(body, stream_->data());
+  EXPECT_TRUE(stream_->IsDoneReading());
+}
+
 TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutFin) {
   // Test that received Trailers must always have the FIN set.
   Initialize(kShouldProcessData);
 
+  // In IETF QUIC, there is no such thing as FIN flag on HTTP/3 frames like the
+  // HEADERS frame.
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
   // Receive initial headers.
   auto headers = AsHeaderList(headers_);
   stream_->OnStreamHeaderList(/*fin=*/false,
@@ -1053,6 +1137,13 @@
   // If body data are received with a FIN, no trailers should then arrive.
   Initialize(kShouldProcessData);
 
+  // If HEADERS frames are sent on the request/response stream,
+  // then the sequencer will block them from reaching QuicSpdyStream
+  // after the stream is closed.
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
   // Receive initial headers without FIN set.
   ProcessHeaders(false, headers_);
   stream_->ConsumeHeaderList();
@@ -1101,6 +1192,12 @@
   // to be sent on a stream.
   Initialize(kShouldProcessData);
 
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    // In this case, TestStream::WriteHeadersImpl() does not prevent writes.
+    EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _))
+        .Times(AtLeast(1));
+  }
+
   // Write the initial headers, without a FIN.
   EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
@@ -1118,6 +1215,12 @@
   // peer contain the final offset field indicating last byte of data.
   Initialize(kShouldProcessData);
 
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    // In this case, TestStream::WriteHeadersImpl() does not prevent writes.
+    EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _))
+        .Times(AtLeast(1));
+  }
+
   // Write the initial headers.
   EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
@@ -1137,12 +1240,18 @@
   // number of body bytes written (including queued bytes).
   SpdyHeaderBlock trailers;
   trailers["trailer key"] = "trailer value";
-  SpdyHeaderBlock trailers_with_offset(trailers.Clone());
-  trailers_with_offset[kFinalOffsetHeaderKey] =
-      QuicTextUtils::Uint64ToString(body.length() + header_length);
+
+  SpdyHeaderBlock expected_trailers(trailers.Clone());
+  // :final-offset pseudo-header is only added if trailers are sent
+  // on the headers stream.
+  if (!VersionUsesQpack(GetParam().transport_version)) {
+    expected_trailers[kFinalOffsetHeaderKey] =
+        QuicTextUtils::Uint64ToString(body.length() + header_length);
+  }
+
   EXPECT_CALL(*stream_, WriteHeadersMock(true));
   stream_->WriteTrailers(std::move(trailers), nullptr);
-  EXPECT_EQ(trailers_with_offset, stream_->saved_headers());
+  EXPECT_EQ(expected_trailers, stream_->saved_headers());
 }
 
 TEST_P(QuicSpdyStreamTest, WritingTrailersClosesWriteSide) {
@@ -1150,12 +1259,16 @@
   // (headers and body), that this closes the stream for writing.
   Initialize(kShouldProcessData);
 
+  // Expect data being written on the stream.  In addition to that, headers are
+  // also written on the stream in case of IETF QUIC.
+  EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _))
+      .Times(AtLeast(1));
+
   // Write the initial headers.
   EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // Write non-zero body data.
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
   const int kBodySize = 1 * 1024;  // 1 kB
   stream_->WriteOrBufferBody(std::string(kBodySize, 'x'), false);
   EXPECT_EQ(0u, stream_->BufferedDataBytes());
@@ -1168,6 +1281,13 @@
 }
 
 TEST_P(QuicSpdyStreamTest, WritingTrailersWithQueuedBytes) {
+  // This test exercises sending trailers on the headers stream while data is
+  // still queued on the response/request stream.  In IETF QUIC, data and
+  // trailers are sent on the same stream, so this test does not apply.
+  if (VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
   // Test that the stream is not closed for writing when trailers are sent
   // while there are still body bytes queued.
   testing::InSequence seq;
@@ -1202,7 +1322,10 @@
 
 TEST_P(QuicSpdyStreamTest, WritingTrailersAfterFIN) {
   // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
-  if (GetParam() != AllSupportedVersions()[0]) {
+  // In IETF QUIC, there is no such thing as FIN flag on HTTP/3 frames like the
+  // HEADERS frame.  That is version 99, which is element 0 of the array, so
+  // pick another element.
+  if (GetParam() != AllSupportedVersions()[1]) {
     return;
   }
 
@@ -1554,6 +1677,65 @@
       QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
 }
 
+TEST_P(QuicSpdyStreamTest, HeadersFrameOnRequestStream) {
+  if (!VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
+  Initialize(kShouldProcessData);
+
+  // QPACK encoded header block with single header field "foo: bar".
+  std::string headers_frame_payload =
+      QuicTextUtils::HexDecode("00002a94e703626172");
+  std::unique_ptr<char[]> headers_buffer;
+  QuicByteCount headers_frame_header_length =
+      encoder_.SerializeHeadersFrameHeader(headers_frame_payload.length(),
+                                           &headers_buffer);
+  QuicStringPiece headers_frame_header(headers_buffer.get(),
+                                       headers_frame_header_length);
+
+  std::string data_frame_payload = "some data";
+  std::unique_ptr<char[]> data_buffer;
+  QuicByteCount data_frame_header_length = encoder_.SerializeDataFrameHeader(
+      data_frame_payload.length(), &data_buffer);
+  QuicStringPiece data_frame_header(data_buffer.get(),
+                                    data_frame_header_length);
+
+  // QPACK encoded header block with single header field
+  // "custom-key: custom-value".
+  std::string trailers_frame_payload =
+      QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf");
+  std::unique_ptr<char[]> trailers_buffer;
+  QuicByteCount trailers_frame_header_length =
+      encoder_.SerializeHeadersFrameHeader(trailers_frame_payload.length(),
+                                           &trailers_buffer);
+  QuicStringPiece trailers_frame_header(trailers_buffer.get(),
+                                        trailers_frame_header_length);
+
+  std::string stream_frame_payload = QuicStrCat(
+      headers_frame_header, headers_frame_payload, data_frame_header,
+      data_frame_payload, trailers_frame_header, trailers_frame_payload);
+  QuicStreamFrame frame(stream_->id(), false, 0, stream_frame_payload);
+  stream_->OnStreamFrame(frame);
+
+  auto it = stream_->header_list().begin();
+  ASSERT_TRUE(it != stream_->header_list().end());
+  EXPECT_EQ("foo", it->first);
+  EXPECT_EQ("bar", it->second);
+  ++it;
+  EXPECT_TRUE(it == stream_->header_list().end());
+
+  // QuicSpdyStream only calls OnBodyAvailable()
+  // after the header list has been consumed.
+  EXPECT_EQ("", stream_->data());
+  stream_->ConsumeHeaderList();
+  EXPECT_EQ("some data", stream_->data());
+
+  const spdy::SpdyHeaderBlock& trailers = stream_->received_trailers();
+  EXPECT_THAT(trailers, testing::ElementsAre(
+                            testing::Pair("custom-key", "custom-value")));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic