Add a class to represent a sequence of BalsaHeaders.

This CL introduces BalsaHeadersSequence, which wraps a std::list<BalsaHeaders>
and an iterator. Users add BalsaHeaders to the BalsaHeadersSequence (owning),
and can then get a pointer (non-owning) to successive BalsaHeaders.

The immediate usage of BalsaHeadersSequence will be in SimpleClient
(RequestsTestHelper ecosystem, cl/527686928), but the long-term envisioned use
case will be in JetstreamSession, e.g., adding to BalsaHeadersSequence in the
readers and consuming the BalsaHeaders while driving the response pipeline [1].

[1] Roughly
http://google3/gfe/gfe2/jetstream/jetstream_session.cc;l=6760;rcl=528925001 and
http://google3/gfe/gfe2/jetstream/net_http_requester.cc;l=2560;rcl=528925001.

PiperOrigin-RevId: 529155933
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 50d3f48..fd20c48 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -12,6 +12,7 @@
     "balsa/balsa_enums.h",
     "balsa/balsa_frame.h",
     "balsa/balsa_headers.h",
+    "balsa/balsa_headers_sequence.h",
     "balsa/balsa_visitor_interface.h",
     "balsa/framer_interface.h",
     "balsa/header_api.h",
@@ -407,6 +408,7 @@
     "balsa/balsa_enums.cc",
     "balsa/balsa_frame.cc",
     "balsa/balsa_headers.cc",
+    "balsa/balsa_headers_sequence.cc",
     "balsa/header_properties.cc",
     "balsa/simple_buffer.cc",
     "balsa/standard_header_map.cc",
