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