Move MetadataFrameSequence from third_party/spdy/core to gfe/gfe2/metadata.
This CL moves //third_party/spdy/core:metadata_extension_lib to
//gfe/gfe2/metadata:metadata_frame_sequence. With this move, this CL also
removes MetadataVisitor, which was unused outside of tests. The MetadataVisitor
tests are arguably covered by MetadataManager tests, e.g.:
- MetadataExtensionTest.MetadataNotSupported -->
MetadataManagerTest.MetadataNotSupported
- MetadataExtensionTest.MetadataSupported -->
MetadataManagerTest.MetadataSupported
- MetadataExtensionTest.MetadataDeliveredToUnknownFrameCallbacks -->
MetadataManagerTest.SetUp() [1]
- MetadataExtensionTest.MetadataPayloadEndToEnd -->
MetadataManagerTest.SendAndReceiveMetadata
- MetadataExtensionTest.MetadataPayloadInterleaved -->
MetadataManagerTest.ReceivedMetadataPayloadInterleaved
The net effect is some code removal, with less code that needs to be moved from
//third_party/spdy to third_party/http2.
#cleanup
[1]
http://google3/gfe/gfe2/metadata/metadata_manager_test.cc;l=210-224;rcl=565024527,
and I can add an explicit test if helpful
PiperOrigin-RevId: 599261755
diff --git a/build/source_list.bzl b/build/source_list.bzl
index dcdedb9..9431ce4 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -379,7 +379,6 @@
"spdy/core/http2_frame_decoder_adapter.h",
"spdy/core/http2_header_block.h",
"spdy/core/http2_header_block_hpack_listener.h",
- "spdy/core/metadata_extension.h",
"spdy/core/no_op_headers_handler.h",
"spdy/core/recording_headers_handler.h",
"spdy/core/spdy_alt_svc_wire_format.h",
@@ -674,7 +673,6 @@
"spdy/core/hpack/hpack_output_stream.cc",
"spdy/core/hpack/hpack_static_table.cc",
"spdy/core/http2_frame_decoder_adapter.cc",
- "spdy/core/metadata_extension.cc",
"spdy/core/recording_headers_handler.cc",
"spdy/core/spdy_alt_svc_wire_format.cc",
"spdy/core/spdy_frame_builder.cc",
@@ -1306,7 +1304,6 @@
"spdy/core/hpack/hpack_output_stream_test.cc",
"spdy/core/hpack/hpack_round_trip_test.cc",
"spdy/core/hpack/hpack_static_table_test.cc",
- "spdy/core/metadata_extension_test.cc",
"spdy/core/spdy_alt_svc_wire_format_test.cc",
"spdy/core/spdy_frame_builder_test.cc",
"spdy/core/spdy_framer_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 7d44368..9e3a202 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -379,7 +379,6 @@
"src/quiche/spdy/core/http2_frame_decoder_adapter.h",
"src/quiche/spdy/core/http2_header_block.h",
"src/quiche/spdy/core/http2_header_block_hpack_listener.h",
- "src/quiche/spdy/core/metadata_extension.h",
"src/quiche/spdy/core/no_op_headers_handler.h",
"src/quiche/spdy/core/recording_headers_handler.h",
"src/quiche/spdy/core/spdy_alt_svc_wire_format.h",
@@ -674,7 +673,6 @@
"src/quiche/spdy/core/hpack/hpack_output_stream.cc",
"src/quiche/spdy/core/hpack/hpack_static_table.cc",
"src/quiche/spdy/core/http2_frame_decoder_adapter.cc",
- "src/quiche/spdy/core/metadata_extension.cc",
"src/quiche/spdy/core/recording_headers_handler.cc",
"src/quiche/spdy/core/spdy_alt_svc_wire_format.cc",
"src/quiche/spdy/core/spdy_frame_builder.cc",
@@ -1307,7 +1305,6 @@
"src/quiche/spdy/core/hpack/hpack_output_stream_test.cc",
"src/quiche/spdy/core/hpack/hpack_round_trip_test.cc",
"src/quiche/spdy/core/hpack/hpack_static_table_test.cc",
- "src/quiche/spdy/core/metadata_extension_test.cc",
"src/quiche/spdy/core/spdy_alt_svc_wire_format_test.cc",
"src/quiche/spdy/core/spdy_frame_builder_test.cc",
"src/quiche/spdy/core/spdy_framer_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 967ddc8..2971cf8 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -378,7 +378,6 @@
"quiche/spdy/core/http2_frame_decoder_adapter.h",
"quiche/spdy/core/http2_header_block.h",
"quiche/spdy/core/http2_header_block_hpack_listener.h",
- "quiche/spdy/core/metadata_extension.h",
"quiche/spdy/core/no_op_headers_handler.h",
"quiche/spdy/core/recording_headers_handler.h",
"quiche/spdy/core/spdy_alt_svc_wire_format.h",
@@ -673,7 +672,6 @@
"quiche/spdy/core/hpack/hpack_output_stream.cc",
"quiche/spdy/core/hpack/hpack_static_table.cc",
"quiche/spdy/core/http2_frame_decoder_adapter.cc",
- "quiche/spdy/core/metadata_extension.cc",
"quiche/spdy/core/recording_headers_handler.cc",
"quiche/spdy/core/spdy_alt_svc_wire_format.cc",
"quiche/spdy/core/spdy_frame_builder.cc",
@@ -1306,7 +1304,6 @@
"quiche/spdy/core/hpack/hpack_output_stream_test.cc",
"quiche/spdy/core/hpack/hpack_round_trip_test.cc",
"quiche/spdy/core/hpack/hpack_static_table_test.cc",
- "quiche/spdy/core/metadata_extension_test.cc",
"quiche/spdy/core/spdy_alt_svc_wire_format_test.cc",
"quiche/spdy/core/spdy_frame_builder_test.cc",
"quiche/spdy/core/spdy_framer_test.cc",
diff --git a/quiche/spdy/core/metadata_extension.cc b/quiche/spdy/core/metadata_extension.cc
deleted file mode 100644
index 7fdde18..0000000
--- a/quiche/spdy/core/metadata_extension.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-#include "quiche/spdy/core/metadata_extension.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <list>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "quiche/http2/decoder/decode_buffer.h"
-#include "quiche/http2/hpack/decoder/hpack_decoder.h"
-#include "quiche/common/platform/api/quiche_bug_tracker.h"
-#include "quiche/common/platform/api/quiche_logging.h"
-#include "quiche/spdy/core/hpack/hpack_encoder.h"
-#include "quiche/spdy/core/http2_header_block.h"
-#include "quiche/spdy/core/http2_header_block_hpack_listener.h"
-#include "quiche/spdy/core/spdy_protocol.h"
-
-namespace spdy {
-
-// Non-standard constants related to METADATA frames.
-const SpdySettingsId MetadataVisitor::kMetadataExtensionId = 0x4d44;
-const uint8_t MetadataVisitor::kMetadataFrameType = 0x4d;
-const uint8_t MetadataVisitor::kEndMetadataFlag = 0x4;
-
-namespace {
-
-const size_t kMaxMetadataBlockSize = 1 << 20; // 1 MB
-
-} // anonymous namespace
-
-MetadataFrameSequence::MetadataFrameSequence(SpdyStreamId stream_id,
- spdy::Http2HeaderBlock payload)
- : stream_id_(stream_id), payload_(std::move(payload)) {
- // Metadata should not use HPACK compression.
- encoder_.DisableCompression();
- HpackEncoder::Representations r;
- for (const auto& kv_pair : payload_) {
- r.push_back(kv_pair);
- }
- progressive_encoder_ = encoder_.EncodeRepresentations(r);
-}
-
-bool MetadataFrameSequence::HasNext() const {
- return progressive_encoder_->HasNext();
-}
-
-std::unique_ptr<spdy::SpdyFrameIR> MetadataFrameSequence::Next() {
- if (!HasNext()) {
- return nullptr;
- }
- // METADATA frames obey the HTTP/2 maximum frame size.
- std::string payload =
- progressive_encoder_->Next(spdy::kHttp2DefaultFramePayloadLimit);
- const bool end_metadata = !HasNext();
- const uint8_t flags = end_metadata ? MetadataVisitor::kEndMetadataFlag : 0;
- return std::make_unique<spdy::SpdyUnknownIR>(
- stream_id_, MetadataVisitor::kMetadataFrameType, flags,
- std::move(payload));
-}
-
-struct MetadataVisitor::MetadataPayloadState {
- MetadataPayloadState(size_t remaining, bool end)
- : bytes_remaining(remaining), end_metadata(end) {}
- std::list<std::string> buffer;
- size_t bytes_remaining;
- bool end_metadata;
-};
-
-MetadataVisitor::MetadataVisitor(OnCompletePayload on_payload,
- OnMetadataSupport on_support)
- : on_payload_(std::move(on_payload)),
- on_support_(std::move(on_support)),
- peer_supports_metadata_(MetadataSupportState::UNSPECIFIED) {}
-
-MetadataVisitor::~MetadataVisitor() {}
-
-void MetadataVisitor::OnSetting(SpdySettingsId id, uint32_t value) {
- QUICHE_VLOG(1) << "MetadataVisitor::OnSetting(" << id << ", " << value << ")";
- if (id == kMetadataExtensionId) {
- if (value == 0) {
- const MetadataSupportState previous_state = peer_supports_metadata_;
- peer_supports_metadata_ = MetadataSupportState::NOT_SUPPORTED;
- if (previous_state == MetadataSupportState::UNSPECIFIED ||
- previous_state == MetadataSupportState::SUPPORTED) {
- on_support_(false);
- }
- } else if (value == 1) {
- const MetadataSupportState previous_state = peer_supports_metadata_;
- peer_supports_metadata_ = MetadataSupportState::SUPPORTED;
- if (previous_state == MetadataSupportState::UNSPECIFIED ||
- previous_state == MetadataSupportState::NOT_SUPPORTED) {
- on_support_(true);
- }
- } else {
- QUICHE_LOG_EVERY_N_SEC(WARNING, 1)
- << "Unrecognized value for setting " << id << ": " << value;
- }
- }
-}
-
-bool MetadataVisitor::OnFrameHeader(SpdyStreamId stream_id, size_t length,
- uint8_t type, uint8_t flags) {
- QUICHE_VLOG(1) << "OnFrameHeader(stream_id=" << stream_id
- << ", length=" << length << ", type=" << static_cast<int>(type)
- << ", flags=" << static_cast<int>(flags);
- // TODO(birenroy): Consider disabling METADATA handling until our setting
- // advertising METADATA support has been acked.
- if (type != kMetadataFrameType) {
- return false;
- }
- auto it = metadata_map_.find(stream_id);
- if (it == metadata_map_.end()) {
- auto state = std::make_unique<MetadataPayloadState>(
- length, flags & kEndMetadataFlag);
- auto result =
- metadata_map_.insert(std::make_pair(stream_id, std::move(state)));
- QUICHE_BUG_IF(bug_if_2781_1, !result.second) << "Map insertion failed.";
- it = result.first;
- } else {
- QUICHE_BUG_IF(bug_22051_1, it->second->end_metadata)
- << "Inconsistent metadata payload state!";
- QUICHE_BUG_IF(bug_if_2781_2, it->second->bytes_remaining > 0)
- << "Incomplete metadata block!";
- }
-
- if (it->second == nullptr) {
- QUICHE_BUG(bug_2781_3) << "Null metadata payload state!";
- return false;
- }
- current_stream_ = stream_id;
- it->second->bytes_remaining = length;
- it->second->end_metadata = (flags & kEndMetadataFlag);
- return true;
-}
-
-void MetadataVisitor::OnFramePayload(const char* data, size_t len) {
- QUICHE_VLOG(1) << "OnFramePayload(stream_id=" << current_stream_
- << ", len=" << len << ")";
- auto it = metadata_map_.find(current_stream_);
- if (it == metadata_map_.end() || it->second == nullptr) {
- QUICHE_BUG(bug_2781_4) << "Invalid order of operations on MetadataVisitor.";
- } else {
- MetadataPayloadState* state = it->second.get(); // For readability.
- state->buffer.push_back(std::string(data, len));
- if (len < state->bytes_remaining) {
- state->bytes_remaining -= len;
- } else {
- QUICHE_BUG_IF(bug_22051_2, len > state->bytes_remaining)
- << "Metadata payload overflow! len: " << len
- << " bytes_remaining: " << state->bytes_remaining;
- state->bytes_remaining = 0;
- if (state->end_metadata) {
- // The whole process of decoding the HPACK-encoded metadata block,
- // below, is more cumbersome than it ought to be.
- spdy::Http2HeaderBlockHpackListener listener;
- http2::HpackDecoder decoder(&listener, kMaxMetadataBlockSize);
-
- // If any operations fail, the decode process should be aborted.
- bool success = decoder.StartDecodingBlock();
- for (const std::string& slice : state->buffer) {
- if (!success) {
- break;
- }
- http2::DecodeBuffer buffer(slice.data(), slice.size());
- success = success && decoder.DecodeFragment(&buffer);
- }
- success =
- success && decoder.EndDecodingBlock() && !listener.hpack_error();
- if (success) {
- on_payload_(current_stream_, listener.release_header_block());
- }
- // TODO(birenroy): add varz counting metadata decode successes/failures.
- metadata_map_.erase(it);
- }
- }
- }
-}
-
-} // namespace spdy
diff --git a/quiche/spdy/core/metadata_extension.h b/quiche/spdy/core/metadata_extension.h
deleted file mode 100644
index 3cfb878..0000000
--- a/quiche/spdy/core/metadata_extension.h
+++ /dev/null
@@ -1,123 +0,0 @@
-#ifndef QUICHE_SPDY_CORE_METADATA_EXTENSION_H_
-#define QUICHE_SPDY_CORE_METADATA_EXTENSION_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <memory>
-#include <type_traits>
-
-#include "absl/container/flat_hash_map.h"
-#include "quiche/common/platform/api/quiche_export.h"
-#include "quiche/common/quiche_callbacks.h"
-#include "quiche/spdy/core/hpack/hpack_encoder.h"
-#include "quiche/spdy/core/http2_frame_decoder_adapter.h"
-#include "quiche/spdy/core/http2_header_block.h"
-#include "quiche/spdy/core/spdy_protocol.h"
-
-namespace spdy {
-
-// An implementation of the ExtensionVisitorInterface that can parse
-// METADATA frames. METADATA is a non-standard HTTP/2 extension developed and
-// used internally at Google. A peer advertises support for METADATA by sending
-// a setting with a setting ID of kMetadataExtensionId and a value of 1.
-//
-// Metadata is represented as a HPACK header block with literal encoding.
-class QUICHE_EXPORT MetadataVisitor : public spdy::ExtensionVisitorInterface {
- public:
- using MetadataPayload = spdy::Http2HeaderBlock;
-
- static_assert(!std::is_copy_constructible<MetadataPayload>::value,
- "MetadataPayload should be a move-only type!");
-
- using OnMetadataSupport = quiche::MultiUseCallback<void(bool)>;
- using OnCompletePayload =
- quiche::MultiUseCallback<void(spdy::SpdyStreamId, MetadataPayload)>;
-
- // The HTTP/2 SETTINGS ID that is used to indicate support for METADATA
- // frames.
- static const spdy::SpdySettingsId kMetadataExtensionId;
-
- // The 8-bit frame type code for a METADATA frame.
- static const uint8_t kMetadataFrameType;
-
- // The flag that indicates the end of a logical metadata block. Due to frame
- // size limits, a single metadata block may be emitted as several HTTP/2
- // frames.
- static const uint8_t kEndMetadataFlag;
-
- // |on_payload| is invoked whenever a complete metadata payload is received.
- // |on_support| is invoked whenever the peer's advertised support for metadata
- // changes.
- MetadataVisitor(OnCompletePayload on_payload, OnMetadataSupport on_support);
- ~MetadataVisitor() override;
-
- MetadataVisitor(const MetadataVisitor&) = delete;
- MetadataVisitor& operator=(const MetadataVisitor&) = delete;
-
- // Interprets the non-standard setting indicating support for METADATA.
- void OnSetting(spdy::SpdySettingsId id, uint32_t value) override;
-
- // Returns true iff |type| indicates a METADATA frame.
- bool OnFrameHeader(spdy::SpdyStreamId stream_id, size_t length, uint8_t type,
- uint8_t flags) override;
-
- // Consumes a METADATA frame payload. Invokes the registered callback when a
- // complete payload has been received.
- void OnFramePayload(const char* data, size_t len) override;
-
- // Returns true if the peer has advertised support for METADATA via the
- // appropriate setting.
- bool PeerSupportsMetadata() const {
- return peer_supports_metadata_ == MetadataSupportState::SUPPORTED;
- }
-
- private:
- enum class MetadataSupportState : uint8_t {
- UNSPECIFIED,
- SUPPORTED,
- NOT_SUPPORTED,
- };
-
- struct MetadataPayloadState;
-
- using StreamMetadataMap =
- absl::flat_hash_map<spdy::SpdyStreamId,
- std::unique_ptr<MetadataPayloadState>>;
-
- OnCompletePayload on_payload_;
- OnMetadataSupport on_support_;
- StreamMetadataMap metadata_map_;
- spdy::SpdyStreamId current_stream_;
- MetadataSupportState peer_supports_metadata_;
-};
-
-// This class uses an HpackEncoder to serialize a METADATA block as a series of
-// METADATA frames.
-class QUICHE_EXPORT MetadataFrameSequence {
- public:
- MetadataFrameSequence(SpdyStreamId stream_id, spdy::Http2HeaderBlock payload);
-
- // Copies are not allowed.
- MetadataFrameSequence(const MetadataFrameSequence& other) = delete;
- MetadataFrameSequence& operator=(const MetadataFrameSequence& other) = delete;
-
- // True if Next() would return non-nullptr.
- bool HasNext() const;
-
- // Returns the next HTTP/2 METADATA frame for this block, unless the block has
- // been entirely serialized in frames returned by previous calls of Next(), in
- // which case returns nullptr.
- std::unique_ptr<spdy::SpdyFrameIR> Next();
-
- SpdyStreamId stream_id() const { return stream_id_; }
-
- private:
- SpdyStreamId stream_id_;
- Http2HeaderBlock payload_;
- HpackEncoder encoder_;
- std::unique_ptr<HpackEncoder::ProgressiveEncoder> progressive_encoder_;
-};
-
-} // namespace spdy
-
-#endif // QUICHE_SPDY_CORE_METADATA_EXTENSION_H_
diff --git a/quiche/spdy/core/metadata_extension_test.cc b/quiche/spdy/core/metadata_extension_test.cc
deleted file mode 100644
index 4f10006..0000000
--- a/quiche/spdy/core/metadata_extension_test.cc
+++ /dev/null
@@ -1,285 +0,0 @@
-#include "quiche/spdy/core/metadata_extension.h"
-
-#include <cstddef>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/container/flat_hash_map.h"
-#include "absl/functional/bind_front.h"
-#include "absl/strings/string_view.h"
-#include "quiche/common/platform/api/quiche_test.h"
-#include "quiche/spdy/core/array_output_buffer.h"
-#include "quiche/spdy/core/http2_frame_decoder_adapter.h"
-#include "quiche/spdy/core/http2_header_block.h"
-#include "quiche/spdy/core/spdy_framer.h"
-#include "quiche/spdy/core/spdy_no_op_visitor.h"
-#include "quiche/spdy/core/spdy_protocol.h"
-#include "quiche/spdy/test_tools/mock_spdy_framer_visitor.h"
-
-namespace spdy {
-namespace test {
-namespace {
-
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::HasSubstr;
-using ::testing::IsEmpty;
-
-const size_t kBufferSize = 64 * 1024;
-char kBuffer[kBufferSize];
-
-class MetadataExtensionTest : public quiche::test::QuicheTest {
- protected:
- MetadataExtensionTest() : test_buffer_(kBuffer, kBufferSize) {}
-
- void SetUp() override {
- extension_ = std::make_unique<MetadataVisitor>(
- absl::bind_front(&MetadataExtensionTest::OnCompletePayload, this),
- absl::bind_front(&MetadataExtensionTest::OnMetadataSupport, this));
- }
-
- void OnCompletePayload(spdy::SpdyStreamId stream_id,
- MetadataVisitor::MetadataPayload payload) {
- ++received_count_;
- received_payload_map_.insert(std::make_pair(stream_id, std::move(payload)));
- }
-
- void OnMetadataSupport(bool peer_supports_metadata) {
- EXPECT_EQ(peer_supports_metadata, extension_->PeerSupportsMetadata());
- received_metadata_support_.push_back(peer_supports_metadata);
- }
-
- Http2HeaderBlock PayloadForData(absl::string_view data) {
- Http2HeaderBlock block;
- block["example-payload"] = data;
- return block;
- }
-
- std::unique_ptr<MetadataVisitor> extension_;
- absl::flat_hash_map<spdy::SpdyStreamId, Http2HeaderBlock>
- received_payload_map_;
- std::vector<bool> received_metadata_support_;
- size_t received_count_ = 0;
- spdy::ArrayOutputBuffer test_buffer_;
-};
-
-// This test verifies that the MetadataVisitor is initialized to a state where
-// it believes the peer does not support metadata.
-TEST_F(MetadataExtensionTest, MetadataNotSupported) {
- EXPECT_FALSE(extension_->PeerSupportsMetadata());
- EXPECT_THAT(received_metadata_support_, IsEmpty());
-}
-
-// This test verifies that upon receiving a specific setting, the extension
-// realizes that the peer supports metadata.
-TEST_F(MetadataExtensionTest, MetadataSupported) {
- EXPECT_FALSE(extension_->PeerSupportsMetadata());
- // 3 is not an appropriate value for the metadata extension key.
- extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 3);
- EXPECT_FALSE(extension_->PeerSupportsMetadata());
- extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
- ASSERT_TRUE(extension_->PeerSupportsMetadata());
- extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 0);
- EXPECT_FALSE(extension_->PeerSupportsMetadata());
- EXPECT_THAT(received_metadata_support_, ElementsAre(true, false));
-}
-
-TEST_F(MetadataExtensionTest, MetadataDeliveredToUnknownFrameCallbacks) {
- const char kData[] = "some payload";
- Http2HeaderBlock payload = PayloadForData(kData);
-
- extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
- ASSERT_TRUE(extension_->PeerSupportsMetadata());
-
- MetadataFrameSequence sequence(3, std::move(payload));
-
- http2::Http2DecoderAdapter deframer;
- ::testing::StrictMock<MockSpdyFramerVisitor> visitor;
- deframer.set_visitor(&visitor);
-
- EXPECT_CALL(visitor,
- OnCommonHeader(3, _, MetadataVisitor::kMetadataFrameType, _));
- // The Return(true) should not be necessary. http://b/36023792
- EXPECT_CALL(visitor, OnUnknownFrame(3, MetadataVisitor::kMetadataFrameType))
- .WillOnce(::testing::Return(true));
- EXPECT_CALL(visitor,
- OnUnknownFrameStart(3, _, MetadataVisitor::kMetadataFrameType,
- MetadataVisitor::kEndMetadataFlag));
- EXPECT_CALL(visitor, OnUnknownFramePayload(3, HasSubstr(kData)));
-
- SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
- auto frame = sequence.Next();
- ASSERT_TRUE(frame != nullptr);
- while (frame != nullptr) {
- const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
- ASSERT_GT(frame_size, 0u);
- ASSERT_FALSE(deframer.HasError());
- ASSERT_EQ(frame_size, test_buffer_.Size());
- EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
- test_buffer_.Reset();
- frame = sequence.Next();
- }
- EXPECT_FALSE(deframer.HasError());
- EXPECT_THAT(received_metadata_support_, ElementsAre(true));
-}
-
-// This test verifies that the METADATA frame emitted by a MetadataExtension
-// can be parsed by another SpdyFramer with a MetadataVisitor.
-TEST_F(MetadataExtensionTest, MetadataPayloadEndToEnd) {
- Http2HeaderBlock block1;
- block1["foo"] = "Some metadata value.";
- Http2HeaderBlock block2;
- block2["bar"] =
- "The color taupe truly represents a triumph of the human spirit over "
- "adversity.";
- block2["baz"] =
- "Or perhaps it represents abject surrender to the implacable and "
- "incomprehensible forces of the universe.";
- const absl::string_view binary_payload{"binary\0payload", 14};
- block2["qux"] = binary_payload;
- EXPECT_EQ(binary_payload, block2["qux"]);
- for (const Http2HeaderBlock& payload_block :
- {std::move(block1), std::move(block2)}) {
- extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
- ASSERT_TRUE(extension_->PeerSupportsMetadata());
-
- MetadataFrameSequence sequence(3, payload_block.Clone());
- http2::Http2DecoderAdapter deframer;
- ::spdy::SpdyNoOpVisitor visitor;
- deframer.set_visitor(&visitor);
- deframer.set_extension_visitor(extension_.get());
- SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
- auto frame = sequence.Next();
- ASSERT_TRUE(frame != nullptr);
- while (frame != nullptr) {
- const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
- ASSERT_GT(frame_size, 0u);
- ASSERT_FALSE(deframer.HasError());
- ASSERT_EQ(frame_size, test_buffer_.Size());
- EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
- test_buffer_.Reset();
- frame = sequence.Next();
- }
- EXPECT_EQ(1u, received_count_);
- auto it = received_payload_map_.find(3);
- ASSERT_TRUE(it != received_payload_map_.end());
- EXPECT_EQ(payload_block, it->second);
-
- received_count_ = 0;
- received_payload_map_.clear();
- }
-}
-
-// This test verifies that METADATA frames for two different streams can be
-// interleaved and still successfully parsed by another SpdyFramer with a
-// MetadataVisitor.
-TEST_F(MetadataExtensionTest, MetadataPayloadInterleaved) {
- const std::string kData1 = std::string(65 * 1024, 'a');
- const std::string kData2 = std::string(65 * 1024, 'b');
- const Http2HeaderBlock payload1 = PayloadForData(kData1);
- const Http2HeaderBlock payload2 = PayloadForData(kData2);
-
- extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
- ASSERT_TRUE(extension_->PeerSupportsMetadata());
-
- MetadataFrameSequence sequence1(3, payload1.Clone());
- MetadataFrameSequence sequence2(5, payload2.Clone());
-
- http2::Http2DecoderAdapter deframer;
- ::spdy::SpdyNoOpVisitor visitor;
- deframer.set_visitor(&visitor);
- deframer.set_extension_visitor(extension_.get());
-
- SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
- auto frame1 = sequence1.Next();
- ASSERT_TRUE(frame1 != nullptr);
- auto frame2 = sequence2.Next();
- ASSERT_TRUE(frame2 != nullptr);
- while (frame1 != nullptr || frame2 != nullptr) {
- for (auto frame : {frame1.get(), frame2.get()}) {
- if (frame != nullptr) {
- const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
- ASSERT_GT(frame_size, 0u);
- ASSERT_FALSE(deframer.HasError());
- ASSERT_EQ(frame_size, test_buffer_.Size());
- EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
- test_buffer_.Reset();
- }
- }
- frame1 = sequence1.Next();
- frame2 = sequence2.Next();
- }
- EXPECT_EQ(2u, received_count_);
- auto it = received_payload_map_.find(3);
- ASSERT_TRUE(it != received_payload_map_.end());
- EXPECT_EQ(payload1, it->second);
-
- it = received_payload_map_.find(5);
- ASSERT_TRUE(it != received_payload_map_.end());
- EXPECT_EQ(payload2, it->second);
-}
-
-// Test that an empty metadata block is serialized as a single frame with
-// END_METADATA set and empty frame payload.
-TEST_F(MetadataExtensionTest, EmptyBlock) {
- MetadataFrameSequence sequence(1, Http2HeaderBlock{});
-
- EXPECT_TRUE(sequence.HasNext());
- std::unique_ptr<SpdyFrameIR> frame = sequence.Next();
- EXPECT_FALSE(sequence.HasNext());
-
- auto* const metadata_frame = static_cast<SpdyUnknownIR*>(frame.get());
- EXPECT_EQ(MetadataVisitor::kEndMetadataFlag,
- metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
- EXPECT_TRUE(metadata_frame->payload().empty());
-}
-
-// Test that a small metadata block is serialized as a single frame with
-// END_METADATA set and non-empty frame payload.
-TEST_F(MetadataExtensionTest, SmallBlock) {
- Http2HeaderBlock metadata_block;
- metadata_block["foo"] = "bar";
- MetadataFrameSequence sequence(1, std::move(metadata_block));
-
- EXPECT_TRUE(sequence.HasNext());
- std::unique_ptr<SpdyFrameIR> frame = sequence.Next();
- EXPECT_FALSE(sequence.HasNext());
-
- auto* const metadata_frame = static_cast<SpdyUnknownIR*>(frame.get());
- EXPECT_EQ(MetadataVisitor::kEndMetadataFlag,
- metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
- EXPECT_LT(0u, metadata_frame->payload().size());
-}
-
-// Test that a large metadata block is serialized as multiple frames,
-// with END_METADATA set only on the last one.
-TEST_F(MetadataExtensionTest, LargeBlock) {
- Http2HeaderBlock metadata_block;
- metadata_block["foo"] = std::string(65 * 1024, 'a');
- MetadataFrameSequence sequence(1, std::move(metadata_block));
-
- int frame_count = 0;
- while (sequence.HasNext()) {
- std::unique_ptr<SpdyFrameIR> frame = sequence.Next();
- ++frame_count;
-
- auto* const metadata_frame = static_cast<SpdyUnknownIR*>(frame.get());
- EXPECT_LT(0u, metadata_frame->payload().size());
-
- if (sequence.HasNext()) {
- EXPECT_EQ(0u,
- metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
- } else {
- EXPECT_EQ(MetadataVisitor::kEndMetadataFlag,
- metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
- }
- }
-
- EXPECT_LE(2, frame_count);
-}
-
-} // anonymous namespace
-} // namespace test
-} // namespace spdy