1 // Copyright 2014 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 "net/spdy/hpack_decoder.h" 6 7 #include <map> 8 #include <string> 9 10 #include "base/basictypes.h" 11 #include "base/logging.h" 12 #include "base/strings/string_piece.h" 13 #include "net/spdy/hpack_encoder.h" 14 #include "net/spdy/hpack_input_stream.h" 15 #include "net/spdy/hpack_output_stream.h" 16 #include "net/spdy/spdy_test_utils.h" 17 #include "testing/gmock/include/gmock/gmock.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace net { 21 22 namespace test { 23 24 using base::StringPiece; 25 using std::string; 26 27 class HpackDecoderPeer { 28 public: 29 explicit HpackDecoderPeer(HpackDecoder* decoder) 30 : decoder_(decoder) {} 31 32 void HandleHeaderRepresentation(StringPiece name, StringPiece value) { 33 decoder_->HandleHeaderRepresentation(name, value); 34 } 35 bool DecodeNextName(HpackInputStream* in, StringPiece* out) { 36 return decoder_->DecodeNextName(in, out); 37 } 38 HpackHeaderTable* header_table() { 39 return &decoder_->header_table_; 40 } 41 void set_cookie_value(string value) { 42 decoder_->cookie_value_ = value; 43 } 44 string cookie_value() { 45 return decoder_->cookie_value_; 46 } 47 const std::map<string, string>& decoded_block() const { 48 return decoder_->decoded_block_; 49 } 50 const string& headers_block_buffer() const { 51 return decoder_->headers_block_buffer_; 52 } 53 54 private: 55 HpackDecoder* decoder_; 56 }; 57 58 } // namespace test 59 60 namespace { 61 62 using base::StringPiece; 63 using std::string; 64 using test::a2b_hex; 65 66 using testing::ElementsAre; 67 using testing::Pair; 68 69 const size_t kLiteralBound = 1024; 70 71 class HpackDecoderTest : public ::testing::Test { 72 protected: 73 HpackDecoderTest() 74 : decoder_(ObtainHpackHuffmanTable()), 75 decoder_peer_(&decoder_) {} 76 77 bool DecodeHeaderBlock(StringPiece str) { 78 return decoder_.HandleControlFrameHeadersData(0, str.data(), str.size()) && 79 decoder_.HandleControlFrameHeadersComplete(0); 80 } 81 82 const std::map<string, string>& decoded_block() const { 83 // TODO(jgraettinger): HpackDecoderTest should implement 84 // SpdyHeadersHandlerInterface, and collect headers for examination. 85 return decoder_peer_.decoded_block(); 86 } 87 88 const std::map<string, string>& DecodeBlockExpectingSuccess(StringPiece str) { 89 EXPECT_TRUE(DecodeHeaderBlock(str)); 90 return decoded_block(); 91 } 92 93 void expectEntry(size_t index, size_t size, const string& name, 94 const string& value) { 95 HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index); 96 EXPECT_EQ(name, entry->name()) << "index " << index; 97 EXPECT_EQ(value, entry->value()); 98 EXPECT_EQ(size, entry->Size()); 99 EXPECT_EQ(index, decoder_peer_.header_table()->IndexOf(entry)); 100 } 101 102 void expectStaticEntry(size_t index) { 103 HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index); 104 EXPECT_TRUE(entry->IsStatic()) << "index " << index; 105 } 106 107 HpackDecoder decoder_; 108 test::HpackDecoderPeer decoder_peer_; 109 }; 110 111 TEST_F(HpackDecoderTest, HandleControlFrameHeadersData) { 112 // Strings under threshold are concatenated in the buffer. 113 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData( 114 0, "small string one", 16)); 115 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData( 116 0, "small string two", 16)); 117 // A string which would push the buffer over the threshold is refused. 118 EXPECT_FALSE(decoder_.HandleControlFrameHeadersData( 119 0, "fails", kMaxDecodeBufferSize - 32 + 1)); 120 121 EXPECT_EQ(decoder_peer_.headers_block_buffer(), 122 "small string onesmall string two"); 123 } 124 125 TEST_F(HpackDecoderTest, HandleControlFrameHeadersComplete) { 126 // Decode a block which toggles two static headers into the reference set. 127 EXPECT_TRUE(DecodeHeaderBlock("\x82\x86")); 128 129 decoder_peer_.set_cookie_value("foobar=baz"); 130 131 // Headers in the reference set should be emitted. 132 // Incremental cookie buffer should be emitted and cleared. 133 decoder_.HandleControlFrameHeadersData(0, NULL, 0); 134 decoder_.HandleControlFrameHeadersComplete(0); 135 136 EXPECT_THAT(decoded_block(), ElementsAre( 137 Pair(":method", "GET"), 138 Pair(":path", "/index.html"), 139 Pair("cookie", "foobar=baz"))); 140 EXPECT_EQ(decoder_peer_.cookie_value(), ""); 141 } 142 143 TEST_F(HpackDecoderTest, HandleHeaderRepresentation) { 144 // All cookie crumbs are joined. 145 decoder_peer_.HandleHeaderRepresentation("cookie", " part 1"); 146 decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 "); 147 decoder_peer_.HandleHeaderRepresentation("cookie", "part3"); 148 149 // Already-delimited headers are passed through. 150 decoder_peer_.HandleHeaderRepresentation("passed-through", 151 string("foo\0baz", 7)); 152 153 // Other headers are joined on \0. Case matters. 154 decoder_peer_.HandleHeaderRepresentation("joined", "not joined"); 155 decoder_peer_.HandleHeaderRepresentation("joineD", "value 1"); 156 decoder_peer_.HandleHeaderRepresentation("joineD", "value 2"); 157 158 // Empty headers remain empty. 159 decoder_peer_.HandleHeaderRepresentation("empty", ""); 160 161 // Joined empty headers work as expected. 162 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); 163 decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo"); 164 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); 165 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); 166 167 // Non-contiguous cookie crumb. 168 decoder_peer_.HandleHeaderRepresentation("cookie", " fin!"); 169 170 // Finish and emit all headers. 171 decoder_.HandleControlFrameHeadersComplete(0); 172 173 EXPECT_THAT(decoded_block(), ElementsAre( 174 Pair("cookie", " part 1; part 2 ; part3; fin!"), 175 Pair("empty", ""), 176 Pair("empty-joined", string("\0foo\0\0", 6)), 177 Pair("joineD", string("value 1\0value 2", 15)), 178 Pair("joined", "not joined"), 179 Pair("passed-through", string("foo\0baz", 7)))); 180 } 181 182 // Decoding an encoded name with a valid string literal should work. 183 TEST_F(HpackDecoderTest, DecodeNextNameLiteral) { 184 HpackInputStream input_stream(kLiteralBound, StringPiece("\x00\x04name", 6)); 185 186 StringPiece string_piece; 187 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 188 EXPECT_EQ("name", string_piece); 189 EXPECT_FALSE(input_stream.HasMoreData()); 190 } 191 192 TEST_F(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) { 193 string input = a2b_hex("0088571c5cdb737b2faf"); 194 HpackInputStream input_stream(kLiteralBound, input); 195 196 StringPiece string_piece; 197 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 198 EXPECT_EQ("custom-key", string_piece); 199 EXPECT_FALSE(input_stream.HasMoreData()); 200 } 201 202 // Decoding an encoded name with a valid index should work. 203 TEST_F(HpackDecoderTest, DecodeNextNameIndexed) { 204 HpackInputStream input_stream(kLiteralBound, "\x01"); 205 206 StringPiece string_piece; 207 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 208 EXPECT_EQ(":authority", string_piece); 209 EXPECT_FALSE(input_stream.HasMoreData()); 210 } 211 212 // Decoding an encoded name with an invalid index should fail. 213 TEST_F(HpackDecoderTest, DecodeNextNameInvalidIndex) { 214 // One more than the number of static table entries. 215 HpackInputStream input_stream(kLiteralBound, "\x3e"); 216 217 StringPiece string_piece; 218 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 219 } 220 221 // Decoding an indexed header should toggle the index's presence in 222 // the reference set, making a copy of static table entries if 223 // necessary. It should also emit the header if toggled on (and only 224 // as many times as it was toggled on). 225 TEST_F(HpackDecoderTest, IndexedHeaderBasic) { 226 // Toggle on static table entry #2 (and make a copy at index #1), 227 // then toggle on static table entry #5 (which is now #6 because of 228 // the copy of #2). 229 std::map<string, string> header_set1 = 230 DecodeBlockExpectingSuccess("\x82\x86"); 231 std::map<string, string> expected_header_set1; 232 expected_header_set1[":method"] = "GET"; 233 expected_header_set1[":path"] = "/index.html"; 234 EXPECT_EQ(expected_header_set1, header_set1); 235 236 std::map<string, string> expected_header_set2; 237 expected_header_set2[":path"] = "/index.html"; 238 // Toggle off the copy of static table entry #5. 239 std::map<string, string> header_set2 = 240 DecodeBlockExpectingSuccess("\x82"); 241 EXPECT_EQ(expected_header_set2, header_set2); 242 } 243 244 // Test a too-large indexed header. 245 TEST_F(HpackDecoderTest, InvalidIndexedHeader) { 246 // High-bit set, and a prefix of one more than the number of static entries. 247 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\xbe", 1))); 248 } 249 250 TEST_F(HpackDecoderTest, ContextUpdateMaximumSize) { 251 EXPECT_EQ(kDefaultHeaderTableSizeSetting, 252 decoder_peer_.header_table()->max_size()); 253 string input; 254 { 255 // Maximum-size update with size 126. Succeeds. 256 HpackOutputStream output_stream; 257 output_stream.AppendPrefix(kEncodingContextOpcode); 258 output_stream.AppendPrefix(kEncodingContextNewMaximumSize); 259 output_stream.AppendUint32(126); 260 261 output_stream.TakeString(&input); 262 EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input))); 263 EXPECT_EQ(126u, decoder_peer_.header_table()->max_size()); 264 } 265 { 266 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds. 267 HpackOutputStream output_stream; 268 output_stream.AppendPrefix(kEncodingContextOpcode); 269 output_stream.AppendPrefix(kEncodingContextNewMaximumSize); 270 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting); 271 272 output_stream.TakeString(&input); 273 EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input))); 274 EXPECT_EQ(kDefaultHeaderTableSizeSetting, 275 decoder_peer_.header_table()->max_size()); 276 } 277 { 278 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails. 279 HpackOutputStream output_stream; 280 output_stream.AppendPrefix(kEncodingContextOpcode); 281 output_stream.AppendPrefix(kEncodingContextNewMaximumSize); 282 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1); 283 284 output_stream.TakeString(&input); 285 EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input))); 286 EXPECT_EQ(kDefaultHeaderTableSizeSetting, 287 decoder_peer_.header_table()->max_size()); 288 } 289 } 290 291 TEST_F(HpackDecoderTest, ContextUpdateClearReferenceSet) { 292 // Toggle on a couple of headers. 293 std::map<string, string> header_set1 = 294 DecodeBlockExpectingSuccess("\x82\x86"); 295 std::map<string, string> expected_header_set1; 296 expected_header_set1[":method"] = "GET"; 297 expected_header_set1[":path"] = "/index.html"; 298 EXPECT_EQ(expected_header_set1, header_set1); 299 300 // Send a context update to clear the reference set. 301 std::map<string, string> header_set2 = 302 DecodeBlockExpectingSuccess("\x30"); 303 std::map<string, string> expected_header_set2; 304 EXPECT_EQ(expected_header_set2, header_set2); 305 } 306 307 // Decoding two valid encoded literal headers with no indexing should 308 // work. 309 TEST_F(HpackDecoderTest, LiteralHeaderNoIndexing) { 310 // First header with indexed name, second header with string literal 311 // name. 312 const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2"; 313 std::map<string, string> header_set = 314 DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1)); 315 316 std::map<string, string> expected_header_set; 317 expected_header_set[":path"] = "/sample/path"; 318 expected_header_set[":path2"] = "/sample/path/2"; 319 EXPECT_EQ(expected_header_set, header_set); 320 } 321 322 // Decoding two valid encoded literal headers with incremental 323 // indexing and string literal names should work and add the headers 324 // to the reference set. 325 TEST_F(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { 326 const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2"; 327 std::map<string, string> header_set = 328 DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1)); 329 330 std::map<string, string> expected_header_set; 331 expected_header_set[":path"] = "/sample/path"; 332 expected_header_set[":path2"] = "/sample/path/2"; 333 EXPECT_EQ(expected_header_set, header_set); 334 335 // Decoding an empty string should just return the reference set. 336 std::map<string, string> header_set2 = DecodeBlockExpectingSuccess(""); 337 EXPECT_EQ(expected_header_set, header_set2); 338 } 339 340 TEST_F(HpackDecoderTest, LiteralHeaderWithIndexingInvalidNameIndex) { 341 decoder_.ApplyHeaderTableSizeSetting(0); 342 343 // Name is the last static index. Works. 344 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x7d\x03ooo"))); 345 // Name is one beyond the last static index. Fails. 346 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x7e\x03ooo"))); 347 } 348 349 TEST_F(HpackDecoderTest, LiteralHeaderNoIndexingInvalidNameIndex) { 350 // Name is the last static index. Works. 351 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x0f\x2e\x03ooo"))); 352 // Name is one beyond the last static index. Fails. 353 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x0f\x2f\x03ooo"))); 354 } 355 356 TEST_F(HpackDecoderTest, LiteralHeaderNeverIndexedInvalidNameIndex) { 357 // Name is the last static index. Works. 358 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x1f\x2e\x03ooo"))); 359 // Name is one beyond the last static index. Fails. 360 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x1f\x2f\x03ooo"))); 361 } 362 363 // Round-tripping the header set from E.2.1 should work. 364 TEST_F(HpackDecoderTest, BasicE21) { 365 HpackEncoder encoder(ObtainHpackHuffmanTable()); 366 367 std::map<string, string> expected_header_set; 368 expected_header_set[":method"] = "GET"; 369 expected_header_set[":scheme"] = "http"; 370 expected_header_set[":path"] = "/"; 371 expected_header_set[":authority"] = "www.example.com"; 372 373 string encoded_header_set; 374 EXPECT_TRUE(encoder.EncodeHeaderSet( 375 expected_header_set, &encoded_header_set)); 376 377 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set)); 378 EXPECT_EQ(expected_header_set, decoded_block()); 379 } 380 381 TEST_F(HpackDecoderTest, SectionD3RequestHuffmanExamples) { 382 std::map<string, string> header_set; 383 384 // 82 | == Indexed - Add == 385 // | idx = 2 386 // | -> :method: GET 387 // 87 | == Indexed - Add == 388 // | idx = 7 389 // | -> :scheme: http 390 // 86 | == Indexed - Add == 391 // | idx = 6 392 // | -> :path: / 393 // 44 | == Literal indexed == 394 // | Indexed name (idx = 4) 395 // | :authority 396 // 8c | Literal value (len = 15) 397 // | Huffman encoded: 398 // e7cf 9beb e89b 6fb1 6fa9 b6ff | ......o.o... 399 // | Decoded: 400 // | www.example.com 401 // | -> :authority: www.example.com 402 string first = a2b_hex("828786448ce7cf9bebe89b6fb16fa9b6ff"); 403 header_set = DecodeBlockExpectingSuccess(first); 404 405 EXPECT_THAT(header_set, ElementsAre( 406 Pair(":authority", "www.example.com"), 407 Pair(":method", "GET"), 408 Pair(":path", "/"), 409 Pair(":scheme", "http"))); 410 411 expectEntry(1, 57, ":authority", "www.example.com"); 412 expectEntry(2, 38, ":path", "/"); 413 expectEntry(3, 43, ":scheme", "http"); 414 expectEntry(4, 42, ":method", "GET"); 415 expectStaticEntry(5); 416 EXPECT_EQ(180u, decoder_peer_.header_table()->size()); 417 418 // 5c | == Literal indexed == 419 // | Indexed name (idx = 28) 420 // | cache-control 421 // 86 | Literal value (len = 8) 422 // | Huffman encoded: 423 // b9b9 9495 56bf | ....V. 424 // | Decoded: 425 // | no-cache 426 // | -> cache-control: no-cache 427 string second = a2b_hex("5c86b9b9949556bf"); 428 header_set = DecodeBlockExpectingSuccess(second); 429 430 EXPECT_THAT(header_set, ElementsAre( 431 Pair(":authority", "www.example.com"), 432 Pair(":method", "GET"), 433 Pair(":path", "/"), 434 Pair(":scheme", "http"), 435 Pair("cache-control", "no-cache"))); 436 437 expectEntry(1, 53, "cache-control", "no-cache"); 438 expectEntry(2, 57, ":authority", "www.example.com"); 439 expectEntry(3, 38, ":path", "/"); 440 expectEntry(4, 43, ":scheme", "http"); 441 expectEntry(5, 42, ":method", "GET"); 442 expectStaticEntry(6); 443 EXPECT_EQ(233u, decoder_peer_.header_table()->size()); 444 445 // 30 | == Empty reference set == 446 // | idx = 0 447 // | flag = 1 448 // 85 | == Indexed - Add == 449 // | idx = 5 450 // | -> :method: GET 451 // 8c | == Indexed - Add == 452 // | idx = 12 453 // | -> :scheme: https 454 // 8b | == Indexed - Add == 455 // | idx = 11 456 // | -> :path: /index.html 457 // 84 | == Indexed - Add == 458 // | idx = 4 459 // | -> :authority: www.example.com 460 // 40 | == Literal indexed == 461 // 88 | Literal name (len = 10) 462 // | Huffman encoded: 463 // 571c 5cdb 737b 2faf | W.\.s{/. 464 // | Decoded: 465 // | custom-key 466 // 89 | Literal value (len = 12) 467 // | Huffman encoded: 468 // 571c 5cdb 7372 4d9c 57 | W.\.srM.W 469 // | Decoded: 470 // | custom-value 471 // | -> custom-key: custom-value 472 string third = a2b_hex("30858c8b844088571c5cdb737b2faf89" 473 "571c5cdb73724d9c57"); 474 header_set = DecodeBlockExpectingSuccess(third); 475 476 EXPECT_THAT(header_set, ElementsAre( 477 Pair(":authority", "www.example.com"), 478 Pair(":method", "GET"), 479 Pair(":path", "/index.html"), 480 Pair(":scheme", "https"), 481 Pair("custom-key", "custom-value"))); 482 483 expectEntry(1, 54, "custom-key", "custom-value"); 484 expectEntry(2, 48, ":path", "/index.html"); 485 expectEntry(3, 44, ":scheme", "https"); 486 expectEntry(4, 53, "cache-control", "no-cache"); 487 expectEntry(5, 57, ":authority", "www.example.com"); 488 expectEntry(6, 38, ":path", "/"); 489 expectEntry(7, 43, ":scheme", "http"); 490 expectEntry(8, 42, ":method", "GET"); 491 expectStaticEntry(9); 492 EXPECT_EQ(379u, decoder_peer_.header_table()->size()); 493 } 494 495 TEST_F(HpackDecoderTest, SectionD5ResponseHuffmanExamples) { 496 std::map<string, string> header_set; 497 decoder_.ApplyHeaderTableSizeSetting(256); 498 499 // 48 | == Literal indexed == 500 // | Indexed name (idx = 8) 501 // | :status 502 // 82 | Literal value (len = 3) 503 // | Huffman encoded: 504 // 4017 | @. 505 // | Decoded: 506 // | 302 507 // | -> :status: 302 508 // 59 | == Literal indexed == 509 // | Indexed name (idx = 25) 510 // | cache-control 511 // 85 | Literal value (len = 7) 512 // | Huffman encoded: 513 // bf06 724b 97 | ..rK. 514 // | Decoded: 515 // | private 516 // | -> cache-control: private 517 // 63 | == Literal indexed == 518 // | Indexed name (idx = 35) 519 // | date 520 // 93 | Literal value (len = 29) 521 // | Huffman encoded: 522 // d6db b298 84de 2a71 8805 0620 9851 3109 | ......*q... .Q1. 523 // b56b a3 | .k. 524 // | Decoded: 525 // | Mon, 21 Oct 2013 20:13:21 526 // | GMT 527 // | -> date: Mon, 21 Oct 2013 528 // | 20:13:21 GMT 529 // 71 | == Literal indexed == 530 // | Indexed name (idx = 49) 531 // | location 532 // 91 | Literal value (len = 23) 533 // | Huffman encoded: 534 // adce bf19 8e7e 7cf9 bebe 89b6 fb16 fa9b | ......|......... 535 // 6f | o 536 // | Decoded: 537 // | https://www.example.com 538 // | -> location: https://www.e 539 // | xample.com 540 string first = a2b_hex("488240175985bf06724b976393d6dbb2" 541 "9884de2a718805062098513109b56ba3" 542 "7191adcebf198e7e7cf9bebe89b6fb16" 543 "fa9b6f"); 544 header_set = DecodeBlockExpectingSuccess(first); 545 546 EXPECT_THAT(header_set, ElementsAre( 547 Pair(":status", "302"), 548 Pair("cache-control", "private"), 549 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 550 Pair("location", "https://www.example.com"))); 551 552 expectEntry(1, 63, "location", "https://www.example.com"); 553 expectEntry(2, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); 554 expectEntry(3, 52, "cache-control", "private"); 555 expectEntry(4, 42, ":status", "302"); 556 expectStaticEntry(5); 557 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); 558 559 // 8c | == Indexed - Add == 560 // | idx = 12 561 // | - evict: :status: 302 562 // | -> :status: 200 563 string second = a2b_hex("8c"); 564 header_set = DecodeBlockExpectingSuccess(second); 565 566 EXPECT_THAT(header_set, ElementsAre( 567 Pair(":status", "200"), 568 Pair("cache-control", "private"), 569 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 570 Pair("location", "https://www.example.com"))); 571 572 expectEntry(1, 42, ":status", "200"); 573 expectEntry(2, 63, "location", "https://www.example.com"); 574 expectEntry(3, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); 575 expectEntry(4, 52, "cache-control", "private"); 576 expectStaticEntry(5); 577 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); 578 579 // 84 | == Indexed - Remove == 580 // | idx = 4 581 // | -> cache-control: private 582 // 84 | == Indexed - Add == 583 // | idx = 4 584 // | -> cache-control: private 585 // 43 | == Literal indexed == 586 // | Indexed name (idx = 3) 587 // | date 588 // 93 | Literal value (len = 29) 589 // | Huffman encoded: 590 // d6db b298 84de 2a71 8805 0620 9851 3111 | ......*q... .Q1. 591 // b56b a3 | .k. 592 // | Decoded: 593 // | Mon, 21 Oct 2013 20:13:22 594 // | GMT 595 // | - evict: cache-control: pr 596 // | ivate 597 // | -> date: Mon, 21 Oct 2013 598 // | 20:13:22 GMT 599 // 5e | == Literal indexed == 600 // | Indexed name (idx = 30) 601 // | content-encoding 602 // 84 | Literal value (len = 4) 603 // | Huffman encoded: 604 // abdd 97ff | .... 605 // | Decoded: 606 // | gzip 607 // | - evict: date: Mon, 21 Oct 608 // | 2013 20:13:21 GMT 609 // | -> content-encoding: gzip 610 // 84 | == Indexed - Remove == 611 // | idx = 4 612 // | -> location: https://www.e 613 // | xample.com 614 // 84 | == Indexed - Add == 615 // | idx = 4 616 // | -> location: https://www.e 617 // | xample.com 618 // 83 | == Indexed - Remove == 619 // | idx = 3 620 // | -> :status: 200 621 // 83 | == Indexed - Add == 622 // | idx = 3 623 // | -> :status: 200 624 // 7b | == Literal indexed == 625 // | Indexed name (idx = 59) 626 // | set-cookie 627 // b1 | Literal value (len = 56) 628 // | Huffman encoded: 629 // e0d6 cf9f 6e8f 9fd3 e5f6 fa76 fefd 3c7e | ....n......v.... 630 // df9e ff1f 2f0f 3cfe 9f6f cf7f 8f87 9f61 | ..../....o.....a 631 // ad4f 4cc9 a973 a220 0ec3 725e 18b1 b74e | .OL..s. ..r^...N 632 // 3f | ? 633 // | Decoded: 634 // | foo=ASDJKHQKBZXOQWEOPIUAXQ 635 // | WEOIU; max-age=3600; versi 636 // | on=1 637 // | - evict: location: https:/ 638 // | /www.example.com 639 // | - evict: :status: 200 640 // | -> set-cookie: foo=ASDJKHQ 641 // | KBZXOQWEOPIUAXQWEOIU; ma 642 // | x-age=3600; version=1 643 string third = a2b_hex("84844393d6dbb29884de2a7188050620" 644 "98513111b56ba35e84abdd97ff848483" 645 "837bb1e0d6cf9f6e8f9fd3e5f6fa76fe" 646 "fd3c7edf9eff1f2f0f3cfe9f6fcf7f8f" 647 "879f61ad4f4cc9a973a2200ec3725e18" 648 "b1b74e3f"); 649 header_set = DecodeBlockExpectingSuccess(third); 650 651 EXPECT_THAT(header_set, ElementsAre( 652 Pair(":status", "200"), 653 Pair("cache-control", "private"), 654 Pair("content-encoding", "gzip"), 655 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), 656 Pair("location", "https://www.example.com"), 657 Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" 658 " max-age=3600; version=1"))); 659 660 expectEntry(1, 98, "set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" 661 " max-age=3600; version=1"); 662 expectEntry(2, 52, "content-encoding", "gzip"); 663 expectEntry(3, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT"); 664 expectStaticEntry(4); 665 EXPECT_EQ(215u, decoder_peer_.header_table()->size()); 666 } 667 668 } // namespace 669 670 } // namespace net 671