Change HTTP/3 frame type from 1 byte integer to variable length integer, and
allow partial delivery.

gfe-relnote: Used in version 99 only.
PiperOrigin-RevId: 244287295
Change-Id: Ic9a58f2822815006295f47f23f312b1f2c040f4e
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index ec0062a..653652f 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -24,8 +24,6 @@
   return (flags >> offset) & GetMaskFromNumBits(num_bits);
 }
 
-// Length of the type field of HTTP/3 frames.
-static const QuicByteCount kFrameTypeLength = 1;
 // Length of the weight field of a priority frame.
 static const size_t kPriorityWeightLength = 1;
 // Length of a priority frame's first byte.
@@ -41,6 +39,8 @@
       remaining_length_field_length_(0),
       current_frame_length_(0),
       remaining_frame_length_(0),
+      current_type_field_length_(0),
+      remaining_type_field_length_(0),
       error_(QUIC_NO_ERROR),
       error_detail_("") {}
 
@@ -97,9 +97,39 @@
 
 void HttpDecoder::ReadFrameType(QuicDataReader* reader) {
   DCHECK_NE(0u, reader->BytesRemaining());
-  if (!reader->ReadUInt8(&current_frame_type_)) {
-    RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame type");
-    return;
+  if (current_type_field_length_ == 0) {
+    // A new frame is coming.
+    current_type_field_length_ = reader->PeekVarInt62Length();
+    if (current_type_field_length_ == 0) {
+      RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame type length");
+      visitor_->OnError(this);
+      return;
+    }
+    if (current_type_field_length_ <= reader->BytesRemaining()) {
+      // The reader has all type data needed, so no need to buffer.
+      if (!reader->ReadVarInt62(&current_frame_type_)) {
+        RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame type");
+        return;
+      }
+    } else {
+      // Buffer a new type field.
+      remaining_type_field_length_ = current_type_field_length_;
+      BufferFrameType(reader);
+      return;
+    }
+  } else {
+    // Buffer the existing type field.
+    BufferFrameType(reader);
+    // The frame is still not buffered completely.
+    if (remaining_type_field_length_ != 0) {
+      return;
+    }
+    QuicDataReader type_reader(type_buffer_.data(), current_type_field_length_);
+    if (!type_reader.ReadVarInt62(&current_frame_type_)) {
+      RaiseError(QUIC_INTERNAL_ERROR, "Unable to read buffered frame type");
+      visitor_->OnError(this);
+      return;
+    }
   }
 
   if (current_frame_length_ > MaxFrameLength(current_frame_type_)) {
@@ -112,13 +142,16 @@
   // frame payload.
   if (current_frame_type_ == 0x0) {
     visitor_->OnDataFrameStart(Http3FrameLengths(
-        current_length_field_size_ + kFrameTypeLength, current_frame_length_));
+        current_length_field_size_ + current_type_field_length_,
+        current_frame_length_));
   } else if (current_frame_type_ == 0x1) {
     visitor_->OnHeadersFrameStart(Http3FrameLengths(
-        current_length_field_size_ + kFrameTypeLength, current_frame_length_));
+        current_length_field_size_ + current_type_field_length_,
+        current_frame_length_));
   } else if (current_frame_type_ == 0x4) {
     visitor_->OnSettingsFrameStart(Http3FrameLengths(
-        current_length_field_size_ + kFrameTypeLength, current_frame_length_));
+        current_length_field_size_ + current_type_field_length_,
+        current_frame_length_));
   }
 
   state_ = (remaining_frame_length_ == 0) ? STATE_FINISH_PARSING
@@ -331,6 +364,7 @@
   }
 
   current_length_field_size_ = 0;
+  current_type_field_length_ = 0;
   state_ = STATE_READING_FRAME_LENGTH;
 }
 
@@ -346,6 +380,7 @@
   if (remaining_frame_length_ == 0) {
     state_ = STATE_READING_FRAME_LENGTH;
     current_length_field_size_ = 0;
+    current_type_field_length_ = 0;
   }
 }
 
