Introduce HttpDecoder::DecodeSettings().

In IETF QUIC, the client will cache a serialized SETTINGS frame (done in QuicSpdyClientSessionBase::OnSettingsFrame). Later when 0-rtt is attempted, the client session needs to decode the settings and apply to itself.

Unused code. not protected.

PiperOrigin-RevId: 310039639
Change-Id: I634670c019286ebe24e20f86b44da5a834e82066
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 87f9919..7c3b81b 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -11,6 +11,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
 
 namespace quic {
@@ -34,6 +35,55 @@
 
 HttpDecoder::~HttpDecoder() {}
 
+// static
+bool HttpDecoder::DecodeSettings(const char* data,
+                                 QuicByteCount len,
+                                 SettingsFrame* frame) {
+  QuicDataReader reader(data, len);
+  uint64_t frame_type;
+  if (!reader.ReadVarInt62(&frame_type)) {
+    QUIC_DLOG(ERROR) << "Unable to read frame type.";
+    return false;
+  }
+
+  if (frame_type != static_cast<uint64_t>(HttpFrameType::SETTINGS)) {
+    QUIC_DLOG(ERROR) << "Invalid frame type " << frame_type;
+    return false;
+  }
+  uint64_t frame_length;
+  if (!reader.ReadVarInt62(&frame_length)) {
+    QUIC_DLOG(ERROR) << "Unable to read frame length.";
+    return false;
+  }
+
+  std::string buffer;
+  if (!reader.ReadBytes(buffer.data(), frame_length)) {
+    QUIC_DLOG(ERROR) << "Unable to read frame payload.";
+    return false;
+  }
+
+  QuicDataReader frame_reader(buffer.data(), frame_length);
+
+  while (!frame_reader.IsDoneReading()) {
+    uint64_t id;
+    if (!frame_reader.ReadVarInt62(&id)) {
+      QUIC_DLOG(ERROR) << "Unable to read setting identifier.";
+      return false;
+    }
+    uint64_t content;
+    if (!frame_reader.ReadVarInt62(&content)) {
+      QUIC_DLOG(ERROR) << "Unable to read setting value.";
+      return false;
+    }
+    auto result = frame->values.insert({id, content});
+    if (!result.second) {
+      QUIC_DLOG(ERROR) << "Duplicate setting identifier.";
+      return false;
+    }
+  }
+  return true;
+}
+
 QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) {
   DCHECK_EQ(QUIC_NO_ERROR, error_);
   DCHECK_NE(STATE_ERROR, state_);
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index 3650bef..1551aa7 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -131,6 +131,13 @@
   // occurred.
   QuicByteCount ProcessInput(const char* data, QuicByteCount len);
 
+  // Decode settings frame from |data|.
+  // Upon successful decoding, |frame| will be populated, and returns true.
+  // This method is not used for regular processing of incoming data.
+  static bool DecodeSettings(const char* data,
+                             QuicByteCount len,
+                             SettingsFrame* frame);
+
   // Returns an error code other than QUIC_NO_ERROR if and only if
   // Visitor::OnError() has been called.
   QuicErrorCode error() const { return error_; }
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 27f0990..86a1b69 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -1072,6 +1072,43 @@
   }
 }
 
+TEST_F(HttpDecoderTest, DecodeSettings) {
+  std::string input = quiche::QuicheTextUtils::HexDecode(
+      "04"    // type (SETTINGS)
+      "07"    // length
+      "01"    // identifier (SETTINGS_QPACK_MAX_TABLE_CAPACITY)
+      "02"    // content
+      "06"    // identifier (SETTINGS_MAX_HEADER_LIST_SIZE)
+      "05"    // content
+      "4100"  // identifier, encoded on 2 bytes (0x40), value is 256 (0x100)
+      "04");  // content
+
+  SettingsFrame frame;
+  frame.values[1] = 2;
+  frame.values[6] = 5;
+  frame.values[256] = 4;
+
+  SettingsFrame out;
+  EXPECT_TRUE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out));
+  EXPECT_EQ(frame, out);
+
+  // non-settings frame.
+  input = quiche::QuicheTextUtils::HexDecode(
+      "0D"    // type (MAX_PUSH_ID)
+      "01"    // length
+      "01");  // Push Id
+
+  EXPECT_FALSE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out));
+
+  // Corrupt SETTINGS.
+  input = quiche::QuicheTextUtils::HexDecode(
+      "04"    // type (SETTINGS)
+      "01"    // length
+      "42");  // First byte of setting identifier, indicating a 2-byte varint62.
+
+  EXPECT_FALSE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out));
+}
+
 }  // namespace test
 
 }  // namespace quic