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