@@ -391,6 +426,22 @@
   remaining_length_field_length_ -= bytes_to_read;
 }
 
+void HttpDecoder::BufferFrameType(QuicDataReader* reader) {
+  if (current_type_field_length_ == remaining_type_field_length_) {
+    type_buffer_.fill(0);
+  }
+  QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+      remaining_type_field_length_, reader->BytesRemaining());
+  if (!reader->ReadBytes(type_buffer_.data() + current_type_field_length_ -
+                             remaining_type_field_length_,
+                         bytes_to_read)) {
+    RaiseError(QUIC_INTERNAL_ERROR, "Unable to buffer frame type bytes.");
+    visitor_->OnError(this);
+    return;
+  }
+  remaining_type_field_length_ -= bytes_to_read;
+}
+
 void HttpDecoder::RaiseError(QuicErrorCode error, std::string error_detail) {
   state_ = STATE_ERROR;
   error_ = error;
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index 55de8ad..8ed7973 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -117,6 +117,7 @@
 
   QuicErrorCode error() const { return error_; }
   const std::string& error_detail() const { return error_detail_; }
+  uint64_t current_frame_type() const { return current_frame_type_; }
 
  private:
   // Represents the current state of the parsing state machine.
@@ -152,9 +153,12 @@
   void BufferFramePayload(QuicDataReader* reader);
 
   // Buffers any remaining frame length field from |reader| into
-  // |length_buffer_|
+  // |length_buffer_|.
   void BufferFrameLength(QuicDataReader* reader);
 
+  // Buffers any remaining frame type field from |reader| into |type_buffer_|.
+  void BufferFrameType(QuicDataReader* reader);
+
   // Sets |error_| and |error_detail_| accordingly.
   void RaiseError(QuicErrorCode error, std::string error_detail);
 
@@ -172,7 +176,7 @@
   // Current state of the parsing.
   HttpDecoderState state_;
   // Type of the frame currently being parsed.
-  uint8_t current_frame_type_;
+  uint64_t current_frame_type_;
   // Size of the frame's length field.
   QuicByteCount current_length_field_size_;
   // Remaining length that's needed for the frame's length field.
@@ -181,6 +185,10 @@
   QuicByteCount current_frame_length_;
   // Remaining payload bytes to be parsed.
   QuicByteCount remaining_frame_length_;
+  // Length of the frame's type field.
+  QuicByteCount current_type_field_length_;
+  // Remaining length that's needed for the frame's type field.
+  QuicByteCount remaining_type_field_length_;
   // Last error.
   QuicErrorCode error_;
   // The issue which caused |error_|
@@ -189,6 +197,8 @@
   std::string buffer_;
   // Remaining unparsed length field data.
   std::string length_buffer_;
+  // Remaining unparsed type field data.
+  std::array<char, sizeof(uint64_t)> type_buffer_;
 };
 
 }  // namespace quic
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 7f0dcd4..b0a3f6b 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -7,6 +7,7 @@
 #include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 using testing::InSequence;
@@ -54,51 +55,99 @@
 }
 
 TEST_F(HttpDecoderTest, ReservedFramesNoPayload) {
+  std::unique_ptr<char[]> input;
   for (int n = 0; n < 8; ++n) {
     const uint8_t type = 0xB + 0x1F * n;
-    char input[] = {// length
-                    0x00,
-                    // type
-                    type};
+    QuicByteCount total_length = QuicDataWriter::GetVarInt62Len(0x00) +
+                                 QuicDataWriter::GetVarInt62Len(type);
+    input = QuicMakeUnique<char[]>(total_length);
+    QuicDataWriter writer(total_length, input.get());
+    writer.WriteVarInt62(0x00);
+    writer.WriteVarInt62(type);
 
-    EXPECT_EQ(2u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))) << n;
+    EXPECT_EQ(total_length, decoder_.ProcessInput(input.get(), total_length))
+        << n;
     EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
     ASSERT_EQ("", decoder_.error_detail());
