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