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