+    EXPECT_EQ(type, decoder_.current_frame_type());
   }
+  // Test on a arbitrary reserved frame with 2-byte type field by hard coding
+  // variable length integer.
+  char in[] = {// length
+               0x00,
+               // type 0xB + 0x1F*3
+               0x40, 0x68};
+  EXPECT_EQ(3u, decoder_.ProcessInput(in, QUIC_ARRAYSIZE(in)));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  ASSERT_EQ("", decoder_.error_detail());
+  EXPECT_EQ(0xB + 0x1F * 3, decoder_.current_frame_type());
 }
 
 TEST_F(HttpDecoderTest, ReservedFramesSmallPayload) {
+  std::unique_ptr<char[]> input;
+  const uint8_t payload_size = 50;
+  std::string data(payload_size, 'a');
   for (int n = 0; n < 8; ++n) {
     const uint8_t type = 0xB + 0x1F * n;
-    const uint8_t payload_size = 50;
-    char input[payload_size + 2] = {// length
-                                    payload_size,
-                                    // type
-                                    type};
-
-    EXPECT_EQ(QUIC_ARRAYSIZE(input),
-              decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)))
+    QuicByteCount total_length = QuicDataWriter::GetVarInt62Len(payload_size) +
+                                 QuicDataWriter::GetVarInt62Len(type) +
+                                 payload_size;
+    input = QuicMakeUnique<char[]>(total_length);
+    QuicDataWriter writer(total_length, input.get());
+    writer.WriteVarInt62(payload_size);
+    writer.WriteVarInt62(type);
+    writer.WriteStringPiece(data);
+    EXPECT_EQ(total_length, decoder_.ProcessInput(input.get(), total_length))
         << n;
     EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
     ASSERT_EQ("", decoder_.error_detail());
+    EXPECT_EQ(type, decoder_.current_frame_type());
   }
+
+  // Test on a arbitrary reserved frame with 2-byte type field by hard coding
+  // variable length integer.
+  char in[payload_size + 3] = {// length
+                               payload_size,
+                               // type 0xB + 0x1F*3
+                               0x40, 0x68};
+  EXPECT_EQ(QUIC_ARRAYSIZE(in), decoder_.ProcessInput(in, QUIC_ARRAYSIZE(in)));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  ASSERT_EQ("", decoder_.error_detail());
+  EXPECT_EQ(0xB + 0x1F * 3, decoder_.current_frame_type());
 }
 
 TEST_F(HttpDecoderTest, ReservedFramesLargePayload) {
+  std::unique_ptr<char[]> input;
+  const QuicByteCount payload_size = 256;
+  std::string data(payload_size, 'a');
   for (int n = 0; n < 8; ++n) {
     const uint8_t type = 0xB + 0x1F * n;
-    const QuicByteCount payload_size = 256;
-    char input[payload_size + 3] = {// length
-                                    0x40 + 0x01, 0x00,
-                                    // type
-                                    type};
+    QuicByteCount total_length = QuicDataWriter::GetVarInt62Len(payload_size) +
+                                 QuicDataWriter::GetVarInt62Len(type) +
+                                 payload_size;
+    input = QuicMakeUnique<char[]>(total_length);
+    QuicDataWriter writer(total_length, input.get());
+    writer.WriteVarInt62(payload_size);
+    writer.WriteVarInt62(type);
+    writer.WriteStringPiece(data);
 
-    EXPECT_EQ(QUIC_ARRAYSIZE(input),
-              decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)))
+    EXPECT_EQ(total_length, decoder_.ProcessInput(input.get(), total_length))
         << n;
     EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
     ASSERT_EQ("", decoder_.error_detail());
+    EXPECT_EQ(type, decoder_.current_frame_type());
   }
