Factor out size limit logic from QuicHeaderList.

QuicHeaderList is used both in IETF QUIC and in gQUIC. The size limit
functionality is only used in gQUIC. (In IETF QUIC, size is limited in
QpackDecodedHeadersAccumulator). This CL creates a wrapper class for use in
gQUIC only. When gQUIC gets deprecated, this wrapper class can be cleanly
removed, together with QuicSpdySession::SpdyFramerVisitor.

PiperOrigin-RevId: 662112283
diff --git a/quiche/quic/core/http/quic_header_list.cc b/quiche/quic/core/http/quic_header_list.cc
index 771e617..5751683 100644
--- a/quiche/quic/core/http/quic_header_list.cc
+++ b/quiche/quic/core/http/quic_header_list.cc
@@ -14,51 +14,18 @@
 
 namespace quic {
 
-QuicHeaderList::QuicHeaderList()
-    : max_header_list_size_(std::numeric_limits<size_t>::max()),
-      current_header_list_size_(0),
-      uncompressed_header_bytes_(0),
-      compressed_header_bytes_(0) {}
-
-QuicHeaderList::QuicHeaderList(QuicHeaderList&& other) = default;
-
-QuicHeaderList::QuicHeaderList(const QuicHeaderList& other) = default;
-
-QuicHeaderList& QuicHeaderList::operator=(const QuicHeaderList& other) =
-    default;
-
-QuicHeaderList& QuicHeaderList::operator=(QuicHeaderList&& other) = default;
-
-QuicHeaderList::~QuicHeaderList() {}
-
-void QuicHeaderList::OnHeaderBlockStart() {
-  QUIC_BUG_IF(quic_bug_12518_1, current_header_list_size_ != 0)
-      << "OnHeaderBlockStart called more than once!";
-}
-
 void QuicHeaderList::OnHeader(absl::string_view name, absl::string_view value) {
-  // Avoid infinite buffering of headers. No longer store headers
-  // once the current headers are over the limit.
-  if (current_header_list_size_ < max_header_list_size_) {
-    current_header_list_size_ += name.size();
-    current_header_list_size_ += value.size();
-    current_header_list_size_ += kQpackEntrySizeOverhead;
-    header_list_.emplace_back(std::string(name), std::string(value));
-  }
+  header_list_.emplace_back(std::string(name), std::string(value));
 }
 
 void QuicHeaderList::OnHeaderBlockEnd(size_t uncompressed_header_bytes,
                                       size_t compressed_header_bytes) {
   uncompressed_header_bytes_ = uncompressed_header_bytes;
   compressed_header_bytes_ = compressed_header_bytes;
-  if (current_header_list_size_ > max_header_list_size_) {
-    Clear();
-  }
 }
 
 void QuicHeaderList::Clear() {
   header_list_.clear();
-  current_header_list_size_ = 0;
   uncompressed_header_bytes_ = 0;
   compressed_header_bytes_ = 0;
 }
diff --git a/quiche/quic/core/http/quic_header_list.h b/quiche/quic/core/http/quic_header_list.h
index 51c2a82..ac64cec 100644
--- a/quiche/quic/core/http/quic_header_list.h
+++ b/quiche/quic/core/http/quic_header_list.h
@@ -19,25 +19,23 @@
 namespace quic {
 
 // A simple class that accumulates header pairs
-class QUICHE_EXPORT QuicHeaderList : public spdy::SpdyHeadersHandlerInterface {
+class QUICHE_EXPORT QuicHeaderList {
  public:
   using ListType =
       quiche::QuicheCircularDeque<std::pair<std::string, std::string>>;
   using value_type = ListType::value_type;
   using const_iterator = ListType::const_iterator;
 
-  QuicHeaderList();
-  QuicHeaderList(QuicHeaderList&& other);
-  QuicHeaderList(const QuicHeaderList& other);
-  QuicHeaderList& operator=(QuicHeaderList&& other);
-  QuicHeaderList& operator=(const QuicHeaderList& other);
-  ~QuicHeaderList() override;
+  QuicHeaderList() = default;
+  QuicHeaderList(QuicHeaderList&& other) = default;
+  QuicHeaderList(const QuicHeaderList& other) = default;
+  QuicHeaderList& operator=(QuicHeaderList&& other) = default;
+  QuicHeaderList& operator=(const QuicHeaderList& other) = default;
 
-  // From SpdyHeadersHandlerInteface.
-  void OnHeaderBlockStart() override;
-  void OnHeader(absl::string_view name, absl::string_view value) override;
+  void OnHeaderBlockStart() {}
+  void OnHeader(absl::string_view name, absl::string_view value);
   void OnHeaderBlockEnd(size_t uncompressed_header_bytes,
-                        size_t compressed_header_bytes) override;
+                        size_t compressed_header_bytes);
 
   void Clear();
 
@@ -50,28 +48,13 @@
   }
   size_t compressed_header_bytes() const { return compressed_header_bytes_; }
 
-  // Deprecated.  TODO(b/145909215): remove.
-  void set_max_header_list_size(size_t max_header_list_size) {
-    max_header_list_size_ = max_header_list_size;
-  }
-
   std::string DebugString() const;
 
  private:
   quiche::QuicheCircularDeque<std::pair<std::string, std::string>> header_list_;
 
-  // The limit on the size of the header list (defined by spec as name + value +
-  // overhead for each header field). Headers over this limit will not be
-  // buffered, and the list will be cleared upon OnHeaderBlockEnd.
-  size_t max_header_list_size_;
-
-  // Defined per the spec as the size of all header fields with an additional
-  // overhead for each field.
-  size_t current_header_list_size_;
-
-  // TODO(dahollings) Are these fields necessary?
-  size_t uncompressed_header_bytes_;
-  size_t compressed_header_bytes_;
+  size_t uncompressed_header_bytes_ = 0;
+  size_t compressed_header_bytes_ = 0;
 };
 
 inline bool operator==(const QuicHeaderList& l1, const QuicHeaderList& l2) {
diff --git a/quiche/quic/core/http/quic_header_list_test.cc b/quiche/quic/core/http/quic_header_list_test.cc
index 573aae5..e13e254 100644
--- a/quiche/quic/core/http/quic_header_list_test.cc
+++ b/quiche/quic/core/http/quic_header_list_test.cc
@@ -36,37 +36,6 @@
   EXPECT_EQ("{ foo=bar, april=fools, beep=, }", headers.DebugString());
 }
 
-TEST_F(QuicHeaderListTest, TooLarge) {
-  const size_t kMaxHeaderListSize = 256;
-
-  QuicHeaderList headers;
-  headers.set_max_header_list_size(kMaxHeaderListSize);
-  std::string key = "key";
-  std::string value(kMaxHeaderListSize, '1');
-  // Send a header that exceeds max_header_list_size.
-  headers.OnHeader(key, value);
-  // Send a second header exceeding max_header_list_size.
-  headers.OnHeader(key + "2", value);
-  // We should not allocate more memory after exceeding max_header_list_size.
-  EXPECT_LT(headers.DebugString().size(), 2 * value.size());
-  size_t total_bytes = 2 * (key.size() + value.size()) + 1;
-  headers.OnHeaderBlockEnd(total_bytes, total_bytes);
-
-  EXPECT_TRUE(headers.empty());
-  EXPECT_EQ("{ }", headers.DebugString());
-}
-
-TEST_F(QuicHeaderListTest, NotTooLarge) {
-  QuicHeaderList headers;
-  headers.set_max_header_list_size(1 << 20);
-  std::string key = "key";
-  std::string value(1 << 18, '1');
-  headers.OnHeader(key, value);
-  size_t total_bytes = key.size() + value.size();
-  headers.OnHeaderBlockEnd(total_bytes, total_bytes);
-  EXPECT_FALSE(headers.empty());
-}
-
 // This test verifies that QuicHeaderList is copyable and assignable.
 TEST_F(QuicHeaderListTest, IsCopyableAndAssignable) {
   QuicHeaderList headers;
diff --git a/quiche/quic/core/http/quic_spdy_session.cc b/quiche/quic/core/http/quic_spdy_session.cc
index 8c81b3c..d53a4b2 100644
--- a/quiche/quic/core/http/quic_spdy_session.cc
+++ b/quiche/quic/core/http/quic_spdy_session.cc
@@ -198,10 +198,62 @@
   return kDefaultQpackMaxDynamicTableCapacity;
 }
 
+// This class is only used in gQUIC.
+class SizeLimitingHeaderList : public spdy::SpdyHeadersHandlerInterface {
+ public:
+  ~SizeLimitingHeaderList() override = default;
+
+  void OnHeaderBlockStart() override {
+    QUIC_BUG_IF(quic_bug_12518_1, current_header_list_size_ != 0)
+        << "OnHeaderBlockStart called more than once!";
+  }
+
+  void OnHeader(absl::string_view name, absl::string_view value) override {
+    if (current_header_list_size_ < max_header_list_size_) {
+      current_header_list_size_ += name.size();
+      current_header_list_size_ += value.size();
+      current_header_list_size_ += kQpackEntrySizeOverhead;
+      header_list_.OnHeader(name, value);
+    }
+  }
+
+  void OnHeaderBlockEnd(size_t uncompressed_header_bytes,
+                        size_t compressed_header_bytes) override {
+    header_list_.OnHeaderBlockEnd(uncompressed_header_bytes,
+                                  compressed_header_bytes);
+    if (current_header_list_size_ > max_header_list_size_) {
+      Clear();
+    }
+  }
+
+  void set_max_header_list_size(size_t max_header_list_size) {
+    max_header_list_size_ = max_header_list_size;
+  }
+
+  void Clear() {
+    header_list_.Clear();
+    current_header_list_size_ = 0;
+  }
+
+  const QuicHeaderList& header_list() const { return header_list_; }
+
+ private:
+  QuicHeaderList header_list_;
+
+  // The limit on the size of the header list (defined by spec as name + value +
+  // overhead for each header field). Headers over this limit will not be
+  // buffered, and the list will be cleared upon OnHeaderBlockEnd().
+  size_t max_header_list_size_ = std::numeric_limits<size_t>::max();
+
+  // The total size of headers so far, including overhead.
+  size_t current_header_list_size_ = 0;
+};
+
 }  // namespace
 
 // A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
 // closes the connection if any unexpected frames are received.
+// This class is only used in gQUIC.
 class QuicSpdySession::SpdyFramerVisitor
     : public SpdyFramerVisitorInterface,
       public SpdyFramerDebugVisitorInterface {
@@ -221,12 +273,13 @@
 
     LogHeaderCompressionRatioHistogram(
         /* using_qpack = */ false,
-        /* is_sent = */ false, header_list_.compressed_header_bytes(),
-        header_list_.uncompressed_header_bytes());
+        /* is_sent = */ false,
+        header_list_.header_list().compressed_header_bytes(),
+        header_list_.header_list().uncompressed_header_bytes());
 
     // Ignore pushed request headers.
     if (session_->IsConnected() && !expecting_pushed_headers_) {
-      session_->OnHeaderList(header_list_);
+      session_->OnHeaderList(header_list_.header_list());
     }
     expecting_pushed_headers_ = false;
     header_list_.Clear();
@@ -464,7 +517,7 @@
   }
 
   QuicSpdySession* session_;
-  QuicHeaderList header_list_;
+  SizeLimitingHeaderList header_list_;
 
   // True if the next OnHeaderFrameEnd() call signals the end of pushed request
   // headers.