• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "quiche/spdy/core/metadata_extension.h"
2 
3 #include <memory>
4 
5 #include "absl/container/flat_hash_map.h"
6 #include "absl/functional/bind_front.h"
7 #include "absl/strings/str_cat.h"
8 #include "absl/strings/string_view.h"
9 #include "quiche/common/platform/api/quiche_test.h"
10 #include "quiche/spdy/core/array_output_buffer.h"
11 #include "quiche/spdy/core/spdy_framer.h"
12 #include "quiche/spdy/core/spdy_no_op_visitor.h"
13 #include "quiche/spdy/core/spdy_protocol.h"
14 #include "quiche/spdy/test_tools/mock_spdy_framer_visitor.h"
15 
16 namespace spdy {
17 namespace test {
18 namespace {
19 
20 using ::absl::bind_front;
21 using ::testing::_;
22 using ::testing::ElementsAre;
23 using ::testing::HasSubstr;
24 using ::testing::IsEmpty;
25 
26 const size_t kBufferSize = 64 * 1024;
27 char kBuffer[kBufferSize];
28 
29 class MetadataExtensionTest : public quiche::test::QuicheTest {
30  protected:
MetadataExtensionTest()31   MetadataExtensionTest() : test_buffer_(kBuffer, kBufferSize) {}
32 
SetUp()33   void SetUp() override {
34     extension_ = std::make_unique<MetadataVisitor>(
35         bind_front(&MetadataExtensionTest::OnCompletePayload, this),
36         bind_front(&MetadataExtensionTest::OnMetadataSupport, this));
37   }
38 
OnCompletePayload(spdy::SpdyStreamId stream_id,MetadataVisitor::MetadataPayload payload)39   void OnCompletePayload(spdy::SpdyStreamId stream_id,
40                          MetadataVisitor::MetadataPayload payload) {
41     ++received_count_;
42     received_payload_map_.insert(std::make_pair(stream_id, std::move(payload)));
43   }
44 
OnMetadataSupport(bool peer_supports_metadata)45   void OnMetadataSupport(bool peer_supports_metadata) {
46     EXPECT_EQ(peer_supports_metadata, extension_->PeerSupportsMetadata());
47     received_metadata_support_.push_back(peer_supports_metadata);
48   }
49 
PayloadForData(absl::string_view data)50   Http2HeaderBlock PayloadForData(absl::string_view data) {
51     Http2HeaderBlock block;
52     block["example-payload"] = data;
53     return block;
54   }
55 
56   std::unique_ptr<MetadataVisitor> extension_;
57   absl::flat_hash_map<spdy::SpdyStreamId, Http2HeaderBlock>
58       received_payload_map_;
59   std::vector<bool> received_metadata_support_;
60   size_t received_count_ = 0;
61   spdy::ArrayOutputBuffer test_buffer_;
62 };
63 
64 // This test verifies that the MetadataVisitor is initialized to a state where
65 // it believes the peer does not support metadata.
TEST_F(MetadataExtensionTest,MetadataNotSupported)66 TEST_F(MetadataExtensionTest, MetadataNotSupported) {
67   EXPECT_FALSE(extension_->PeerSupportsMetadata());
68   EXPECT_THAT(received_metadata_support_, IsEmpty());
69 }
70 
71 // This test verifies that upon receiving a specific setting, the extension
72 // realizes that the peer supports metadata.
TEST_F(MetadataExtensionTest,MetadataSupported)73 TEST_F(MetadataExtensionTest, MetadataSupported) {
74   EXPECT_FALSE(extension_->PeerSupportsMetadata());
75   // 3 is not an appropriate value for the metadata extension key.
76   extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 3);
77   EXPECT_FALSE(extension_->PeerSupportsMetadata());
78   extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
79   ASSERT_TRUE(extension_->PeerSupportsMetadata());
80   extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 0);
81   EXPECT_FALSE(extension_->PeerSupportsMetadata());
82   EXPECT_THAT(received_metadata_support_, ElementsAre(true, false));
83 }
84 
TEST_F(MetadataExtensionTest,MetadataDeliveredToUnknownFrameCallbacks)85 TEST_F(MetadataExtensionTest, MetadataDeliveredToUnknownFrameCallbacks) {
86   const char kData[] = "some payload";
87   Http2HeaderBlock payload = PayloadForData(kData);
88 
89   extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
90   ASSERT_TRUE(extension_->PeerSupportsMetadata());
91 
92   MetadataFrameSequence sequence(3, std::move(payload));
93 
94   http2::Http2DecoderAdapter deframer;
95   ::testing::StrictMock<MockSpdyFramerVisitor> visitor;
96   deframer.set_visitor(&visitor);
97 
98   EXPECT_CALL(visitor,
99               OnCommonHeader(3, _, MetadataVisitor::kMetadataFrameType, _));
100   // The Return(true) should not be necessary. http://b/36023792
101   EXPECT_CALL(visitor, OnUnknownFrame(3, MetadataVisitor::kMetadataFrameType))
102       .WillOnce(::testing::Return(true));
103   EXPECT_CALL(visitor,
104               OnUnknownFrameStart(3, _, MetadataVisitor::kMetadataFrameType,
105                                   MetadataVisitor::kEndMetadataFlag));
106   EXPECT_CALL(visitor, OnUnknownFramePayload(3, HasSubstr(kData)));
107 
108   SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
109   auto frame = sequence.Next();
110   ASSERT_TRUE(frame != nullptr);
111   while (frame != nullptr) {
112     const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
113     ASSERT_GT(frame_size, 0u);
114     ASSERT_FALSE(deframer.HasError());
115     ASSERT_EQ(frame_size, test_buffer_.Size());
116     EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
117     test_buffer_.Reset();
118     frame = sequence.Next();
119   }
120   EXPECT_FALSE(deframer.HasError());
121   EXPECT_THAT(received_metadata_support_, ElementsAre(true));
122 }
123 
124 // This test verifies that the METADATA frame emitted by a MetadataExtension
125 // can be parsed by another SpdyFramer with a MetadataVisitor.
TEST_F(MetadataExtensionTest,MetadataPayloadEndToEnd)126 TEST_F(MetadataExtensionTest, MetadataPayloadEndToEnd) {
127   Http2HeaderBlock block1;
128   block1["foo"] = "Some metadata value.";
129   Http2HeaderBlock block2;
130   block2["bar"] =
131       "The color taupe truly represents a triumph of the human spirit over "
132       "adversity.";
133   block2["baz"] =
134       "Or perhaps it represents abject surrender to the implacable and "
135       "incomprehensible forces of the universe.";
136   const absl::string_view binary_payload{"binary\0payload", 14};
137   block2["qux"] = binary_payload;
138   EXPECT_EQ(binary_payload, block2["qux"]);
139   for (const Http2HeaderBlock& payload_block :
140        {std::move(block1), std::move(block2)}) {
141     extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
142     ASSERT_TRUE(extension_->PeerSupportsMetadata());
143 
144     MetadataFrameSequence sequence(3, payload_block.Clone());
145     http2::Http2DecoderAdapter deframer;
146     ::spdy::SpdyNoOpVisitor visitor;
147     deframer.set_visitor(&visitor);
148     deframer.set_extension_visitor(extension_.get());
149     SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
150     auto frame = sequence.Next();
151     ASSERT_TRUE(frame != nullptr);
152     while (frame != nullptr) {
153       const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
154       ASSERT_GT(frame_size, 0u);
155       ASSERT_FALSE(deframer.HasError());
156       ASSERT_EQ(frame_size, test_buffer_.Size());
157       EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
158       test_buffer_.Reset();
159       frame = sequence.Next();
160     }
161     EXPECT_EQ(1u, received_count_);
162     auto it = received_payload_map_.find(3);
163     ASSERT_TRUE(it != received_payload_map_.end());
164     EXPECT_EQ(payload_block, it->second);
165 
166     received_count_ = 0;
167     received_payload_map_.clear();
168   }
169 }
170 
171 // This test verifies that METADATA frames for two different streams can be
172 // interleaved and still successfully parsed by another SpdyFramer with a
173 // MetadataVisitor.
TEST_F(MetadataExtensionTest,MetadataPayloadInterleaved)174 TEST_F(MetadataExtensionTest, MetadataPayloadInterleaved) {
175   const std::string kData1 = std::string(65 * 1024, 'a');
176   const std::string kData2 = std::string(65 * 1024, 'b');
177   const Http2HeaderBlock payload1 = PayloadForData(kData1);
178   const Http2HeaderBlock payload2 = PayloadForData(kData2);
179 
180   extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
181   ASSERT_TRUE(extension_->PeerSupportsMetadata());
182 
183   MetadataFrameSequence sequence1(3, payload1.Clone());
184   MetadataFrameSequence sequence2(5, payload2.Clone());
185 
186   http2::Http2DecoderAdapter deframer;
187   ::spdy::SpdyNoOpVisitor visitor;
188   deframer.set_visitor(&visitor);
189   deframer.set_extension_visitor(extension_.get());
190 
191   SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
192   auto frame1 = sequence1.Next();
193   ASSERT_TRUE(frame1 != nullptr);
194   auto frame2 = sequence2.Next();
195   ASSERT_TRUE(frame2 != nullptr);
196   while (frame1 != nullptr || frame2 != nullptr) {
197     for (auto frame : {frame1.get(), frame2.get()}) {
198       if (frame != nullptr) {
199         const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
200         ASSERT_GT(frame_size, 0u);
201         ASSERT_FALSE(deframer.HasError());
202         ASSERT_EQ(frame_size, test_buffer_.Size());
203         EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
204         test_buffer_.Reset();
205       }
206     }
207     frame1 = sequence1.Next();
208     frame2 = sequence2.Next();
209   }
210   EXPECT_EQ(2u, received_count_);
211   auto it = received_payload_map_.find(3);
212   ASSERT_TRUE(it != received_payload_map_.end());
213   EXPECT_EQ(payload1, it->second);
214 
215   it = received_payload_map_.find(5);
216   ASSERT_TRUE(it != received_payload_map_.end());
217   EXPECT_EQ(payload2, it->second);
218 }
219 
220 // Test that an empty metadata block is serialized as a single frame with
221 // END_METADATA set and empty frame payload.
TEST_F(MetadataExtensionTest,EmptyBlock)222 TEST_F(MetadataExtensionTest, EmptyBlock) {
223   MetadataFrameSequence sequence(1, Http2HeaderBlock{});
224 
225   EXPECT_TRUE(sequence.HasNext());
226   std::unique_ptr<SpdyFrameIR> frame = sequence.Next();
227   EXPECT_FALSE(sequence.HasNext());
228 
229   auto* const metadata_frame = static_cast<SpdyUnknownIR*>(frame.get());
230   EXPECT_EQ(MetadataVisitor::kEndMetadataFlag,
231             metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
232   EXPECT_TRUE(metadata_frame->payload().empty());
233 }
234 
235 // Test that a small metadata block is serialized as a single frame with
236 // END_METADATA set and non-empty frame payload.
TEST_F(MetadataExtensionTest,SmallBlock)237 TEST_F(MetadataExtensionTest, SmallBlock) {
238   Http2HeaderBlock metadata_block;
239   metadata_block["foo"] = "bar";
240   MetadataFrameSequence sequence(1, std::move(metadata_block));
241 
242   EXPECT_TRUE(sequence.HasNext());
243   std::unique_ptr<SpdyFrameIR> frame = sequence.Next();
244   EXPECT_FALSE(sequence.HasNext());
245 
246   auto* const metadata_frame = static_cast<SpdyUnknownIR*>(frame.get());
247   EXPECT_EQ(MetadataVisitor::kEndMetadataFlag,
248             metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
249   EXPECT_LT(0u, metadata_frame->payload().size());
250 }
251 
252 // Test that a large metadata block is serialized as multiple frames,
253 // with END_METADATA set only on the last one.
TEST_F(MetadataExtensionTest,LargeBlock)254 TEST_F(MetadataExtensionTest, LargeBlock) {
255   Http2HeaderBlock metadata_block;
256   metadata_block["foo"] = std::string(65 * 1024, 'a');
257   MetadataFrameSequence sequence(1, std::move(metadata_block));
258 
259   int frame_count = 0;
260   while (sequence.HasNext()) {
261     std::unique_ptr<SpdyFrameIR> frame = sequence.Next();
262     ++frame_count;
263 
264     auto* const metadata_frame = static_cast<SpdyUnknownIR*>(frame.get());
265     EXPECT_LT(0u, metadata_frame->payload().size());
266 
267     if (sequence.HasNext()) {
268       EXPECT_EQ(0u,
269                 metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
270     } else {
271       EXPECT_EQ(MetadataVisitor::kEndMetadataFlag,
272                 metadata_frame->flags() & MetadataVisitor::kEndMetadataFlag);
273     }
274   }
275 
276   EXPECT_LE(2, frame_count);
277 }
278 
279 }  // anonymous namespace
280 }  // namespace test
281 }  // namespace spdy
282