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.
5 #include "net/spdy/hpack/hpack_decoder.h"
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/strings/string_piece.h"
13 #include "net/spdy/hpack/hpack_encoder.h"
14 #include "net/spdy/hpack/hpack_input_stream.h"
15 #include "net/spdy/hpack/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"
24 using base::StringPiece
;
27 class HpackDecoderPeer
{
29 explicit HpackDecoderPeer(HpackDecoder
* decoder
) : decoder_(decoder
) {}
31 void HandleHeaderRepresentation(StringPiece name
, StringPiece value
) {
32 decoder_
->HandleHeaderRepresentation(name
, value
);
34 bool DecodeNextName(HpackInputStream
* in
, StringPiece
* out
) {
35 return decoder_
->DecodeNextName(in
, out
);
37 HpackHeaderTable
* header_table() { return &decoder_
->header_table_
; }
38 void set_cookie_value(string value
) { decoder_
->cookie_value_
= value
; }
39 string
cookie_value() { return decoder_
->cookie_value_
; }
40 const SpdyHeaderBlock
& decoded_block() const {
41 return decoder_
->decoded_block_
;
43 const string
& headers_block_buffer() const {
44 return decoder_
->headers_block_buffer_
;
48 HpackDecoder
* decoder_
;
55 using base::StringPiece
;
59 using testing::ElementsAre
;
62 const size_t kLiteralBound
= 1024;
64 class HpackDecoderTest
: public ::testing::Test
{
67 : decoder_(ObtainHpackHuffmanTable()), decoder_peer_(&decoder_
) {}
69 bool DecodeHeaderBlock(StringPiece str
) {
70 return decoder_
.HandleControlFrameHeadersData(0, str
.data(), str
.size()) &&
71 decoder_
.HandleControlFrameHeadersComplete(0, nullptr);
74 const SpdyHeaderBlock
& decoded_block() const {
75 // TODO(jgraettinger): HpackDecoderTest should implement
76 // SpdyHeadersHandlerInterface, and collect headers for examination.
77 return decoder_peer_
.decoded_block();
80 const SpdyHeaderBlock
& DecodeBlockExpectingSuccess(StringPiece str
) {
81 EXPECT_TRUE(DecodeHeaderBlock(str
));
82 return decoded_block();
85 void expectEntry(size_t index
,
88 const string
& value
) {
89 const HpackEntry
* entry
= decoder_peer_
.header_table()->GetByIndex(index
);
90 EXPECT_EQ(name
, entry
->name()) << "index " << index
;
91 EXPECT_EQ(value
, entry
->value());
92 EXPECT_EQ(size
, entry
->Size());
93 EXPECT_EQ(index
, decoder_peer_
.header_table()->IndexOf(entry
));
96 HpackDecoder decoder_
;
97 test::HpackDecoderPeer decoder_peer_
;
100 TEST_F(HpackDecoderTest
, HandleControlFrameHeadersData
) {
101 // Strings under threshold are concatenated in the buffer.
103 decoder_
.HandleControlFrameHeadersData(0, "small string one", 16));
105 decoder_
.HandleControlFrameHeadersData(0, "small string two", 16));
106 // A string which would push the buffer over the threshold is refused.
107 EXPECT_FALSE(decoder_
.HandleControlFrameHeadersData(
108 0, "fails", kMaxDecodeBufferSize
- 32 + 1));
110 EXPECT_EQ(decoder_peer_
.headers_block_buffer(),
111 "small string onesmall string two");
114 TEST_F(HpackDecoderTest
, HandleControlFrameHeadersComplete
) {
115 decoder_peer_
.set_cookie_value("foobar=baz");
117 // Incremental cookie buffer should be emitted and cleared.
118 decoder_
.HandleControlFrameHeadersData(0, "\x82\x85", 2);
119 decoder_
.HandleControlFrameHeadersComplete(0, nullptr);
121 EXPECT_THAT(decoded_block(),
122 ElementsAre(Pair(":method", "GET"), Pair(":path", "/index.html"),
123 Pair("cookie", "foobar=baz")));
124 EXPECT_EQ(decoder_peer_
.cookie_value(), "");
127 TEST_F(HpackDecoderTest
, HandleHeaderRepresentation
) {
128 // All cookie crumbs are joined.
129 decoder_peer_
.HandleHeaderRepresentation("cookie", " part 1");
130 decoder_peer_
.HandleHeaderRepresentation("cookie", "part 2 ");
131 decoder_peer_
.HandleHeaderRepresentation("cookie", "part3");
133 // Already-delimited headers are passed through.
134 decoder_peer_
.HandleHeaderRepresentation("passed-through",
135 string("foo\0baz", 7));
137 // Other headers are joined on \0. Case matters.
138 decoder_peer_
.HandleHeaderRepresentation("joined", "not joined");
139 decoder_peer_
.HandleHeaderRepresentation("joineD", "value 1");
140 decoder_peer_
.HandleHeaderRepresentation("joineD", "value 2");
142 // Empty headers remain empty.
143 decoder_peer_
.HandleHeaderRepresentation("empty", "");
145 // Joined empty headers work as expected.
146 decoder_peer_
.HandleHeaderRepresentation("empty-joined", "");
147 decoder_peer_
.HandleHeaderRepresentation("empty-joined", "foo");
148 decoder_peer_
.HandleHeaderRepresentation("empty-joined", "");
149 decoder_peer_
.HandleHeaderRepresentation("empty-joined", "");
151 // Non-contiguous cookie crumb.
152 decoder_peer_
.HandleHeaderRepresentation("cookie", " fin!");
154 // Finish and emit all headers.
155 decoder_
.HandleControlFrameHeadersComplete(0, nullptr);
157 EXPECT_THAT(decoded_block(),
158 ElementsAre(Pair("cookie", " part 1; part 2 ; part3; fin!"),
160 Pair("empty-joined", string("\0foo\0\0", 6)),
161 Pair("joineD", string("value 1\0value 2", 15)),
162 Pair("joined", "not joined"),
163 Pair("passed-through", string("foo\0baz", 7))));
166 // Decoding an encoded name with a valid string literal should work.
167 TEST_F(HpackDecoderTest
, DecodeNextNameLiteral
) {
168 HpackInputStream
input_stream(kLiteralBound
, StringPiece("\x00\x04name", 6));
170 StringPiece string_piece
;
171 EXPECT_TRUE(decoder_peer_
.DecodeNextName(&input_stream
, &string_piece
));
172 EXPECT_EQ("name", string_piece
);
173 EXPECT_FALSE(input_stream
.HasMoreData());
176 TEST_F(HpackDecoderTest
, DecodeNextNameLiteralWithHuffmanEncoding
) {
177 string input
= a2b_hex("008825a849e95ba97d7f");
178 HpackInputStream
input_stream(kLiteralBound
, input
);
180 StringPiece string_piece
;
181 EXPECT_TRUE(decoder_peer_
.DecodeNextName(&input_stream
, &string_piece
));
182 EXPECT_EQ("custom-key", string_piece
);
183 EXPECT_FALSE(input_stream
.HasMoreData());
186 // Decoding an encoded name with a valid index should work.
187 TEST_F(HpackDecoderTest
, DecodeNextNameIndexed
) {
188 HpackInputStream
input_stream(kLiteralBound
, "\x01");
190 StringPiece string_piece
;
191 EXPECT_TRUE(decoder_peer_
.DecodeNextName(&input_stream
, &string_piece
));
192 EXPECT_EQ(":authority", string_piece
);
193 EXPECT_FALSE(input_stream
.HasMoreData());
196 // Decoding an encoded name with an invalid index should fail.
197 TEST_F(HpackDecoderTest
, DecodeNextNameInvalidIndex
) {
198 // One more than the number of static table entries.
199 HpackInputStream
input_stream(kLiteralBound
, "\x3e");
201 StringPiece string_piece
;
202 EXPECT_FALSE(decoder_peer_
.DecodeNextName(&input_stream
, &string_piece
));
205 // Decoding indexed static table field should work.
206 TEST_F(HpackDecoderTest
, IndexedHeaderStatic
) {
207 // Reference static table entries #2 and #5.
208 SpdyHeaderBlock header_set1
= DecodeBlockExpectingSuccess("\x82\x85");
209 SpdyHeaderBlock expected_header_set1
;
210 expected_header_set1
[":method"] = "GET";
211 expected_header_set1
[":path"] = "/index.html";
212 EXPECT_EQ(expected_header_set1
, header_set1
);
214 // Reference static table entry #2.
215 SpdyHeaderBlock header_set2
= DecodeBlockExpectingSuccess("\x82");
216 SpdyHeaderBlock expected_header_set2
;
217 expected_header_set2
[":method"] = "GET";
218 EXPECT_EQ(expected_header_set2
, header_set2
);
221 TEST_F(HpackDecoderTest
, IndexedHeaderDynamic
) {
222 // First header block: add an entry to header table.
223 SpdyHeaderBlock header_set1
= DecodeBlockExpectingSuccess(
228 SpdyHeaderBlock expected_header_set1
;
229 expected_header_set1
["foo"] = "bar";
230 EXPECT_EQ(expected_header_set1
, header_set1
);
232 // Second header block: add another entry to header table.
233 SpdyHeaderBlock header_set2
= DecodeBlockExpectingSuccess(
238 SpdyHeaderBlock expected_header_set2
;
239 expected_header_set2
["foo"] = "bar";
240 expected_header_set2
["spam"] = "eggs";
241 EXPECT_EQ(expected_header_set2
, header_set2
);
243 // Third header block: refer to most recently added entry.
244 SpdyHeaderBlock header_set3
= DecodeBlockExpectingSuccess("\xbe");
245 SpdyHeaderBlock expected_header_set3
;
246 expected_header_set3
["spam"] = "eggs";
247 EXPECT_EQ(expected_header_set3
, header_set3
);
250 // Test a too-large indexed header.
251 TEST_F(HpackDecoderTest
, InvalidIndexedHeader
) {
252 // High-bit set, and a prefix of one more than the number of static entries.
253 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\xbe", 1)));
256 // Test that a header block with a pseudo-header field following a regular one
257 // is treated as malformed. (HTTP2 draft-14 8.1.2.1., HPACK draft-09 3.1.)
259 TEST_F(HpackDecoderTest
, InvalidPseudoHeaderPositionStatic
) {
260 // Okay: ":path" (static entry 4) followed by "allow" (static entry 20).
261 EXPECT_TRUE(DecodeHeaderBlock(a2b_hex("8494")));
262 // Malformed: "allow" (static entry 20) followed by ":path" (static entry 4).
263 EXPECT_FALSE(DecodeHeaderBlock(a2b_hex("9484")));
266 TEST_F(HpackDecoderTest
, InvalidPseudoHeaderPositionLiteral
) {
267 // Okay: literal ":bar" followed by literal "foo".
268 EXPECT_TRUE(DecodeHeaderBlock(a2b_hex("40043a626172004003666f6f00")));
269 // Malformed: literal "foo" followed by literal ":bar".
270 EXPECT_FALSE(DecodeHeaderBlock(a2b_hex("4003666f6f0040043a62617200")));
273 TEST_F(HpackDecoderTest
, ContextUpdateMaximumSize
) {
274 EXPECT_EQ(kDefaultHeaderTableSizeSetting
,
275 decoder_peer_
.header_table()->max_size());
278 // Maximum-size update with size 126. Succeeds.
279 HpackOutputStream output_stream
;
280 output_stream
.AppendPrefix(kHeaderTableSizeUpdateOpcode
);
281 output_stream
.AppendUint32(126);
283 output_stream
.TakeString(&input
);
284 EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input
)));
285 EXPECT_EQ(126u, decoder_peer_
.header_table()->max_size());
288 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds.
289 HpackOutputStream output_stream
;
290 output_stream
.AppendPrefix(kHeaderTableSizeUpdateOpcode
);
291 output_stream
.AppendUint32(kDefaultHeaderTableSizeSetting
);
293 output_stream
.TakeString(&input
);
294 EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input
)));
295 EXPECT_EQ(kDefaultHeaderTableSizeSetting
,
296 decoder_peer_
.header_table()->max_size());
299 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails.
300 HpackOutputStream output_stream
;
301 output_stream
.AppendPrefix(kHeaderTableSizeUpdateOpcode
);
302 output_stream
.AppendUint32(kDefaultHeaderTableSizeSetting
+ 1);
304 output_stream
.TakeString(&input
);
305 EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input
)));
306 EXPECT_EQ(kDefaultHeaderTableSizeSetting
,
307 decoder_peer_
.header_table()->max_size());
311 // Decoding two valid encoded literal headers with no indexing should
313 TEST_F(HpackDecoderTest
, LiteralHeaderNoIndexing
) {
314 // First header with indexed name, second header with string literal
316 const char input
[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2";
317 SpdyHeaderBlock header_set
=
318 DecodeBlockExpectingSuccess(StringPiece(input
, arraysize(input
) - 1));
320 SpdyHeaderBlock expected_header_set
;
321 expected_header_set
[":path"] = "/sample/path";
322 expected_header_set
[":path2"] = "/sample/path/2";
323 EXPECT_EQ(expected_header_set
, header_set
);
326 // Decoding two valid encoded literal headers with incremental
327 // indexing and string literal names should work.
328 TEST_F(HpackDecoderTest
, LiteralHeaderIncrementalIndexing
) {
329 const char input
[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2";
330 SpdyHeaderBlock header_set
=
331 DecodeBlockExpectingSuccess(StringPiece(input
, arraysize(input
) - 1));
333 SpdyHeaderBlock expected_header_set
;
334 expected_header_set
[":path"] = "/sample/path";
335 expected_header_set
[":path2"] = "/sample/path/2";
336 EXPECT_EQ(expected_header_set
, header_set
);
339 TEST_F(HpackDecoderTest
, LiteralHeaderWithIndexingInvalidNameIndex
) {
340 decoder_
.ApplyHeaderTableSizeSetting(0);
342 // Name is the last static index. Works.
343 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x7d\x03ooo")));
344 // Name is one beyond the last static index. Fails.
345 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x7e\x03ooo")));
348 TEST_F(HpackDecoderTest
, LiteralHeaderNoIndexingInvalidNameIndex
) {
349 // Name is the last static index. Works.
350 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x0f\x2e\x03ooo")));
351 // Name is one beyond the last static index. Fails.
352 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x0f\x2f\x03ooo")));
355 TEST_F(HpackDecoderTest
, LiteralHeaderNeverIndexedInvalidNameIndex
) {
356 // Name is the last static index. Works.
357 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x1f\x2e\x03ooo")));
358 // Name is one beyond the last static index. Fails.
359 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x1f\x2f\x03ooo")));
362 // Round-tripping the header set from E.2.1 should work.
363 TEST_F(HpackDecoderTest
, BasicE21
) {
364 HpackEncoder
encoder(ObtainHpackHuffmanTable());
366 SpdyHeaderBlock expected_header_set
;
367 expected_header_set
[":method"] = "GET";
368 expected_header_set
[":scheme"] = "http";
369 expected_header_set
[":path"] = "/";
370 expected_header_set
[":authority"] = "www.example.com";
372 string encoded_header_set
;
374 encoder
.EncodeHeaderSet(expected_header_set
, &encoded_header_set
));
376 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set
));
377 EXPECT_EQ(expected_header_set
, decoded_block());
380 TEST_F(HpackDecoderTest
, SectionD4RequestHuffmanExamples
) {
381 SpdyHeaderBlock header_set
;
383 // 82 | == Indexed - Add ==
386 // 86 | == Indexed - Add ==
388 // | -> :scheme: http
389 // 84 | == Indexed - Add ==
392 // 41 | == Literal indexed ==
393 // | Indexed name (idx = 1)
395 // 8c | Literal value (len = 15)
396 // | Huffman encoded:
397 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
400 // | -> :authority: www.example.com
401 string first
= a2b_hex(
402 "828684418cf1e3c2e5f23a6ba0ab90f4"
404 header_set
= DecodeBlockExpectingSuccess(first
);
408 ElementsAre(Pair(":authority", "www.example.com"), Pair(":method", "GET"),
409 Pair(":path", "/"), Pair(":scheme", "http")));
411 expectEntry(62, 57, ":authority", "www.example.com");
412 EXPECT_EQ(57u, decoder_peer_
.header_table()->size());
414 // 82 | == Indexed - Add ==
417 // 86 | == Indexed - Add ==
419 // | -> :scheme: http
420 // 84 | == Indexed - Add ==
423 // be | == Indexed - Add ==
425 // | -> :authority: www.example.com
426 // 58 | == Literal indexed ==
427 // | Indexed name (idx = 24)
429 // 86 | Literal value (len = 8)
430 // | Huffman encoded:
431 // a8eb 1064 9cbf | ...d..
434 // | -> cache-control: no-cache
436 string second
= a2b_hex("828684be5886a8eb10649cbf");
437 header_set
= DecodeBlockExpectingSuccess(second
);
441 ElementsAre(Pair(":authority", "www.example.com"), Pair(":method", "GET"),
442 Pair(":path", "/"), Pair(":scheme", "http"),
443 Pair("cache-control", "no-cache")));
445 expectEntry(62, 53, "cache-control", "no-cache");
446 expectEntry(63, 57, ":authority", "www.example.com");
447 EXPECT_EQ(110u, decoder_peer_
.header_table()->size());
449 // 82 | == Indexed - Add ==
452 // 87 | == Indexed - Add ==
454 // | -> :scheme: https
455 // 85 | == Indexed - Add ==
457 // | -> :path: /index.html
458 // bf | == Indexed - Add ==
460 // | -> :authority: www.example.com
461 // 40 | == Literal indexed ==
462 // 88 | Literal name (len = 10)
463 // | Huffman encoded:
464 // 25a8 49e9 5ba9 7d7f | %.I.[.}.
467 // 89 | Literal value (len = 12)
468 // | Huffman encoded:
469 // 25a8 49e9 5bb8 e8b4 bf | %.I.[....
472 // | -> custom-key: custom-value
473 string third
= a2b_hex(
474 "828785bf408825a849e95ba97d7f89"
475 "25a849e95bb8e8b4bf");
476 header_set
= DecodeBlockExpectingSuccess(third
);
480 ElementsAre(Pair(":authority", "www.example.com"), Pair(":method", "GET"),
481 Pair(":path", "/index.html"), Pair(":scheme", "https"),
482 Pair("custom-key", "custom-value")));
484 expectEntry(62, 54, "custom-key", "custom-value");
485 expectEntry(63, 53, "cache-control", "no-cache");
486 expectEntry(64, 57, ":authority", "www.example.com");
487 EXPECT_EQ(164u, decoder_peer_
.header_table()->size());
490 TEST_F(HpackDecoderTest
, SectionD6ResponseHuffmanExamples
) {
491 SpdyHeaderBlock header_set
;
492 decoder_
.ApplyHeaderTableSizeSetting(256);
494 // 48 | == Literal indexed ==
495 // | Indexed name (idx = 8)
497 // 82 | Literal value (len = 3)
498 // | Huffman encoded:
503 // 58 | == Literal indexed ==
504 // | Indexed name (idx = 24)
506 // 85 | Literal value (len = 7)
507 // | Huffman encoded:
508 // aec3 771a 4b | ..w.K
511 // | -> cache-control: private
512 // 61 | == Literal indexed ==
513 // | Indexed name (idx = 33)
515 // 96 | Literal value (len = 29)
516 // | Huffman encoded:
517 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
518 // e082 a62d 1bff | ...-..
520 // | Mon, 21 Oct 2013 20:13:21
522 // | -> date: Mon, 21 Oct 2013
524 // 6e | == Literal indexed ==
525 // | Indexed name (idx = 46)
527 // 91 | Literal value (len = 23)
528 // | Huffman encoded:
529 // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
532 // | https://www.example.com
533 // | -> location: https://www.e
536 string first
= a2b_hex(
537 "488264025885aec3771a4b6196d07abe"
538 "941054d444a8200595040b8166e082a6"
539 "2d1bff6e919d29ad171863c78f0b97c8"
541 header_set
= DecodeBlockExpectingSuccess(first
);
545 ElementsAre(Pair(":status", "302"), Pair("cache-control", "private"),
546 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
547 Pair("location", "https://www.example.com")));
549 expectEntry(62, 63, "location", "https://www.example.com");
550 expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
551 expectEntry(64, 52, "cache-control", "private");
552 expectEntry(65, 42, ":status", "302");
553 EXPECT_EQ(222u, decoder_peer_
.header_table()->size());
555 // 48 | == Literal indexed ==
556 // | Indexed name (idx = 8)
558 // 83 | Literal value (len = 3)
559 // | Huffman encoded:
563 // | - evict: :status: 302
565 // c1 | == Indexed - Add ==
567 // | -> cache-control: private
568 // c0 | == Indexed - Add ==
570 // | -> date: Mon, 21 Oct 2013
572 // bf | == Indexed - Add ==
575 // | https://www.example.com
576 string second
= a2b_hex("4883640effc1c0bf");
577 header_set
= DecodeBlockExpectingSuccess(second
);
581 ElementsAre(Pair(":status", "307"), Pair("cache-control", "private"),
582 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
583 Pair("location", "https://www.example.com")));
585 expectEntry(62, 42, ":status", "307");
586 expectEntry(63, 63, "location", "https://www.example.com");
587 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
588 expectEntry(65, 52, "cache-control", "private");
589 EXPECT_EQ(222u, decoder_peer_
.header_table()->size());
591 // 88 | == Indexed - Add ==
594 // c1 | == Indexed - Add ==
596 // | -> cache-control: private
597 // 61 | == Literal indexed ==
598 // | Indexed name (idx = 33)
600 // 96 | Literal value (len = 22)
601 // | Huffman encoded:
602 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
603 // e084 a62d 1bff | ...-..
605 // | Mon, 21 Oct 2013 20:13:22
607 // | - evict: cache-control:
609 // | -> date: Mon, 21 Oct 2013
611 // c0 | == Indexed - Add ==
614 // | https://www.example.com
615 // 5a | == Literal indexed ==
616 // | Indexed name (idx = 26)
617 // | content-encoding
618 // 83 | Literal value (len = 3)
619 // | Huffman encoded:
623 // | - evict: date: Mon, 21 Oct
624 // | 2013 20:13:21 GMT
625 // | -> content-encoding: gzip
626 // 77 | == Literal indexed ==
627 // | Indexed name (idx = 55)
629 // ad | Literal value (len = 45)
630 // | Huffman encoded:
631 // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9`
632 // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)...
633 // 3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P.
635 // | foo=ASDJKHQKBZXOQWEOPIUAXQ
636 // | WEOIU; max-age=3600; versi
638 // | - evict: location:
639 // | https://www.example.com
640 // | - evict: :status: 307
641 // | -> set-cookie: foo=ASDJKHQ
642 // | KBZXOQWEOPIUAXQWEOIU;
643 // | max-age=3600; version=1
644 string third
= a2b_hex(
645 "88c16196d07abe941054d444a8200595"
646 "040b8166e084a62d1bffc05a839bd9ab"
647 "77ad94e7821dd7f2e6c7b335dfdfcd5b"
648 "3960d5af27087f3672c1ab270fb5291f"
649 "9587316065c003ed4ee5b1063d5007");
650 header_set
= DecodeBlockExpectingSuccess(third
);
654 ElementsAre(Pair(":status", "200"), Pair("cache-control", "private"),
655 Pair("content-encoding", "gzip"),
656 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
657 Pair("location", "https://www.example.com"),
659 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
660 " max-age=3600; version=1")));
662 expectEntry(62, 98, "set-cookie",
663 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
664 " max-age=3600; version=1");
665 expectEntry(63, 52, "content-encoding", "gzip");
666 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT");
667 EXPECT_EQ(215u, decoder_peer_
.header_table()->size());