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