Internal QUICHE change

PiperOrigin-RevId: 260509821
Change-Id: I493ff904b175d015825232cd3cccf5e58c3f1300
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index c9fa852..5d58c75 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -152,18 +152,16 @@
     return stream_->OnPushPromiseFrameEnd();
   }
 
-  bool OnUnknownFrameStart(uint64_t /* frame_type */,
-                           Http3FrameLengths /* frame_length */) override {
-    // TODO(b/137554973): Consume frame header.
-    return true;
+  bool OnUnknownFrameStart(uint64_t frame_type,
+                           Http3FrameLengths frame_length) override {
+    return stream_->OnUnknownFrameStart(frame_type, frame_length);
   }
 
-  bool OnUnknownFramePayload(QuicStringPiece /* payload */) override {
-    // TODO(b/137554973): Consume frame payload.
-    return true;
+  bool OnUnknownFramePayload(QuicStringPiece payload) override {
+    return stream_->OnUnknownFramePayload(payload);
   }
 
-  bool OnUnknownFrameEnd() override { return true; }
+  bool OnUnknownFrameEnd() override { return stream_->OnUnknownFrameEnd(); }
 
  private:
   void CloseConnectionOnWrongFrame(std::string frame_type) {
@@ -967,6 +965,30 @@
   return !sequencer()->IsClosed() && !reading_stopped();
 }
 
+bool QuicSpdyStream::OnUnknownFrameStart(uint64_t frame_type,
+                                         Http3FrameLengths frame_length) {
+  // Ignore unknown frames, but consume frame header.
+  QUIC_DVLOG(1) << "Discarding " << frame_length.header_length
+                << " byte long frame header of frame of unknown type "
+                << frame_type << ".";
+  QUIC_DVLOG(1) << "Frame total payload length is "
+                << frame_length.payload_length << ".";
+  sequencer()->MarkConsumed(body_buffer_.OnNonBody(frame_length.header_length));
+  return true;
+}
+
+bool QuicSpdyStream::OnUnknownFramePayload(QuicStringPiece payload) {
+  // Ignore unknown frames, but consume frame payload.
+  QUIC_DVLOG(1) << "Discarding " << payload.size()
+                << " bytes of payload of frame of unknown type.";
+  sequencer()->MarkConsumed(body_buffer_.OnNonBody(payload.size()));
+  return true;
+}
+
+bool QuicSpdyStream::OnUnknownFrameEnd() {
+  return true;
+}
+
 void QuicSpdyStream::ProcessDecodedHeaders(const QuicHeaderList& headers) {
   if (spdy_session_->promised_stream_id() ==
       QuicUtils::GetInvalidStreamId(
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index b2e9c84..48cb948 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -263,6 +263,9 @@
                                QuicByteCount push_id_length);
   bool OnPushPromiseFramePayload(QuicStringPiece payload);
   bool OnPushPromiseFrameEnd();
+  bool OnUnknownFrameStart(uint64_t frame_type, Http3FrameLengths frame_length);
+  bool OnUnknownFramePayload(QuicStringPiece payload);
+  bool OnUnknownFrameEnd();
 
   // Called internally when headers are decoded.
   void ProcessDecodedHeaders(const QuicHeaderList& headers);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index f641dce..1defc5a 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -228,6 +228,24 @@
     return QuicStrCat(data_frame_header, payload);
   }
 
+  std::string UnknownFrame(uint64_t frame_type, QuicStringPiece payload) {
+    std::string frame;
+    const size_t length = QuicDataWriter::GetVarInt62Len(frame_type) +
+                          QuicDataWriter::GetVarInt62Len(payload.size()) +
+                          payload.size();
+    frame.resize(length);
+
+    QuicDataWriter writer(length, frame.data());
+    writer.WriteVarInt62(frame_type);
+    writer.WriteStringPieceVarInt62(payload);
+    // Even though integers can be encoded with different lengths,
+    // QuicDataWriter is expected to produce an encoding in Write*() of length
+    // promised in GetVarInt62Len().
+    DCHECK_EQ(length, writer.length());
+
+    return frame;
+  }
+
   MockQuicConnectionHelper helper_;
   MockAlarmFactory alarm_factory_;
   MockQuicConnection* connection_;
@@ -2086,7 +2104,7 @@
 
 // Test that stream bytes are consumed (by calling
 // sequencer()->MarkConsumed()) incrementally, as soon as possible.
-TEST_P(QuicSpdyStreamIncrementalConsumptionTest, IncrementalConsumptionTest) {
+TEST_P(QuicSpdyStreamIncrementalConsumptionTest, OnlyKnownFrames) {
   if (!VersionUsesQpack(GetParam().transport_version)) {
     return;
   }
@@ -2149,6 +2167,92 @@
               ElementsAre(Pair("custom-key", "custom-value")));
 }
 
+TEST_P(QuicSpdyStreamIncrementalConsumptionTest, UnknownFramesInterleaved) {
+  if (!VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
+  Initialize(!kShouldProcessData);
+
+  // Unknown frame of reserved type before HEADERS is consumed immediately.
+  std::string unknown_frame1 = UnknownFrame(0x21, "foo");
+  OnStreamFrame(unknown_frame1);
+  EXPECT_EQ(unknown_frame1.size(), NewlyConsumedBytes());
+
+  // HEADERS frame with QPACK encoded single header field "foo: bar".
+  std::string headers =
+      HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172"));
+
+  // All HEADERS frame bytes are consumed even if the frame is not received
+  // completely.
+  OnStreamFrame(QuicStringPiece(headers).substr(0, headers.size() - 1));
+  EXPECT_EQ(headers.size() - 1, NewlyConsumedBytes());
+
+  // The rest of the HEADERS frame is also consumed immediately.
+  OnStreamFrame(QuicStringPiece(headers).substr(headers.size() - 1));
+  EXPECT_EQ(1u, NewlyConsumedBytes());
+
+  // Verify headers.
+  EXPECT_THAT(stream_->header_list(), ElementsAre(Pair("foo", "bar")));
+  stream_->ConsumeHeaderList();
+
+  // Frame of unknown, not reserved type between HEADERS and DATA is consumed
+  // immediately.
+  std::string unknown_frame2 = UnknownFrame(0x3a, "");
+  OnStreamFrame(unknown_frame2);
+  EXPECT_EQ(unknown_frame2.size(), NewlyConsumedBytes());
+
+  // DATA frame.
+  QuicStringPiece data_payload(kDataFramePayload);
+  std::string data_frame = DataFrame(data_payload);
+  QuicByteCount data_frame_header_length =
+      data_frame.size() - data_payload.size();
+
+  // DATA frame header is consumed.
+  // DATA frame payload is not consumed because payload has to be buffered.
+  OnStreamFrame(data_frame);
+  EXPECT_EQ(data_frame_header_length, NewlyConsumedBytes());
+
+  // Frame of unknown, not reserved type is not consumed because DATA payload is
+  // still buffered.
+  std::string unknown_frame3 = UnknownFrame(0x39, "bar");
+  OnStreamFrame(unknown_frame3);
+  EXPECT_EQ(0u, NewlyConsumedBytes());
+
+  // Consume all but last byte of data.
+  EXPECT_EQ(data_payload.substr(0, data_payload.size() - 1),
+            ReadFromStream(data_payload.size() - 1));
+  EXPECT_EQ(data_payload.size() - 1, NewlyConsumedBytes());
+
+  // Trailing HEADERS frame with QPACK encoded
+  // single header field "custom-key: custom-value".
+  std::string trailers = HeadersFrame(
+      QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
+
+  // No bytes are consumed, because last byte of DATA payload is still buffered.
+  OnStreamFrame(QuicStringPiece(trailers).substr(0, trailers.size() - 1));
+  EXPECT_EQ(0u, NewlyConsumedBytes());
+
+  // Reading last byte of DATA payload triggers consumption of all data received
+  // so far, even though last HEADERS frame has not been received completely.
+  EXPECT_EQ(data_payload.substr(data_payload.size() - 1), ReadFromStream(1));
+  EXPECT_EQ(1 + unknown_frame3.size() + trailers.size() - 1,
+            NewlyConsumedBytes());
+
+  // Last byte of trailers is immediately consumed.
+  OnStreamFrame(QuicStringPiece(trailers).substr(trailers.size() - 1));
+  EXPECT_EQ(1u, NewlyConsumedBytes());
+
+  // Verify trailers.
+  EXPECT_THAT(stream_->received_trailers(),
+              ElementsAre(Pair("custom-key", "custom-value")));
+
+  // Unknown frame of reserved type after trailers is consumed immediately.
+  std::string unknown_frame4 = UnknownFrame(0x40, "");
+  OnStreamFrame(unknown_frame4);
+  EXPECT_EQ(unknown_frame4.size(), NewlyConsumedBytes());
+}
+
 TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) {
   Initialize(kShouldProcessData);
   if (!HasFrameHeader()) {