+
+  // Test on a arbitrary reserved frame with 2-byte type field by hard coding
+  // variable length integer.
+  char in[payload_size + 4] = {// length
+                               0x40 + 0x01, 0x00,
+                               // type 0xB + 0x1F*3
+                               0x40, 0x68};
+  EXPECT_EQ(QUIC_ARRAYSIZE(in), decoder_.ProcessInput(in, QUIC_ARRAYSIZE(in)));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  ASSERT_EQ("", decoder_.error_detail());
+  EXPECT_EQ(0xB + 0x1F * 3, decoder_.current_frame_type());
 }
 
 TEST_F(HttpDecoderTest, CancelPush) {
@@ -352,6 +401,27 @@
   EXPECT_EQ("", decoder_.error_detail());
 }
 
+TEST_F(HttpDecoderTest, PartialDeliveryOfLargeFrameType) {
+  // Use a reserved type that's more than 1 byte in VarInt62.
+  const uint8_t type = 0xB + 0x1F * 3;
+  std::unique_ptr<char[]> input;
+  QuicByteCount total_length = QuicDataWriter::GetVarInt62Len(0x00) +
+                               QuicDataWriter::GetVarInt62Len(type);
+  input.reset(new char[total_length]);
+  QuicDataWriter writer(total_length, input.get());
+  writer.WriteVarInt62(0x00);
+  writer.WriteVarInt62(type);
+
+  auto raw_input = input.get();
+  for (uint64_t i = 0; i < total_length; ++i) {
+    char c = raw_input[i];
+    EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+  }
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+  EXPECT_EQ(type, decoder_.current_frame_type());
+}
+
 TEST_F(HttpDecoderTest, GoAway) {
   char input[] = {// length
                   0x1,
diff --git a/quic/core/http/http_encoder.cc b/quic/core/http/http_encoder.cc
index beeef9b..df04be2 100644
--- a/quic/core/http/http_encoder.cc
+++ b/quic/core/http/http_encoder.cc
@@ -44,8 +44,6 @@
   }
 }
 
-// Length of the type field of a frame.
-static const size_t kFrameTypeLength = 1;
 // Length of the weight field of a priority frame.
 static const size_t kPriorityWeightLength = 1;
 // Length of a priority frame's first byte.
@@ -63,8 +61,9 @@
     QuicByteCount payload_length,
     std::unique_ptr<char[]>* output) {
   DCHECK_NE(0u, payload_length);
-  QuicByteCount header_length =
-      QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength;
+  QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(payload_length) +
+                                QuicDataWriter::GetVarInt62Len(
+                                    static_cast<uint64_t>(HttpFrameType::DATA));
 
   output->reset(new char[header_length]);
   QuicDataWriter writer(header_length, output->get());
@@ -80,7 +79,9 @@
     std::unique_ptr<char[]>* output) {
   DCHECK_NE(0u, payload_length);
   QuicByteCount header_length =
-      QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength;
+      QuicDataWriter::GetVarInt62Len(payload_length) +
+      QuicDataWriter::GetVarInt62Len(
+          static_cast<uint64_t>(HttpFrameType::HEADERS));
 
   output->reset(new char[header_length]);
   QuicDataWriter writer(header_length, output->get());
@@ -99,7 +100,8 @@
       QuicDataWriter::GetVarInt62Len(priority.prioritized_element_id) +
       QuicDataWriter::GetVarInt62Len(priority.element_dependency_id) +
       kPriorityWeightLength;
-  QuicByteCount total_length = GetTotalLength(payload_length);
+  QuicByteCount total_length =
+      GetTotalLength(payload_length, HttpFrameType::PRIORITY);
 
   output->reset(new char[total_length]);
   QuicDataWriter writer(total_length, output->get());
@@ -130,7 +132,8 @@
     std::unique_ptr<char[]>* output) {
   QuicByteCount payload_length =
       QuicDataWriter::GetVarInt62Len(cancel_push.push_id);
-  QuicByteCount total_length = GetTotalLength(payload_length);
+  QuicByteCount total_length =
+      GetTotalLength(payload_length, HttpFrameType::CANCEL_PUSH);
 
   output->reset(new char[total_length]);
   QuicDataWriter writer(total_length, output->get());
@@ -152,7 +155,8 @@
     payload_length += QuicDataWriter::GetVarInt62Len(it->second);
   }
 