@@ -1024,6 +1026,7 @@
 ]
 quiche_tests_srcs = [
     "balsa/balsa_frame_test.cc",
+    "balsa/balsa_headers_sequence_test.cc",
     "balsa/balsa_headers_test.cc",
     "balsa/header_properties_test.cc",
     "balsa/simple_buffer_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index c1baf44..4d34746 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -12,6 +12,7 @@
     "src/quiche/balsa/balsa_enums.h",
     "src/quiche/balsa/balsa_frame.h",
     "src/quiche/balsa/balsa_headers.h",
+    "src/quiche/balsa/balsa_headers_sequence.h",
     "src/quiche/balsa/balsa_visitor_interface.h",
     "src/quiche/balsa/framer_interface.h",
     "src/quiche/balsa/header_api.h",
@@ -407,6 +408,7 @@
     "src/quiche/balsa/balsa_enums.cc",
     "src/quiche/balsa/balsa_frame.cc",
     "src/quiche/balsa/balsa_headers.cc",
+    "src/quiche/balsa/balsa_headers_sequence.cc",
     "src/quiche/balsa/header_properties.cc",
     "src/quiche/balsa/simple_buffer.cc",
     "src/quiche/balsa/standard_header_map.cc",
@@ -1024,6 +1026,7 @@
 ]
 quiche_tests_srcs = [
     "src/quiche/balsa/balsa_frame_test.cc",
+    "src/quiche/balsa/balsa_headers_sequence_test.cc",
     "src/quiche/balsa/balsa_headers_test.cc",
     "src/quiche/balsa/header_properties_test.cc",
     "src/quiche/balsa/simple_buffer_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index e51e004..d9867f7 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -11,6 +11,7 @@
     "quiche/balsa/balsa_enums.h",
     "quiche/balsa/balsa_frame.h",
     "quiche/balsa/balsa_headers.h",
+    "quiche/balsa/balsa_headers_sequence.h",
     "quiche/balsa/balsa_visitor_interface.h",
     "quiche/balsa/framer_interface.h",
     "quiche/balsa/header_api.h",
@@ -406,6 +407,7 @@
     "quiche/balsa/balsa_enums.cc",
     "quiche/balsa/balsa_frame.cc",
     "quiche/balsa/balsa_headers.cc",
+    "quiche/balsa/balsa_headers_sequence.cc",
     "quiche/balsa/header_properties.cc",
     "quiche/balsa/simple_buffer.cc",
     "quiche/balsa/standard_header_map.cc",
@@ -1023,6 +1025,7 @@
   ],
   "quiche_tests_srcs": [
     "quiche/balsa/balsa_frame_test.cc",
+    "quiche/balsa/balsa_headers_sequence_test.cc",
     "quiche/balsa/balsa_headers_test.cc",
     "quiche/balsa/header_properties_test.cc",
     "quiche/balsa/simple_buffer_test.cc",
diff --git a/quiche/balsa/balsa_headers_sequence.cc b/quiche/balsa/balsa_headers_sequence.cc
new file mode 100644
index 0000000..a207a19
--- /dev/null
+++ b/quiche/balsa/balsa_headers_sequence.cc
@@ -0,0 +1,31 @@
+#include "quiche/balsa/balsa_headers_sequence.h"
+
+#include <iterator>
+
+#include "quiche/balsa/balsa_headers.h"
+
+namespace quiche {
+
+void BalsaHeadersSequence::Append(BalsaHeaders headers) {
+  sequence_.push_back(std::move(headers));
+
+  if (iter_ == sequence_.end()) {
+    iter_ = std::prev(sequence_.end());
+  }
+}
+
+bool BalsaHeadersSequence::HasNext() const { return iter_ != sequence_.end(); }
+
+const BalsaHeaders* BalsaHeadersSequence::Next() {
+  if (!HasNext()) {
+    return nullptr;
+  }
+  return &*iter_++;
+}
+
+void BalsaHeadersSequence::Clear() {
+  sequence_.clear();
+  iter_ = sequence_.end();
+}
+
+}  // namespace quiche
diff --git a/quiche/balsa/balsa_headers_sequence.h b/quiche/balsa/balsa_headers_sequence.h
new file mode 100644
index 0000000..9a24ee8
--- /dev/null
+++ b/quiche/balsa/balsa_headers_sequence.h
@@ -0,0 +1,36 @@
+#ifndef QUICHE_BALSA_BALSA_HEADERS_SEQUENCE_H_
+#define QUICHE_BALSA_BALSA_HEADERS_SEQUENCE_H_
+
+#include <list>
+
+#include "quiche/balsa/balsa_headers.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace quiche {
+
+// Represents a sequence of BalsaHeaders. The sequence owns each BalsaHeaders,
+// and the user asks for pointers to successive BalsaHeaders in the sequence.
+class QUICHE_EXPORT BalsaHeadersSequence {
+ public:
+  // Appends `headers` to the end of the sequence.
+  void Append(BalsaHeaders headers);
+
+  // Returns true if there is a BalsaHeaders that has not yet been returned from
+  // `Next()`. IFF true, `Next()` will return non-nullptr.
+  bool HasNext() const;
+
+  // Returns a non-owning pointer to the next BalsaHeaders in the sequence, or
+  // nullptr if the next does not exist.
+  const BalsaHeaders* Next();
+
+  // Clears the sequence. Any previously returned BalsaHeaders become invalid.
+  void Clear();
+
+ private:
+  std::list<BalsaHeaders> sequence_;
+  std::list<BalsaHeaders>::const_iterator iter_ = sequence_.end();
+};
+
+}  // namespace quiche
+
+#endif  // QUICHE_BALSA_BALSA_HEADERS_SEQUENCE_H_
diff --git a/quiche/balsa/balsa_headers_sequence_test.cc b/quiche/balsa/balsa_headers_sequence_test.cc
new file mode 100644
index 0000000..061cc68
--- /dev/null
+++ b/quiche/balsa/balsa_headers_sequence_test.cc
@@ -0,0 +1,64 @@
+#include "quiche/balsa/balsa_headers_sequence.h"
+
+#include <utility>
+
+#include "quiche/balsa/balsa_headers.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace quiche {
+namespace test {
+namespace {
+
+TEST(BalsaHeadersSequenceTest, Initial) {
+  BalsaHeadersSequence sequence;
+  EXPECT_FALSE(sequence.HasNext());
+  EXPECT_EQ(sequence.Next(), nullptr);
+}
+
+TEST(BalsaHeadersSequenceTest, Basic) {
+  BalsaHeadersSequence sequence;
+
+  BalsaHeaders headers_one;
+  headers_one.AppendHeader("one", "fish");
+  sequence.Append(std::move(headers_one));
+  EXPECT_TRUE(sequence.HasNext());
+
+  BalsaHeaders headers_two;
+  headers_two.AppendHeader("two", "fish");
+  sequence.Append(std::move(headers_two));
+  EXPECT_TRUE(sequence.HasNext());
+
+  const BalsaHeaders* headers = sequence.Next();
+  ASSERT_NE(headers, nullptr);
+  EXPECT_TRUE(headers->HasHeader("one"));
+  EXPECT_TRUE(sequence.HasNext());
+
+  headers = sequence.Next();
+  ASSERT_NE(headers, nullptr);
+  EXPECT_TRUE(headers->HasHeader("two"));
+  EXPECT_FALSE(sequence.HasNext());
+
+  EXPECT_EQ(sequence.Next(), nullptr);
+}
+
+TEST(BalsaHeadersSequenceTest, Clear) {
+  BalsaHeadersSequence sequence;
+
+  BalsaHeaders headers_one;
+  headers_one.AppendHeader("one", "fish");
+  sequence.Append(std::move(headers_one));
+  EXPECT_TRUE(sequence.HasNext());
+
+  BalsaHeaders headers_two;
+  headers_two.AppendHeader("two", "fish");
+  sequence.Append(std::move(headers_two));
+  EXPECT_TRUE(sequence.HasNext());
+
+  sequence.Clear();
+  EXPECT_FALSE(sequence.HasNext());
+  EXPECT_EQ(sequence.Next(), nullptr);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quiche