1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/spdy/core/hpack/hpack_decoder_adapter.h"
6
7 // Tests of HpackDecoderAdapter.
8
9 #include <stdint.h>
10
11 #include <string>
12 #include <tuple>
13 #include <utility>
14 #include <vector>
15
16 #include "absl/base/macros.h"
17 #include "absl/strings/escaping.h"
18 #include "quiche/http2/hpack/decoder/hpack_decoder_state.h"
19 #include "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
20 #include "quiche/http2/test_tools/hpack_block_builder.h"
21 #include "quiche/http2/test_tools/http2_random.h"
22 #include "quiche/common/platform/api/quiche_logging.h"
23 #include "quiche/common/platform/api/quiche_test.h"
24 #include "quiche/common/quiche_text_utils.h"
25 #include "quiche/spdy/core/hpack/hpack_constants.h"
26 #include "quiche/spdy/core/hpack/hpack_encoder.h"
27 #include "quiche/spdy/core/hpack/hpack_output_stream.h"
28 #include "quiche/spdy/core/recording_headers_handler.h"
29 #include "quiche/spdy/test_tools/spdy_test_utils.h"
30
31 using ::http2::HpackEntryType;
32 using ::http2::HpackStringPair;
33 using ::http2::test::HpackBlockBuilder;
34 using ::http2::test::HpackDecoderPeer;
35 using ::testing::ElementsAre;
36 using ::testing::Pair;
37
38 namespace http2 {
39 namespace test {
40
41 class HpackDecoderStatePeer {
42 public:
GetDecoderTables(HpackDecoderState * state)43 static HpackDecoderTables* GetDecoderTables(HpackDecoderState* state) {
44 return &state->decoder_tables_;
45 }
46 };
47
48 class HpackDecoderPeer {
49 public:
GetDecoderState(HpackDecoder * decoder)50 static HpackDecoderState* GetDecoderState(HpackDecoder* decoder) {
51 return &decoder->decoder_state_;
52 }
GetDecoderTables(HpackDecoder * decoder)53 static HpackDecoderTables* GetDecoderTables(HpackDecoder* decoder) {
54 return HpackDecoderStatePeer::GetDecoderTables(GetDecoderState(decoder));
55 }
56 };
57
58 } // namespace test
59 } // namespace http2
60
61 namespace spdy {
62 namespace test {
63
64 class HpackDecoderAdapterPeer {
65 public:
HpackDecoderAdapterPeer(HpackDecoderAdapter * decoder)66 explicit HpackDecoderAdapterPeer(HpackDecoderAdapter* decoder)
67 : decoder_(decoder) {}
68
HandleHeaderRepresentation(const std::string & name,const std::string & value)69 void HandleHeaderRepresentation(const std::string& name,
70 const std::string& value) {
71 decoder_->listener_adapter_.OnHeader(name, value);
72 }
73
GetDecoderTables()74 http2::HpackDecoderTables* GetDecoderTables() {
75 return HpackDecoderPeer::GetDecoderTables(&decoder_->hpack_decoder_);
76 }
77
GetTableEntry(uint32_t index)78 const HpackStringPair* GetTableEntry(uint32_t index) {
79 return GetDecoderTables()->Lookup(index);
80 }
81
current_header_table_size()82 size_t current_header_table_size() {
83 return GetDecoderTables()->current_header_table_size();
84 }
85
header_table_size_limit()86 size_t header_table_size_limit() {
87 return GetDecoderTables()->header_table_size_limit();
88 }
89
set_header_table_size_limit(size_t size)90 void set_header_table_size_limit(size_t size) {
91 return GetDecoderTables()->DynamicTableSizeUpdate(size);
92 }
93
94 private:
95 HpackDecoderAdapter* decoder_;
96 };
97
98 class HpackEncoderPeer {
99 public:
CookieToCrumbs(const HpackEncoder::Representation & cookie,HpackEncoder::Representations * crumbs_out)100 static void CookieToCrumbs(const HpackEncoder::Representation& cookie,
101 HpackEncoder::Representations* crumbs_out) {
102 HpackEncoder::CookieToCrumbs(cookie, crumbs_out);
103 }
104 };
105
106 namespace {
107
108 const bool kNoCheckDecodedSize = false;
109 const char* kCookieKey = "cookie";
110
111 // Is HandleControlFrameHeadersStart to be called, and with what value?
112 enum StartChoice { START_WITH_HANDLER, START_WITHOUT_HANDLER, NO_START };
113
114 class HpackDecoderAdapterTest
115 : public quiche::test::QuicheTestWithParam<std::tuple<StartChoice, bool>> {
116 protected:
HpackDecoderAdapterTest()117 HpackDecoderAdapterTest() : decoder_(), decoder_peer_(&decoder_) {}
118
SetUp()119 void SetUp() override {
120 std::tie(start_choice_, randomly_split_input_buffer_) = GetParam();
121 }
122
HandleControlFrameHeadersStart()123 void HandleControlFrameHeadersStart() {
124 bytes_passed_in_ = 0;
125 switch (start_choice_) {
126 case START_WITH_HANDLER:
127 decoder_.HandleControlFrameHeadersStart(&handler_);
128 break;
129 case START_WITHOUT_HANDLER:
130 decoder_.HandleControlFrameHeadersStart(nullptr);
131 break;
132 case NO_START:
133 break;
134 }
135 }
136
HandleControlFrameHeadersData(absl::string_view str)137 bool HandleControlFrameHeadersData(absl::string_view str) {
138 QUICHE_VLOG(3) << "HandleControlFrameHeadersData:\n"
139 << quiche::QuicheTextUtils::HexDump(str);
140 bytes_passed_in_ += str.size();
141 return decoder_.HandleControlFrameHeadersData(str.data(), str.size());
142 }
143
HandleControlFrameHeadersComplete()144 bool HandleControlFrameHeadersComplete() {
145 bool rc = decoder_.HandleControlFrameHeadersComplete();
146 return rc;
147 }
148
DecodeHeaderBlock(absl::string_view str,bool check_decoded_size=true)149 bool DecodeHeaderBlock(absl::string_view str,
150 bool check_decoded_size = true) {
151 // Don't call this again if HandleControlFrameHeadersData failed previously.
152 EXPECT_FALSE(decode_has_failed_);
153 HandleControlFrameHeadersStart();
154 if (randomly_split_input_buffer_) {
155 do {
156 // Decode some fragment of the remaining bytes.
157 size_t bytes = str.size();
158 if (!str.empty()) {
159 bytes = random_.Uniform(str.size()) + 1;
160 }
161 EXPECT_LE(bytes, str.size());
162 if (!HandleControlFrameHeadersData(str.substr(0, bytes))) {
163 decode_has_failed_ = true;
164 return false;
165 }
166 str.remove_prefix(bytes);
167 } while (!str.empty());
168 } else if (!HandleControlFrameHeadersData(str)) {
169 decode_has_failed_ = true;
170 return false;
171 }
172 if (start_choice_ == START_WITH_HANDLER) {
173 if (!HandleControlFrameHeadersComplete()) {
174 decode_has_failed_ = true;
175 return false;
176 }
177 EXPECT_EQ(handler_.compressed_header_bytes(), bytes_passed_in_);
178 } else {
179 if (!HandleControlFrameHeadersComplete()) {
180 decode_has_failed_ = true;
181 return false;
182 }
183 }
184 if (check_decoded_size && start_choice_ == START_WITH_HANDLER) {
185 EXPECT_EQ(handler_.uncompressed_header_bytes(),
186 SizeOfHeaders(decoded_block()));
187 }
188 return true;
189 }
190
EncodeAndDecodeDynamicTableSizeUpdates(size_t first,size_t second)191 bool EncodeAndDecodeDynamicTableSizeUpdates(size_t first, size_t second) {
192 HpackBlockBuilder hbb;
193 hbb.AppendDynamicTableSizeUpdate(first);
194 if (second != first) {
195 hbb.AppendDynamicTableSizeUpdate(second);
196 }
197 return DecodeHeaderBlock(hbb.buffer());
198 }
199
decoded_block() const200 const Http2HeaderBlock& decoded_block() const {
201 if (start_choice_ == START_WITH_HANDLER) {
202 return handler_.decoded_block();
203 } else {
204 return decoder_.decoded_block();
205 }
206 }
207
SizeOfHeaders(const Http2HeaderBlock & headers)208 static size_t SizeOfHeaders(const Http2HeaderBlock& headers) {
209 size_t size = 0;
210 for (const auto& kv : headers) {
211 if (kv.first == kCookieKey) {
212 HpackEncoder::Representations crumbs;
213 HpackEncoderPeer::CookieToCrumbs(kv, &crumbs);
214 for (const auto& crumb : crumbs) {
215 size += crumb.first.size() + crumb.second.size();
216 }
217 } else {
218 size += kv.first.size() + kv.second.size();
219 }
220 }
221 return size;
222 }
223
DecodeBlockExpectingSuccess(absl::string_view str)224 const Http2HeaderBlock& DecodeBlockExpectingSuccess(absl::string_view str) {
225 EXPECT_TRUE(DecodeHeaderBlock(str));
226 return decoded_block();
227 }
228
expectEntry(size_t index,size_t size,const std::string & name,const std::string & value)229 void expectEntry(size_t index, size_t size, const std::string& name,
230 const std::string& value) {
231 const HpackStringPair* entry = decoder_peer_.GetTableEntry(index);
232 EXPECT_EQ(name, entry->name) << "index " << index;
233 EXPECT_EQ(value, entry->value);
234 EXPECT_EQ(size, entry->size());
235 }
236
MakeHeaderBlock(const std::vector<std::pair<std::string,std::string>> & headers)237 Http2HeaderBlock MakeHeaderBlock(
238 const std::vector<std::pair<std::string, std::string>>& headers) {
239 Http2HeaderBlock result;
240 for (const auto& kv : headers) {
241 result.AppendValueOrAddHeader(kv.first, kv.second);
242 }
243 return result;
244 }
245
246 http2::test::Http2Random random_;
247 HpackDecoderAdapter decoder_;
248 test::HpackDecoderAdapterPeer decoder_peer_;
249 RecordingHeadersHandler handler_;
250 StartChoice start_choice_;
251 bool randomly_split_input_buffer_;
252 bool decode_has_failed_ = false;
253 size_t bytes_passed_in_;
254 };
255
256 INSTANTIATE_TEST_SUITE_P(
257 NoHandler, HpackDecoderAdapterTest,
258 ::testing::Combine(::testing::Values(START_WITHOUT_HANDLER, NO_START),
259 ::testing::Bool()));
260
261 INSTANTIATE_TEST_SUITE_P(
262 WithHandler, HpackDecoderAdapterTest,
263 ::testing::Combine(::testing::Values(START_WITH_HANDLER),
264 ::testing::Bool()));
265
TEST_P(HpackDecoderAdapterTest,ApplyHeaderTableSizeSetting)266 TEST_P(HpackDecoderAdapterTest, ApplyHeaderTableSizeSetting) {
267 EXPECT_EQ(4096u, decoder_.GetCurrentHeaderTableSizeSetting());
268 decoder_.ApplyHeaderTableSizeSetting(12 * 1024);
269 EXPECT_EQ(12288u, decoder_.GetCurrentHeaderTableSizeSetting());
270 }
271
TEST_P(HpackDecoderAdapterTest,AddHeaderDataWithHandleControlFrameHeadersData)272 TEST_P(HpackDecoderAdapterTest,
273 AddHeaderDataWithHandleControlFrameHeadersData) {
274 // The hpack decode buffer size is limited in size. This test verifies that
275 // adding encoded data under that limit is accepted, and data that exceeds the
276 // limit is rejected.
277 HandleControlFrameHeadersStart();
278 const size_t kMaxBufferSizeBytes = 50;
279 const std::string a_value = std::string(49, 'x');
280 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
281 HpackBlockBuilder hbb;
282 hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
283 false, "a", false, a_value);
284 const std::string& s = hbb.buffer();
285 EXPECT_GT(s.size(), kMaxBufferSizeBytes);
286
287 // Any one in input buffer must not exceed kMaxBufferSizeBytes.
288 EXPECT_TRUE(HandleControlFrameHeadersData(s.substr(0, s.size() / 2)));
289 EXPECT_TRUE(HandleControlFrameHeadersData(s.substr(s.size() / 2)));
290
291 EXPECT_FALSE(HandleControlFrameHeadersData(s));
292 Http2HeaderBlock expected_block = MakeHeaderBlock({{"a", a_value}});
293 EXPECT_EQ(expected_block, decoded_block());
294 }
295
TEST_P(HpackDecoderAdapterTest,NameTooLong)296 TEST_P(HpackDecoderAdapterTest, NameTooLong) {
297 // Verify that a name longer than the allowed size generates an error.
298 const size_t kMaxBufferSizeBytes = 50;
299 const std::string name = std::string(2 * kMaxBufferSizeBytes, 'x');
300 const std::string value = "abc";
301
302 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
303
304 HpackBlockBuilder hbb;
305 hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
306 false, name, false, value);
307
308 const size_t fragment_size = (3 * kMaxBufferSizeBytes) / 2;
309 const std::string fragment = hbb.buffer().substr(0, fragment_size);
310
311 HandleControlFrameHeadersStart();
312 EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
313 }
314
TEST_P(HpackDecoderAdapterTest,HeaderTooLongToBuffer)315 TEST_P(HpackDecoderAdapterTest, HeaderTooLongToBuffer) {
316 // Verify that a header longer than the allowed size generates an error if
317 // it isn't all in one input buffer.
318 const std::string name = "some-key";
319 const std::string value = "some-value";
320 const size_t kMaxBufferSizeBytes = name.size() + value.size() - 2;
321 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
322
323 HpackBlockBuilder hbb;
324 hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
325 false, name, false, value);
326 const size_t fragment_size = hbb.size() - 1;
327 const std::string fragment = hbb.buffer().substr(0, fragment_size);
328
329 HandleControlFrameHeadersStart();
330 EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
331 }
332
333 // Verify that a header block that exceeds the maximum length is rejected.
TEST_P(HpackDecoderAdapterTest,HeaderBlockTooLong)334 TEST_P(HpackDecoderAdapterTest, HeaderBlockTooLong) {
335 const std::string name = "some-key";
336 const std::string value = "some-value";
337 const size_t kMaxBufferSizeBytes = 1024;
338
339 HpackBlockBuilder hbb;
340 hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
341 name, false, value);
342 while (hbb.size() < kMaxBufferSizeBytes) {
343 hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
344 "", false, "");
345 }
346 // With no limit on the maximum header block size, the decoder handles the
347 // entire block successfully.
348 HandleControlFrameHeadersStart();
349 EXPECT_TRUE(HandleControlFrameHeadersData(hbb.buffer()));
350 EXPECT_TRUE(HandleControlFrameHeadersComplete());
351
352 // When a total byte limit is imposed, the decoder bails before the end of the
353 // block.
354 decoder_.set_max_header_block_bytes(kMaxBufferSizeBytes);
355 HandleControlFrameHeadersStart();
356 EXPECT_FALSE(HandleControlFrameHeadersData(hbb.buffer()));
357 }
358
359 // Decode with incomplete data in buffer.
TEST_P(HpackDecoderAdapterTest,DecodeWithIncompleteData)360 TEST_P(HpackDecoderAdapterTest, DecodeWithIncompleteData) {
361 HandleControlFrameHeadersStart();
362
363 // No need to wait for more data.
364 EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82"));
365 std::vector<std::pair<std::string, std::string>> expected_headers = {
366 {":method", "GET"}, {":path", "/index.html"}, {":method", "GET"}};
367
368 Http2HeaderBlock expected_block1 = MakeHeaderBlock(expected_headers);
369 EXPECT_EQ(expected_block1, decoded_block());
370
371 // Full and partial headers, won't add partial to the headers.
372 EXPECT_TRUE(
373 HandleControlFrameHeadersData("\x40\x03goo"
374 "\x03gar\xbe\x40\x04spam"));
375 expected_headers.push_back({"goo", "gar"});
376 expected_headers.push_back({"goo", "gar"});
377
378 Http2HeaderBlock expected_block2 = MakeHeaderBlock(expected_headers);
379 EXPECT_EQ(expected_block2, decoded_block());
380
381 // Add the needed data.
382 EXPECT_TRUE(HandleControlFrameHeadersData("\x04gggs"));
383
384 EXPECT_TRUE(HandleControlFrameHeadersComplete());
385
386 expected_headers.push_back({"spam", "gggs"});
387
388 Http2HeaderBlock expected_block3 = MakeHeaderBlock(expected_headers);
389 EXPECT_EQ(expected_block3, decoded_block());
390 }
391
TEST_P(HpackDecoderAdapterTest,HandleHeaderRepresentation)392 TEST_P(HpackDecoderAdapterTest, HandleHeaderRepresentation) {
393 // Make sure the decoder is properly initialized.
394 HandleControlFrameHeadersStart();
395 HandleControlFrameHeadersData("");
396
397 // All cookie crumbs are joined.
398 decoder_peer_.HandleHeaderRepresentation("cookie", " part 1");
399 decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 ");
400 decoder_peer_.HandleHeaderRepresentation("cookie", "part3");
401
402 // Already-delimited headers are passed through.
403 decoder_peer_.HandleHeaderRepresentation("passed-through",
404 std::string("foo\0baz", 7));
405
406 // Other headers are joined on \0. Case matters.
407 decoder_peer_.HandleHeaderRepresentation("joined", "joined");
408 decoder_peer_.HandleHeaderRepresentation("joineD", "value 1");
409 decoder_peer_.HandleHeaderRepresentation("joineD", "value 2");
410
411 // Empty headers remain empty.
412 decoder_peer_.HandleHeaderRepresentation("empty", "");
413
414 // Joined empty headers work as expected.
415 decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
416 decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo");
417 decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
418 decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
419
420 // Non-contiguous cookie crumb.
421 decoder_peer_.HandleHeaderRepresentation("cookie", " fin!");
422
423 // Finish and emit all headers.
424 decoder_.HandleControlFrameHeadersComplete();
425
426 // Resulting decoded headers are in the same order as the inputs.
427 EXPECT_THAT(
428 decoded_block(),
429 ElementsAre(
430 Pair("cookie", " part 1; part 2 ; part3; fin!"),
431 Pair("passed-through", absl::string_view("foo\0baz", 7)),
432 Pair("joined", absl::string_view("joined\0value 1\0value 2", 22)),
433 Pair("empty", ""),
434 Pair("empty-joined", absl::string_view("\0foo\0\0", 6))));
435 }
436
437 // Decoding indexed static table field should work.
TEST_P(HpackDecoderAdapterTest,IndexedHeaderStatic)438 TEST_P(HpackDecoderAdapterTest, IndexedHeaderStatic) {
439 // Reference static table entries #2 and #5.
440 const Http2HeaderBlock& header_set1 = DecodeBlockExpectingSuccess("\x82\x85");
441 Http2HeaderBlock expected_header_set1;
442 expected_header_set1[":method"] = "GET";
443 expected_header_set1[":path"] = "/index.html";
444 EXPECT_EQ(expected_header_set1, header_set1);
445
446 // Reference static table entry #2.
447 const Http2HeaderBlock& header_set2 = DecodeBlockExpectingSuccess("\x82");
448 Http2HeaderBlock expected_header_set2;
449 expected_header_set2[":method"] = "GET";
450 EXPECT_EQ(expected_header_set2, header_set2);
451 }
452
TEST_P(HpackDecoderAdapterTest,IndexedHeaderDynamic)453 TEST_P(HpackDecoderAdapterTest, IndexedHeaderDynamic) {
454 // First header block: add an entry to header table.
455 const Http2HeaderBlock& header_set1 = DecodeBlockExpectingSuccess(
456 "\x40\x03"
457 "foo"
458 "\x03"
459 "bar");
460 Http2HeaderBlock expected_header_set1;
461 expected_header_set1["foo"] = "bar";
462 EXPECT_EQ(expected_header_set1, header_set1);
463
464 // Second header block: add another entry to header table.
465 const Http2HeaderBlock& header_set2 = DecodeBlockExpectingSuccess(
466 "\xbe\x40\x04"
467 "spam"
468 "\x04"
469 "eggs");
470 Http2HeaderBlock expected_header_set2;
471 expected_header_set2["foo"] = "bar";
472 expected_header_set2["spam"] = "eggs";
473 EXPECT_EQ(expected_header_set2, header_set2);
474
475 // Third header block: refer to most recently added entry.
476 const Http2HeaderBlock& header_set3 = DecodeBlockExpectingSuccess("\xbe");
477 Http2HeaderBlock expected_header_set3;
478 expected_header_set3["spam"] = "eggs";
479 EXPECT_EQ(expected_header_set3, header_set3);
480 }
481
482 // Test a too-large indexed header.
TEST_P(HpackDecoderAdapterTest,InvalidIndexedHeader)483 TEST_P(HpackDecoderAdapterTest, InvalidIndexedHeader) {
484 // High-bit set, and a prefix of one more than the number of static entries.
485 EXPECT_FALSE(DecodeHeaderBlock("\xbe"));
486 }
487
TEST_P(HpackDecoderAdapterTest,ContextUpdateMaximumSize)488 TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) {
489 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
490 decoder_peer_.header_table_size_limit());
491 std::string input;
492 {
493 // Maximum-size update with size 126. Succeeds.
494 HpackOutputStream output_stream;
495 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
496 output_stream.AppendUint32(126);
497
498 input = output_stream.TakeString();
499 EXPECT_TRUE(DecodeHeaderBlock(input));
500 EXPECT_EQ(126u, decoder_peer_.header_table_size_limit());
501 }
502 {
503 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds.
504 HpackOutputStream output_stream;
505 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
506 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting);
507
508 input = output_stream.TakeString();
509 EXPECT_TRUE(DecodeHeaderBlock(input));
510 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
511 decoder_peer_.header_table_size_limit());
512 }
513 {
514 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails.
515 HpackOutputStream output_stream;
516 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
517 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1);
518
519 input = output_stream.TakeString();
520 EXPECT_FALSE(DecodeHeaderBlock(input));
521 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
522 decoder_peer_.header_table_size_limit());
523 }
524 }
525
526 // Two HeaderTableSizeUpdates may appear at the beginning of the block
TEST_P(HpackDecoderAdapterTest,TwoTableSizeUpdates)527 TEST_P(HpackDecoderAdapterTest, TwoTableSizeUpdates) {
528 std::string input;
529 {
530 // Should accept two table size updates, update to second one
531 HpackOutputStream output_stream;
532 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
533 output_stream.AppendUint32(0);
534 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
535 output_stream.AppendUint32(122);
536
537 input = output_stream.TakeString();
538 EXPECT_TRUE(DecodeHeaderBlock(input));
539 EXPECT_EQ(122u, decoder_peer_.header_table_size_limit());
540 }
541 }
542
543 // Three HeaderTableSizeUpdates should result in an error
TEST_P(HpackDecoderAdapterTest,ThreeTableSizeUpdatesError)544 TEST_P(HpackDecoderAdapterTest, ThreeTableSizeUpdatesError) {
545 std::string input;
546 {
547 // Should reject three table size updates, update to second one
548 HpackOutputStream output_stream;
549 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
550 output_stream.AppendUint32(5);
551 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
552 output_stream.AppendUint32(10);
553 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
554 output_stream.AppendUint32(15);
555
556 input = output_stream.TakeString();
557
558 EXPECT_FALSE(DecodeHeaderBlock(input));
559 EXPECT_EQ(10u, decoder_peer_.header_table_size_limit());
560 }
561 }
562
563 // HeaderTableSizeUpdates may only appear at the beginning of the block
564 // Any other updates should result in an error
TEST_P(HpackDecoderAdapterTest,TableSizeUpdateSecondError)565 TEST_P(HpackDecoderAdapterTest, TableSizeUpdateSecondError) {
566 std::string input;
567 {
568 // Should reject a table size update appearing after a different entry
569 // The table size should remain as the default
570 HpackOutputStream output_stream;
571 output_stream.AppendBytes("\x82\x85");
572 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
573 output_stream.AppendUint32(123);
574
575 input = output_stream.TakeString();
576
577 EXPECT_FALSE(DecodeHeaderBlock(input));
578 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
579 decoder_peer_.header_table_size_limit());
580 }
581 }
582
583 // HeaderTableSizeUpdates may only appear at the beginning of the block
584 // Any other updates should result in an error
TEST_P(HpackDecoderAdapterTest,TableSizeUpdateFirstThirdError)585 TEST_P(HpackDecoderAdapterTest, TableSizeUpdateFirstThirdError) {
586 std::string input;
587 {
588 // Should reject the second table size update
589 // if a different entry appears after the first update
590 // The table size should update to the first but not the second
591 HpackOutputStream output_stream;
592 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
593 output_stream.AppendUint32(60);
594 output_stream.AppendBytes("\x82\x85");
595 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
596 output_stream.AppendUint32(125);
597
598 input = output_stream.TakeString();
599
600 EXPECT_FALSE(DecodeHeaderBlock(input));
601 EXPECT_EQ(60u, decoder_peer_.header_table_size_limit());
602 }
603 }
604
605 // Decoding two valid encoded literal headers with no indexing should
606 // work.
TEST_P(HpackDecoderAdapterTest,LiteralHeaderNoIndexing)607 TEST_P(HpackDecoderAdapterTest, LiteralHeaderNoIndexing) {
608 // First header with indexed name, second header with string literal
609 // name.
610 const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2";
611 const Http2HeaderBlock& header_set = DecodeBlockExpectingSuccess(
612 absl::string_view(input, ABSL_ARRAYSIZE(input) - 1));
613
614 Http2HeaderBlock expected_header_set;
615 expected_header_set[":path"] = "/sample/path";
616 expected_header_set[":path2"] = "/sample/path/2";
617 EXPECT_EQ(expected_header_set, header_set);
618 }
619
620 // Decoding two valid encoded literal headers with incremental
621 // indexing and string literal names should work.
TEST_P(HpackDecoderAdapterTest,LiteralHeaderIncrementalIndexing)622 TEST_P(HpackDecoderAdapterTest, LiteralHeaderIncrementalIndexing) {
623 const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2";
624 const Http2HeaderBlock& header_set = DecodeBlockExpectingSuccess(
625 absl::string_view(input, ABSL_ARRAYSIZE(input) - 1));
626
627 Http2HeaderBlock expected_header_set;
628 expected_header_set[":path"] = "/sample/path";
629 expected_header_set[":path2"] = "/sample/path/2";
630 EXPECT_EQ(expected_header_set, header_set);
631 }
632
TEST_P(HpackDecoderAdapterTest,LiteralHeaderWithIndexingInvalidNameIndex)633 TEST_P(HpackDecoderAdapterTest, LiteralHeaderWithIndexingInvalidNameIndex) {
634 decoder_.ApplyHeaderTableSizeSetting(0);
635 EXPECT_TRUE(EncodeAndDecodeDynamicTableSizeUpdates(0, 0));
636
637 // Name is the last static index. Works.
638 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view("\x7d\x03ooo")));
639 // Name is one beyond the last static index. Fails.
640 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view("\x7e\x03ooo")));
641 }
642
TEST_P(HpackDecoderAdapterTest,LiteralHeaderNoIndexingInvalidNameIndex)643 TEST_P(HpackDecoderAdapterTest, LiteralHeaderNoIndexingInvalidNameIndex) {
644 // Name is the last static index. Works.
645 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view("\x0f\x2e\x03ooo")));
646 // Name is one beyond the last static index. Fails.
647 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view("\x0f\x2f\x03ooo")));
648 }
649
TEST_P(HpackDecoderAdapterTest,LiteralHeaderNeverIndexedInvalidNameIndex)650 TEST_P(HpackDecoderAdapterTest, LiteralHeaderNeverIndexedInvalidNameIndex) {
651 // Name is the last static index. Works.
652 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view("\x1f\x2e\x03ooo")));
653 // Name is one beyond the last static index. Fails.
654 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view("\x1f\x2f\x03ooo")));
655 }
656
TEST_P(HpackDecoderAdapterTest,TruncatedIndex)657 TEST_P(HpackDecoderAdapterTest, TruncatedIndex) {
658 // Indexed Header, varint for index requires multiple bytes,
659 // but only one provided.
660 EXPECT_FALSE(DecodeHeaderBlock("\xff"));
661 }
662
TEST_P(HpackDecoderAdapterTest,TruncatedHuffmanLiteral)663 TEST_P(HpackDecoderAdapterTest, TruncatedHuffmanLiteral) {
664 // Literal value, Huffman encoded, but with the last byte missing (i.e.
665 // drop the final ff shown below).
666 //
667 // 41 | == Literal indexed ==
668 // | Indexed name (idx = 1)
669 // | :authority
670 // 8c | Literal value (len = 12)
671 // | Huffman encoded:
672 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
673 // | Decoded:
674 // | www.example.com
675 // | -> :authority: www.example.com
676
677 std::string first = absl::HexStringToBytes("418cf1e3c2e5f23a6ba0ab90f4ff");
678 EXPECT_TRUE(DecodeHeaderBlock(first));
679 first.pop_back();
680 EXPECT_FALSE(DecodeHeaderBlock(first));
681 }
682
TEST_P(HpackDecoderAdapterTest,HuffmanEOSError)683 TEST_P(HpackDecoderAdapterTest, HuffmanEOSError) {
684 // Literal value, Huffman encoded, but with an additional ff byte at the end
685 // of the string, i.e. an EOS that is longer than permitted.
686 //
687 // 41 | == Literal indexed ==
688 // | Indexed name (idx = 1)
689 // | :authority
690 // 8d | Literal value (len = 13)
691 // | Huffman encoded:
692 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
693 // | Decoded:
694 // | www.example.com
695 // | -> :authority: www.example.com
696
697 std::string first = absl::HexStringToBytes("418cf1e3c2e5f23a6ba0ab90f4ff");
698 EXPECT_TRUE(DecodeHeaderBlock(first));
699 first = absl::HexStringToBytes("418df1e3c2e5f23a6ba0ab90f4ffff");
700 EXPECT_FALSE(DecodeHeaderBlock(first));
701 }
702
703 // Round-tripping the header set from RFC 7541 C.3.1 should work.
704 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1
TEST_P(HpackDecoderAdapterTest,BasicC31)705 TEST_P(HpackDecoderAdapterTest, BasicC31) {
706 HpackEncoder encoder;
707
708 Http2HeaderBlock expected_header_set;
709 expected_header_set[":method"] = "GET";
710 expected_header_set[":scheme"] = "http";
711 expected_header_set[":path"] = "/";
712 expected_header_set[":authority"] = "www.example.com";
713
714 std::string encoded_header_set =
715 encoder.EncodeHeaderBlock(expected_header_set);
716
717 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set));
718 EXPECT_EQ(expected_header_set, decoded_block());
719 }
720
721 // RFC 7541, Section C.4: Request Examples with Huffman Coding
722 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.4
TEST_P(HpackDecoderAdapterTest,SectionC4RequestHuffmanExamples)723 TEST_P(HpackDecoderAdapterTest, SectionC4RequestHuffmanExamples) {
724 // TODO(jamessynge): Use http2/hpack/tools/hpack_example.h to parse the
725 // example directly, instead of having it as a comment.
726 //
727 // 82 | == Indexed - Add ==
728 // | idx = 2
729 // | -> :method: GET
730 // 86 | == Indexed - Add ==
731 // | idx = 6
732 // | -> :scheme: http
733 // 84 | == Indexed - Add ==
734 // | idx = 4
735 // | -> :path: /
736 // 41 | == Literal indexed ==
737 // | Indexed name (idx = 1)
738 // | :authority
739 // 8c | Literal value (len = 12)
740 // | Huffman encoded:
741 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
742 // | Decoded:
743 // | www.example.com
744 // | -> :authority: www.example.com
745 std::string first =
746 absl::HexStringToBytes("828684418cf1e3c2e5f23a6ba0ab90f4ff");
747 const Http2HeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
748
749 EXPECT_THAT(first_header_set,
750 ElementsAre(
751 // clang-format off
752 Pair(":method", "GET"),
753 Pair(":scheme", "http"),
754 Pair(":path", "/"),
755 Pair(":authority", "www.example.com")));
756 // clang-format on
757
758 expectEntry(62, 57, ":authority", "www.example.com");
759 EXPECT_EQ(57u, decoder_peer_.current_header_table_size());
760
761 // 82 | == Indexed - Add ==
762 // | idx = 2
763 // | -> :method: GET
764 // 86 | == Indexed - Add ==
765 // | idx = 6
766 // | -> :scheme: http
767 // 84 | == Indexed - Add ==
768 // | idx = 4
769 // | -> :path: /
770 // be | == Indexed - Add ==
771 // | idx = 62
772 // | -> :authority: www.example.com
773 // 58 | == Literal indexed ==
774 // | Indexed name (idx = 24)
775 // | cache-control
776 // 86 | Literal value (len = 8)
777 // | Huffman encoded:
778 // a8eb 1064 9cbf | ...d..
779 // | Decoded:
780 // | no-cache
781 // | -> cache-control: no-cache
782
783 std::string second = absl::HexStringToBytes("828684be5886a8eb10649cbf");
784 const Http2HeaderBlock& second_header_set =
785 DecodeBlockExpectingSuccess(second);
786
787 EXPECT_THAT(second_header_set,
788 ElementsAre(
789 // clang-format off
790 Pair(":method", "GET"),
791 Pair(":scheme", "http"),
792 Pair(":path", "/"),
793 Pair(":authority", "www.example.com"),
794 Pair("cache-control", "no-cache")));
795 // clang-format on
796
797 expectEntry(62, 53, "cache-control", "no-cache");
798 expectEntry(63, 57, ":authority", "www.example.com");
799 EXPECT_EQ(110u, decoder_peer_.current_header_table_size());
800
801 // 82 | == Indexed - Add ==
802 // | idx = 2
803 // | -> :method: GET
804 // 87 | == Indexed - Add ==
805 // | idx = 7
806 // | -> :scheme: https
807 // 85 | == Indexed - Add ==
808 // | idx = 5
809 // | -> :path: /index.html
810 // bf | == Indexed - Add ==
811 // | idx = 63
812 // | -> :authority: www.example.com
813 // 40 | == Literal indexed ==
814 // 88 | Literal name (len = 10)
815 // | Huffman encoded:
816 // 25a8 49e9 5ba9 7d7f | %.I.[.}.
817 // | Decoded:
818 // | custom-key
819 // 89 | Literal value (len = 12)
820 // | Huffman encoded:
821 // 25a8 49e9 5bb8 e8b4 bf | %.I.[....
822 // | Decoded:
823 // | custom-value
824 // | -> custom-key: custom-value
825 std::string third = absl::HexStringToBytes(
826 "828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf");
827 const Http2HeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
828
829 EXPECT_THAT(
830 third_header_set,
831 ElementsAre(
832 // clang-format off
833 Pair(":method", "GET"),
834 Pair(":scheme", "https"),
835 Pair(":path", "/index.html"),
836 Pair(":authority", "www.example.com"),
837 Pair("custom-key", "custom-value")));
838 // clang-format on
839
840 expectEntry(62, 54, "custom-key", "custom-value");
841 expectEntry(63, 53, "cache-control", "no-cache");
842 expectEntry(64, 57, ":authority", "www.example.com");
843 EXPECT_EQ(164u, decoder_peer_.current_header_table_size());
844 }
845
846 // RFC 7541, Section C.6: Response Examples with Huffman Coding
847 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.6
TEST_P(HpackDecoderAdapterTest,SectionC6ResponseHuffmanExamples)848 TEST_P(HpackDecoderAdapterTest, SectionC6ResponseHuffmanExamples) {
849 // The example is based on a maximum dynamic table size of 256,
850 // which allows for testing dynamic table evictions.
851 decoder_peer_.set_header_table_size_limit(256);
852
853 // 48 | == Literal indexed ==
854 // | Indexed name (idx = 8)
855 // | :status
856 // 82 | Literal value (len = 3)
857 // | Huffman encoded:
858 // 6402 | d.
859 // | Decoded:
860 // | 302
861 // | -> :status: 302
862 // 58 | == Literal indexed ==
863 // | Indexed name (idx = 24)
864 // | cache-control
865 // 85 | Literal value (len = 7)
866 // | Huffman encoded:
867 // aec3 771a 4b | ..w.K
868 // | Decoded:
869 // | private
870 // | -> cache-control: private
871 // 61 | == Literal indexed ==
872 // | Indexed name (idx = 33)
873 // | date
874 // 96 | Literal value (len = 29)
875 // | Huffman encoded:
876 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
877 // e082 a62d 1bff | ...-..
878 // | Decoded:
879 // | Mon, 21 Oct 2013 20:13:21
880 // | GMT
881 // | -> date: Mon, 21 Oct 2013
882 // | 20:13:21 GMT
883 // 6e | == Literal indexed ==
884 // | Indexed name (idx = 46)
885 // | location
886 // 91 | Literal value (len = 23)
887 // | Huffman encoded:
888 // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
889 // d3 | .
890 // | Decoded:
891 // | https://www.example.com
892 // | -> location: https://www.e
893 // | xample.com
894
895 std::string first = absl::HexStringToBytes(
896 "488264025885aec3771a4b6196d07abe"
897 "941054d444a8200595040b8166e082a6"
898 "2d1bff6e919d29ad171863c78f0b97c8"
899 "e9ae82ae43d3");
900 const Http2HeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
901
902 EXPECT_THAT(first_header_set,
903 ElementsAre(
904 // clang-format off
905 Pair(":status", "302"),
906 Pair("cache-control", "private"),
907 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
908 Pair("location", "https://www.example.com")));
909 // clang-format on
910
911 expectEntry(62, 63, "location", "https://www.example.com");
912 expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
913 expectEntry(64, 52, "cache-control", "private");
914 expectEntry(65, 42, ":status", "302");
915 EXPECT_EQ(222u, decoder_peer_.current_header_table_size());
916
917 // 48 | == Literal indexed ==
918 // | Indexed name (idx = 8)
919 // | :status
920 // 83 | Literal value (len = 3)
921 // | Huffman encoded:
922 // 640e ff | d..
923 // | Decoded:
924 // | 307
925 // | - evict: :status: 302
926 // | -> :status: 307
927 // c1 | == Indexed - Add ==
928 // | idx = 65
929 // | -> cache-control: private
930 // c0 | == Indexed - Add ==
931 // | idx = 64
932 // | -> date: Mon, 21 Oct 2013
933 // | 20:13:21 GMT
934 // bf | == Indexed - Add ==
935 // | idx = 63
936 // | -> location:
937 // | https://www.example.com
938 std::string second = absl::HexStringToBytes("4883640effc1c0bf");
939 const Http2HeaderBlock& second_header_set =
940 DecodeBlockExpectingSuccess(second);
941
942 EXPECT_THAT(second_header_set,
943 ElementsAre(
944 // clang-format off
945 Pair(":status", "307"),
946 Pair("cache-control", "private"),
947 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
948 Pair("location", "https://www.example.com")));
949 // clang-format on
950
951 expectEntry(62, 42, ":status", "307");
952 expectEntry(63, 63, "location", "https://www.example.com");
953 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
954 expectEntry(65, 52, "cache-control", "private");
955 EXPECT_EQ(222u, decoder_peer_.current_header_table_size());
956
957 // 88 | == Indexed - Add ==
958 // | idx = 8
959 // | -> :status: 200
960 // c1 | == Indexed - Add ==
961 // | idx = 65
962 // | -> cache-control: private
963 // 61 | == Literal indexed ==
964 // | Indexed name (idx = 33)
965 // | date
966 // 96 | Literal value (len = 22)
967 // | Huffman encoded:
968 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
969 // e084 a62d 1bff | ...-..
970 // | Decoded:
971 // | Mon, 21 Oct 2013 20:13:22
972 // | GMT
973 // | - evict: cache-control:
974 // | private
975 // | -> date: Mon, 21 Oct 2013
976 // | 20:13:22 GMT
977 // c0 | == Indexed - Add ==
978 // | idx = 64
979 // | -> location:
980 // | https://www.example.com
981 // 5a | == Literal indexed ==
982 // | Indexed name (idx = 26)
983 // | content-encoding
984 // 83 | Literal value (len = 3)
985 // | Huffman encoded:
986 // 9bd9 ab | ...
987 // | Decoded:
988 // | gzip
989 // | - evict: date: Mon, 21 Oct
990 // | 2013 20:13:21 GMT
991 // | -> content-encoding: gzip
992 // 77 | == Literal indexed ==
993 // | Indexed name (idx = 55)
994 // | set-cookie
995 // ad | Literal value (len = 45)
996 // | Huffman encoded:
997 // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9`
998 // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)...
999 // 3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P.
1000 // | Decoded:
1001 // | foo=ASDJKHQKBZXOQWEOPIUAXQ
1002 // | WEOIU; max-age=3600; versi
1003 // | on=1
1004 // | - evict: location:
1005 // | https://www.example.com
1006 // | - evict: :status: 307
1007 // | -> set-cookie: foo=ASDJKHQ
1008 // | KBZXOQWEOPIUAXQWEOIU;
1009 // | max-age=3600; version=1
1010 std::string third = absl::HexStringToBytes(
1011 "88c16196d07abe941054d444a8200595"
1012 "040b8166e084a62d1bffc05a839bd9ab"
1013 "77ad94e7821dd7f2e6c7b335dfdfcd5b"
1014 "3960d5af27087f3672c1ab270fb5291f"
1015 "9587316065c003ed4ee5b1063d5007");
1016 const Http2HeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
1017
1018 EXPECT_THAT(third_header_set,
1019 ElementsAre(
1020 // clang-format off
1021 Pair(":status", "200"),
1022 Pair("cache-control", "private"),
1023 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
1024 Pair("location", "https://www.example.com"),
1025 Pair("content-encoding", "gzip"),
1026 Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
1027 " max-age=3600; version=1")));
1028 // clang-format on
1029
1030 expectEntry(62, 98, "set-cookie",
1031 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
1032 " max-age=3600; version=1");
1033 expectEntry(63, 52, "content-encoding", "gzip");
1034 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT");
1035 EXPECT_EQ(215u, decoder_peer_.current_header_table_size());
1036 }
1037
1038 // Regression test: Found that entries with dynamic indexed names and literal
1039 // values caused "use after free" MSAN failures if the name was evicted as it
1040 // was being re-used.
TEST_P(HpackDecoderAdapterTest,ReuseNameOfEvictedEntry)1041 TEST_P(HpackDecoderAdapterTest, ReuseNameOfEvictedEntry) {
1042 // Each entry is measured as 32 bytes plus the sum of the lengths of the name
1043 // and the value. Set the size big enough for at most one entry, and a fairly
1044 // small one at that (31 ASCII characters).
1045 decoder_.ApplyHeaderTableSizeSetting(63);
1046
1047 HpackBlockBuilder hbb;
1048 hbb.AppendDynamicTableSizeUpdate(0);
1049 hbb.AppendDynamicTableSizeUpdate(63);
1050
1051 const absl::string_view name("some-name");
1052 const absl::string_view value1("some-value");
1053 const absl::string_view value2("another-value");
1054 const absl::string_view value3("yet-another-value");
1055
1056 // Add an entry that will become the first in the dynamic table, entry 62.
1057 hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
1058 name, false, value1);
1059
1060 // Confirm that entry has been added by re-using it.
1061 hbb.AppendIndexedHeader(62);
1062
1063 // Add another entry referring to the name of the first. This will evict the
1064 // first.
1065 hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 62,
1066 false, value2);
1067
1068 // Confirm that entry has been added by re-using it.
1069 hbb.AppendIndexedHeader(62);
1070
1071 // Add another entry referring to the name of the second. This will evict the
1072 // second.
1073 hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 62,
1074 false, value3);
1075
1076 // Confirm that entry has been added by re-using it.
1077 hbb.AppendIndexedHeader(62);
1078
1079 // Can't have DecodeHeaderBlock do the default check for size of the decoded
1080 // data because Http2HeaderBlock will join multiple headers with the same
1081 // name into a single entry, thus we won't see repeated occurrences of the
1082 // name, instead seeing separators between values.
1083 EXPECT_TRUE(DecodeHeaderBlock(hbb.buffer(), kNoCheckDecodedSize));
1084
1085 Http2HeaderBlock expected_header_set;
1086 expected_header_set.AppendValueOrAddHeader(name, value1);
1087 expected_header_set.AppendValueOrAddHeader(name, value1);
1088 expected_header_set.AppendValueOrAddHeader(name, value2);
1089 expected_header_set.AppendValueOrAddHeader(name, value2);
1090 expected_header_set.AppendValueOrAddHeader(name, value3);
1091 expected_header_set.AppendValueOrAddHeader(name, value3);
1092
1093 // Http2HeaderBlock stores these 6 strings as '\0' separated values.
1094 // Make sure that is what happened.
1095 std::string joined_values = expected_header_set[name].as_string();
1096 EXPECT_EQ(joined_values.size(),
1097 2 * value1.size() + 2 * value2.size() + 2 * value3.size() + 5);
1098
1099 EXPECT_EQ(expected_header_set, decoded_block());
1100
1101 if (start_choice_ == START_WITH_HANDLER) {
1102 EXPECT_EQ(handler_.uncompressed_header_bytes(),
1103 6 * name.size() + 2 * value1.size() + 2 * value2.size() +
1104 2 * value3.size());
1105 }
1106 }
1107
1108 // Regression test for https://crbug.com/747395.
TEST_P(HpackDecoderAdapterTest,Cookies)1109 TEST_P(HpackDecoderAdapterTest, Cookies) {
1110 Http2HeaderBlock expected_header_set;
1111 expected_header_set["cookie"] = "foo; bar";
1112
1113 EXPECT_TRUE(DecodeHeaderBlock(absl::HexStringToBytes("608294e76003626172")));
1114 EXPECT_EQ(expected_header_set, decoded_block());
1115 }
1116
1117 } // namespace
1118 } // namespace test
1119 } // namespace spdy
1120