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