-  QuicByteCount total_length = GetTotalLength(payload_length);
+  QuicByteCount total_length =
+      GetTotalLength(payload_length, HttpFrameType::SETTINGS);
 
   output->reset(new char[total_length]);
   QuicDataWriter writer(total_length, output->get());
@@ -178,7 +182,9 @@
       push_promise.headers.length();
   // GetTotalLength() is not used because headers will not be serialized.
   QuicByteCount total_length =
-      QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength +
+      QuicDataWriter::GetVarInt62Len(payload_length) +
+      QuicDataWriter::GetVarInt62Len(
+          static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) +
       QuicDataWriter::GetVarInt62Len(push_promise.push_id);
 
   output->reset(new char[total_length]);
@@ -196,7 +202,8 @@
     std::unique_ptr<char[]>* output) {
   QuicByteCount payload_length =
       QuicDataWriter::GetVarInt62Len(goaway.stream_id);
-  QuicByteCount total_length = GetTotalLength(payload_length);
+  QuicByteCount total_length =
+      GetTotalLength(payload_length, HttpFrameType::GOAWAY);
 
   output->reset(new char[total_length]);
   QuicDataWriter writer(total_length, output->get());
@@ -213,7 +220,8 @@
     std::unique_ptr<char[]>* output) {
   QuicByteCount payload_length =
       QuicDataWriter::GetVarInt62Len(max_push_id.push_id);
-  QuicByteCount total_length = GetTotalLength(payload_length);
+  QuicByteCount total_length =
+      GetTotalLength(payload_length, HttpFrameType::MAX_PUSH_ID);
 
   output->reset(new char[total_length]);
   QuicDataWriter writer(total_length, output->get());
@@ -230,7 +238,8 @@
     std::unique_ptr<char[]>* output) {
   QuicByteCount payload_length =
       QuicDataWriter::GetVarInt62Len(duplicate_push.push_id);
-  QuicByteCount total_length = GetTotalLength(payload_length);
+  QuicByteCount total_length =
+      GetTotalLength(payload_length, HttpFrameType::DUPLICATE_PUSH);
 
   output->reset(new char[total_length]);
   QuicDataWriter writer(total_length, output->get());
@@ -247,11 +256,13 @@
                                    HttpFrameType type,
                                    QuicDataWriter* writer) {
   return writer->WriteVarInt62(length) &&
-         writer->WriteUInt8(static_cast<uint8_t>(type));
+         writer->WriteVarInt62(static_cast<uint64_t>(type));
 }
 
-QuicByteCount HttpEncoder::GetTotalLength(QuicByteCount payload_length) {
-  return QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength +
+QuicByteCount HttpEncoder::GetTotalLength(QuicByteCount payload_length,
+                                          HttpFrameType type) {
+  return QuicDataWriter::GetVarInt62Len(payload_length) +
+         QuicDataWriter::GetVarInt62Len(static_cast<uint64_t>(type)) +
          payload_length;
 }
 
diff --git a/quic/core/http/http_encoder.h b/quic/core/http/http_encoder.h
index f04e6e4..6691e31 100644
--- a/quic/core/http/http_encoder.h
+++ b/quic/core/http/http_encoder.h
@@ -77,7 +77,8 @@
                         HttpFrameType type,
                         QuicDataWriter* writer);
 
-  QuicByteCount GetTotalLength(QuicByteCount payload_length);
+  QuicByteCount GetTotalLength(QuicByteCount payload_length,
+                               HttpFrameType type);
 };
 
 }  // namespace quic