OLD | NEW |
| (Empty) |
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/hpack_decoder.h" | |
6 | |
7 #include <map> | |
8 | |
9 #include "base/logging.h" | |
10 #include "net/spdy/hpack/hpack_encoder.h" | |
11 #include "net/spdy/hpack/hpack_input_stream.h" | |
12 #include "net/spdy/hpack/hpack_output_stream.h" | |
13 #include "net/spdy/spdy_flags.h" | |
14 #include "net/spdy/spdy_protocol.h" | |
15 #include "net/spdy/spdy_test_utils.h" | |
16 #include "testing/gmock/include/gmock/gmock.h" | |
17 #include "testing/gtest/include/gtest/gtest.h" | |
18 | |
19 namespace net { | |
20 namespace test { | |
21 | |
22 class HpackDecoderPeer { | |
23 public: | |
24 explicit HpackDecoderPeer(HpackDecoder* decoder) : decoder_(decoder) {} | |
25 | |
26 void HandleHeaderRepresentation(SpdyStringPiece name, SpdyStringPiece value) { | |
27 decoder_->HandleHeaderRepresentation(name, value); | |
28 } | |
29 bool DecodeNextName(HpackInputStream* in, SpdyStringPiece* out) { | |
30 return decoder_->DecodeNextName(in, out); | |
31 } | |
32 HpackHeaderTable* header_table() { return &decoder_->header_table_; } | |
33 | |
34 bool DecodeNextStringLiteral(HpackInputStream* in, | |
35 bool is_header_key, | |
36 SpdyStringPiece* str) { | |
37 return decoder_->DecodeNextStringLiteral(in, is_header_key, str); | |
38 } | |
39 | |
40 const SpdyString& headers_block_buffer() const { | |
41 return decoder_->headers_block_buffer_; | |
42 } | |
43 | |
44 private: | |
45 HpackDecoder* decoder_; | |
46 }; | |
47 | |
48 namespace { | |
49 | |
50 using test::a2b_hex; | |
51 | |
52 using testing::ElementsAre; | |
53 using testing::Pair; | |
54 | |
55 class HpackDecoderTest : public ::testing::TestWithParam<bool> { | |
56 protected: | |
57 HpackDecoderTest() : decoder_(), decoder_peer_(&decoder_) {} | |
58 | |
59 void SetUp() override { handler_exists_ = GetParam(); } | |
60 | |
61 bool DecodeHeaderBlock(SpdyStringPiece str) { | |
62 if (handler_exists_) { | |
63 decoder_.HandleControlFrameHeadersStart(&handler_); | |
64 } | |
65 return decoder_.HandleControlFrameHeadersData(str.data(), str.size()) && | |
66 decoder_.HandleControlFrameHeadersComplete(nullptr); | |
67 } | |
68 | |
69 bool HandleControlFrameHeadersData(SpdyStringPiece str) { | |
70 return decoder_.HandleControlFrameHeadersData(str.data(), str.size()); | |
71 } | |
72 | |
73 bool HandleControlFrameHeadersComplete(size_t* size) { | |
74 return decoder_.HandleControlFrameHeadersComplete(size); | |
75 } | |
76 | |
77 const SpdyHeaderBlock& decoded_block() const { | |
78 if (handler_exists_) { | |
79 return handler_.decoded_block(); | |
80 } else { | |
81 return decoder_.decoded_block(); | |
82 } | |
83 } | |
84 | |
85 const SpdyHeaderBlock& DecodeBlockExpectingSuccess(SpdyStringPiece str) { | |
86 EXPECT_TRUE(DecodeHeaderBlock(str)); | |
87 return decoded_block(); | |
88 } | |
89 | |
90 void expectEntry(size_t index, | |
91 size_t size, | |
92 const SpdyString& name, | |
93 const SpdyString& value) { | |
94 const HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index); | |
95 EXPECT_EQ(name, entry->name()) << "index " << index; | |
96 EXPECT_EQ(value, entry->value()); | |
97 EXPECT_EQ(size, entry->Size()); | |
98 EXPECT_EQ(index, decoder_peer_.header_table()->IndexOf(entry)); | |
99 } | |
100 | |
101 HpackDecoder decoder_; | |
102 test::HpackDecoderPeer decoder_peer_; | |
103 TestHeadersHandler handler_; | |
104 bool handler_exists_; | |
105 }; | |
106 | |
107 INSTANTIATE_TEST_CASE_P(WithAndWithoutHeadersHandler, | |
108 HpackDecoderTest, | |
109 ::testing::Bool()); | |
110 | |
111 TEST_P(HpackDecoderTest, AddHeaderDataWithHandleControlFrameHeadersData) { | |
112 // The hpack decode buffer size is limited in size. This test verifies that | |
113 // adding encoded data under that limit is accepted, and data that exceeds the | |
114 // limit is rejected. | |
115 const size_t kMaxBufferSizeBytes = 50; | |
116 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes); | |
117 | |
118 // Strings under threshold are concatenated in the buffer. | |
119 SpdyString first_input; | |
120 first_input.push_back(0x00); // Literal name and value, unindexed | |
121 first_input.push_back(0x7f); // Name length = 127 | |
122 ASSERT_EQ(2u, first_input.size()); | |
123 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(first_input.data(), | |
124 first_input.size())); | |
125 // Further 38 bytes to make 40 total buffered bytes. | |
126 SpdyString second_input = SpdyString(38, 'x'); | |
127 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(second_input.data(), | |
128 second_input.size())); | |
129 // A string which would push the buffer over the threshold is refused. | |
130 const int kThirdInputSize = | |
131 kMaxBufferSizeBytes - (first_input.size() + second_input.size()) + 1; | |
132 SpdyString third_input = SpdyString(kThirdInputSize, 'y'); | |
133 ASSERT_GT(first_input.size() + second_input.size() + third_input.size(), | |
134 kMaxBufferSizeBytes); | |
135 EXPECT_FALSE(decoder_.HandleControlFrameHeadersData(third_input.data(), | |
136 third_input.size())); | |
137 | |
138 SpdyString expected(first_input); | |
139 expected.append(second_input); | |
140 EXPECT_EQ(expected, decoder_peer_.headers_block_buffer()); | |
141 } | |
142 | |
143 // Decode with incomplete data in buffer. | |
144 TEST_P(HpackDecoderTest, DecodeWithIncompleteData) { | |
145 // No need to wait for more data. | |
146 EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82")); | |
147 EXPECT_EQ("", decoder_peer_.headers_block_buffer()); | |
148 | |
149 // Need to wait for more data. | |
150 EXPECT_TRUE( | |
151 HandleControlFrameHeadersData("\x40\x03goo" | |
152 "\x03gar\xbe\x40\x04spam")); | |
153 EXPECT_EQ("\x40\x04spam", decoder_peer_.headers_block_buffer()); | |
154 | |
155 // Add the needed data. | |
156 EXPECT_TRUE(HandleControlFrameHeadersData("\x04gggs")); | |
157 EXPECT_EQ("", decoder_peer_.headers_block_buffer()); | |
158 | |
159 size_t size = 0; | |
160 EXPECT_TRUE(HandleControlFrameHeadersComplete(&size)); | |
161 EXPECT_EQ(24u, size); | |
162 } | |
163 | |
164 TEST_P(HpackDecoderTest, HandleHeaderRepresentation) { | |
165 if (handler_exists_) { | |
166 decoder_.HandleControlFrameHeadersStart(&handler_); | |
167 } | |
168 | |
169 // All cookie crumbs are joined. | |
170 decoder_peer_.HandleHeaderRepresentation("cookie", " part 1"); | |
171 decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 "); | |
172 decoder_peer_.HandleHeaderRepresentation("cookie", "part3"); | |
173 | |
174 // Already-delimited headers are passed through. | |
175 decoder_peer_.HandleHeaderRepresentation("passed-through", | |
176 SpdyString("foo\0baz", 7)); | |
177 | |
178 // Other headers are joined on \0. Case matters. | |
179 decoder_peer_.HandleHeaderRepresentation("joined", "not joined"); | |
180 decoder_peer_.HandleHeaderRepresentation("joineD", "value 1"); | |
181 decoder_peer_.HandleHeaderRepresentation("joineD", "value 2"); | |
182 | |
183 // Empty headers remain empty. | |
184 decoder_peer_.HandleHeaderRepresentation("empty", ""); | |
185 | |
186 // Joined empty headers work as expected. | |
187 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); | |
188 decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo"); | |
189 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); | |
190 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); | |
191 | |
192 // Non-contiguous cookie crumb. | |
193 decoder_peer_.HandleHeaderRepresentation("cookie", " fin!"); | |
194 | |
195 // Finish and emit all headers. | |
196 decoder_.HandleControlFrameHeadersComplete(nullptr); | |
197 | |
198 // Resulting decoded headers are in the same order as input. | |
199 EXPECT_THAT( | |
200 decoded_block(), | |
201 ElementsAre(Pair("cookie", " part 1; part 2 ; part3; fin!"), | |
202 Pair("passed-through", SpdyStringPiece("foo\0baz", 7)), | |
203 Pair("joined", "not joined"), | |
204 Pair("joineD", SpdyStringPiece("value 1\0value 2", 15)), | |
205 Pair("empty", ""), | |
206 Pair("empty-joined", SpdyStringPiece("\0foo\0\0", 6)))); | |
207 } | |
208 | |
209 // Decoding an encoded name with a valid string literal should work. | |
210 TEST_P(HpackDecoderTest, DecodeNextNameLiteral) { | |
211 HpackInputStream input_stream(SpdyStringPiece("\x00\x04name", 6)); | |
212 | |
213 SpdyStringPiece string_piece; | |
214 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
215 EXPECT_EQ("name", string_piece); | |
216 EXPECT_FALSE(input_stream.HasMoreData()); | |
217 EXPECT_FALSE(input_stream.NeedMoreData()); | |
218 input_stream.MarkCurrentPosition(); | |
219 EXPECT_EQ(6u, input_stream.ParsedBytes()); | |
220 } | |
221 | |
222 // Decoding an encoded name with an incomplete string literal. | |
223 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithIncompleteHeader) { | |
224 HpackInputStream input_stream(SpdyStringPiece("\x00\x04name\x00\x02g", 9)); | |
225 | |
226 SpdyStringPiece string_piece; | |
227 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
228 EXPECT_FALSE(input_stream.NeedMoreData()); | |
229 input_stream.MarkCurrentPosition(); | |
230 EXPECT_EQ(6u, input_stream.ParsedBytes()); | |
231 | |
232 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
233 EXPECT_TRUE(input_stream.NeedMoreData()); | |
234 input_stream.MarkCurrentPosition(); | |
235 EXPECT_EQ(8u, input_stream.ParsedBytes()); | |
236 } | |
237 | |
238 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) { | |
239 SpdyString input = a2b_hex("008825a849e95ba97d7f"); | |
240 HpackInputStream input_stream(input); | |
241 | |
242 SpdyStringPiece string_piece; | |
243 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
244 EXPECT_EQ("custom-key", string_piece); | |
245 EXPECT_FALSE(input_stream.HasMoreData()); | |
246 EXPECT_FALSE(input_stream.NeedMoreData()); | |
247 input_stream.MarkCurrentPosition(); | |
248 EXPECT_EQ(input.size(), input_stream.ParsedBytes()); | |
249 } | |
250 | |
251 // Decode with incomplete huffman encoding. | |
252 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithIncompleteHuffmanEncoding) { | |
253 // CHECK(huffman_table_.Initialize(kHpackHuffmanCode, | |
254 // arraysize(kHpackHuffmanCode))); | |
255 // Put two copies of the same huffman encoding into input. | |
256 SpdyString input = a2b_hex("008825a849e95ba97d7f008825a849e95ba97d7f"); | |
257 input.resize(input.size() - 1); // Remove the last byte. | |
258 HpackInputStream input_stream(input); | |
259 | |
260 SpdyStringPiece string_piece; | |
261 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
262 EXPECT_FALSE(input_stream.NeedMoreData()); | |
263 input_stream.MarkCurrentPosition(); | |
264 EXPECT_EQ(10u, input_stream.ParsedBytes()); | |
265 | |
266 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
267 EXPECT_TRUE(input_stream.NeedMoreData()); | |
268 input_stream.MarkCurrentPosition(); | |
269 EXPECT_EQ(12u, input_stream.ParsedBytes()); | |
270 } | |
271 | |
272 // Decoding an encoded name with a valid index should work. | |
273 TEST_P(HpackDecoderTest, DecodeNextNameIndexed) { | |
274 HpackInputStream input_stream("\x01"); | |
275 | |
276 SpdyStringPiece string_piece; | |
277 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
278 EXPECT_EQ(":authority", string_piece); | |
279 EXPECT_FALSE(input_stream.HasMoreData()); | |
280 EXPECT_FALSE(input_stream.NeedMoreData()); | |
281 input_stream.MarkCurrentPosition(); | |
282 EXPECT_EQ(1u, input_stream.ParsedBytes()); | |
283 } | |
284 | |
285 // Decoding an encoded name with an invalid index should fail. | |
286 TEST_P(HpackDecoderTest, DecodeNextNameInvalidIndex) { | |
287 // One more than the number of static table entries. | |
288 HpackInputStream input_stream("\x3e"); | |
289 | |
290 SpdyStringPiece string_piece; | |
291 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
292 EXPECT_FALSE(input_stream.NeedMoreData()); | |
293 input_stream.MarkCurrentPosition(); | |
294 EXPECT_EQ(1u, input_stream.ParsedBytes()); | |
295 } | |
296 | |
297 // Decoding indexed static table field should work. | |
298 TEST_P(HpackDecoderTest, IndexedHeaderStatic) { | |
299 // Reference static table entries #2 and #5. | |
300 const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess("\x82\x85"); | |
301 SpdyHeaderBlock expected_header_set1; | |
302 expected_header_set1[":method"] = "GET"; | |
303 expected_header_set1[":path"] = "/index.html"; | |
304 EXPECT_EQ(expected_header_set1, header_set1); | |
305 | |
306 // Reference static table entry #2. | |
307 const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess("\x82"); | |
308 SpdyHeaderBlock expected_header_set2; | |
309 expected_header_set2[":method"] = "GET"; | |
310 EXPECT_EQ(expected_header_set2, header_set2); | |
311 } | |
312 | |
313 TEST_P(HpackDecoderTest, IndexedHeaderDynamic) { | |
314 // First header block: add an entry to header table. | |
315 const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess( | |
316 "\x40\x03" | |
317 "foo" | |
318 "\x03" | |
319 "bar"); | |
320 SpdyHeaderBlock expected_header_set1; | |
321 expected_header_set1["foo"] = "bar"; | |
322 EXPECT_EQ(expected_header_set1, header_set1); | |
323 | |
324 // Second header block: add another entry to header table. | |
325 const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess( | |
326 "\xbe\x40\x04" | |
327 "spam" | |
328 "\x04" | |
329 "eggs"); | |
330 SpdyHeaderBlock expected_header_set2; | |
331 expected_header_set2["foo"] = "bar"; | |
332 expected_header_set2["spam"] = "eggs"; | |
333 EXPECT_EQ(expected_header_set2, header_set2); | |
334 | |
335 // Third header block: refer to most recently added entry. | |
336 const SpdyHeaderBlock& header_set3 = DecodeBlockExpectingSuccess("\xbe"); | |
337 SpdyHeaderBlock expected_header_set3; | |
338 expected_header_set3["spam"] = "eggs"; | |
339 EXPECT_EQ(expected_header_set3, header_set3); | |
340 } | |
341 | |
342 // Test a too-large indexed header. | |
343 TEST_P(HpackDecoderTest, InvalidIndexedHeader) { | |
344 // High-bit set, and a prefix of one more than the number of static entries. | |
345 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\xbe", 1))); | |
346 } | |
347 | |
348 TEST_P(HpackDecoderTest, ContextUpdateMaximumSize) { | |
349 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
350 decoder_peer_.header_table()->max_size()); | |
351 SpdyString input; | |
352 { | |
353 // Maximum-size update with size 126. Succeeds. | |
354 HpackOutputStream output_stream; | |
355 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
356 output_stream.AppendUint32(126); | |
357 | |
358 output_stream.TakeString(&input); | |
359 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
360 EXPECT_EQ(126u, decoder_peer_.header_table()->max_size()); | |
361 } | |
362 { | |
363 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds. | |
364 HpackOutputStream output_stream; | |
365 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
366 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting); | |
367 | |
368 output_stream.TakeString(&input); | |
369 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
370 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
371 decoder_peer_.header_table()->max_size()); | |
372 } | |
373 { | |
374 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails. | |
375 HpackOutputStream output_stream; | |
376 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
377 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1); | |
378 | |
379 output_stream.TakeString(&input); | |
380 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
381 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
382 decoder_peer_.header_table()->max_size()); | |
383 } | |
384 } | |
385 | |
386 // Two HeaderTableSizeUpdates may appear at the beginning of the block | |
387 TEST_P(HpackDecoderTest, TwoTableSizeUpdates) { | |
388 SpdyString input; | |
389 { | |
390 // Should accept two table size updates, update to second one | |
391 HpackOutputStream output_stream; | |
392 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
393 output_stream.AppendUint32(0); | |
394 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
395 output_stream.AppendUint32(122); | |
396 | |
397 output_stream.TakeString(&input); | |
398 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
399 EXPECT_EQ(122u, decoder_peer_.header_table()->max_size()); | |
400 } | |
401 } | |
402 | |
403 // Three HeaderTableSizeUpdates should result in an error | |
404 TEST_P(HpackDecoderTest, ThreeTableSizeUpdatesError) { | |
405 SpdyString input; | |
406 { | |
407 // Should reject three table size updates, update to second one | |
408 HpackOutputStream output_stream; | |
409 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
410 output_stream.AppendUint32(5); | |
411 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
412 output_stream.AppendUint32(10); | |
413 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
414 output_stream.AppendUint32(15); | |
415 | |
416 output_stream.TakeString(&input); | |
417 | |
418 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
419 EXPECT_EQ(10u, decoder_peer_.header_table()->max_size()); | |
420 } | |
421 } | |
422 | |
423 // HeaderTableSizeUpdates may only appear at the beginning of the block | |
424 // Any other updates should result in an error | |
425 TEST_P(HpackDecoderTest, TableSizeUpdateSecondError) { | |
426 SpdyString input; | |
427 { | |
428 // Should reject a table size update appearing after a different entry | |
429 // The table size should remain as the default | |
430 HpackOutputStream output_stream; | |
431 output_stream.AppendBytes("\x82\x85"); | |
432 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
433 output_stream.AppendUint32(123); | |
434 | |
435 output_stream.TakeString(&input); | |
436 | |
437 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
438 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
439 decoder_peer_.header_table()->max_size()); | |
440 } | |
441 } | |
442 | |
443 // HeaderTableSizeUpdates may only appear at the beginning of the block | |
444 // Any other updates should result in an error | |
445 TEST_P(HpackDecoderTest, TableSizeUpdateFirstThirdError) { | |
446 SpdyString input; | |
447 { | |
448 // Should reject the second table size update | |
449 // if a different entry appears after the first update | |
450 // The table size should update to the first but not the second | |
451 HpackOutputStream output_stream; | |
452 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
453 output_stream.AppendUint32(60); | |
454 output_stream.AppendBytes("\x82\x85"); | |
455 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
456 output_stream.AppendUint32(125); | |
457 | |
458 output_stream.TakeString(&input); | |
459 | |
460 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
461 EXPECT_EQ(60u, decoder_peer_.header_table()->max_size()); | |
462 } | |
463 } | |
464 | |
465 // Decoding two valid encoded literal headers with no indexing should | |
466 // work. | |
467 TEST_P(HpackDecoderTest, LiteralHeaderNoIndexing) { | |
468 // First header with indexed name, second header with string literal | |
469 // name. | |
470 const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2"; | |
471 const SpdyHeaderBlock& header_set = | |
472 DecodeBlockExpectingSuccess(SpdyStringPiece(input, arraysize(input) - 1)); | |
473 | |
474 SpdyHeaderBlock expected_header_set; | |
475 expected_header_set[":path"] = "/sample/path"; | |
476 expected_header_set[":path2"] = "/sample/path/2"; | |
477 EXPECT_EQ(expected_header_set, header_set); | |
478 } | |
479 | |
480 // Decoding two valid encoded literal headers with incremental | |
481 // indexing and string literal names should work. | |
482 TEST_P(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { | |
483 const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2"; | |
484 const SpdyHeaderBlock& header_set = | |
485 DecodeBlockExpectingSuccess(SpdyStringPiece(input, arraysize(input) - 1)); | |
486 | |
487 SpdyHeaderBlock expected_header_set; | |
488 expected_header_set[":path"] = "/sample/path"; | |
489 expected_header_set[":path2"] = "/sample/path/2"; | |
490 EXPECT_EQ(expected_header_set, header_set); | |
491 } | |
492 | |
493 TEST_P(HpackDecoderTest, LiteralHeaderWithIndexingInvalidNameIndex) { | |
494 decoder_.ApplyHeaderTableSizeSetting(0); | |
495 | |
496 // Name is the last static index. Works. | |
497 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece("\x7d\x03ooo"))); | |
498 // Name is one beyond the last static index. Fails. | |
499 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\x7e\x03ooo"))); | |
500 } | |
501 | |
502 TEST_P(HpackDecoderTest, LiteralHeaderNoIndexingInvalidNameIndex) { | |
503 // Name is the last static index. Works. | |
504 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece("\x0f\x2e\x03ooo"))); | |
505 // Name is one beyond the last static index. Fails. | |
506 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\x0f\x2f\x03ooo"))); | |
507 } | |
508 | |
509 TEST_P(HpackDecoderTest, LiteralHeaderNeverIndexedInvalidNameIndex) { | |
510 // Name is the last static index. Works. | |
511 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece("\x1f\x2e\x03ooo"))); | |
512 // Name is one beyond the last static index. Fails. | |
513 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\x1f\x2f\x03ooo"))); | |
514 } | |
515 | |
516 // Decode with incomplete string literal. | |
517 TEST_P(HpackDecoderTest, StringLiteralIncomplete) { | |
518 const char input[] = "\x0c/sample/path\x06:path2\x0e/sample/path/"; | |
519 HpackInputStream input_stream(input); | |
520 SpdyStringPiece str; | |
521 EXPECT_TRUE( | |
522 decoder_peer_.DecodeNextStringLiteral(&input_stream, false, &str)); | |
523 EXPECT_FALSE(input_stream.NeedMoreData()); | |
524 input_stream.MarkCurrentPosition(); | |
525 EXPECT_EQ(13u, input_stream.ParsedBytes()); | |
526 | |
527 EXPECT_TRUE( | |
528 decoder_peer_.DecodeNextStringLiteral(&input_stream, false, &str)); | |
529 EXPECT_FALSE(input_stream.NeedMoreData()); | |
530 input_stream.MarkCurrentPosition(); | |
531 EXPECT_EQ(20u, input_stream.ParsedBytes()); | |
532 | |
533 EXPECT_FALSE( | |
534 decoder_peer_.DecodeNextStringLiteral(&input_stream, false, &str)); | |
535 EXPECT_TRUE(input_stream.NeedMoreData()); | |
536 input_stream.MarkCurrentPosition(); | |
537 EXPECT_EQ(21u, input_stream.ParsedBytes()); | |
538 } | |
539 | |
540 // Round-tripping the header set from RFC 7541 C.3.1 should work. | |
541 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1 | |
542 TEST_P(HpackDecoderTest, BasicC31) { | |
543 HpackEncoder encoder(ObtainHpackHuffmanTable()); | |
544 | |
545 SpdyHeaderBlock expected_header_set; | |
546 expected_header_set[":method"] = "GET"; | |
547 expected_header_set[":scheme"] = "http"; | |
548 expected_header_set[":path"] = "/"; | |
549 expected_header_set[":authority"] = "www.example.com"; | |
550 | |
551 SpdyString encoded_header_set; | |
552 EXPECT_TRUE( | |
553 encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set)); | |
554 | |
555 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set)); | |
556 EXPECT_EQ(expected_header_set, decoded_block()); | |
557 } | |
558 | |
559 // RFC 7541, Section C.4: Request Examples with Huffman Coding | |
560 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.4 | |
561 TEST_P(HpackDecoderTest, SectionC4RequestHuffmanExamples) { | |
562 // 82 | == Indexed - Add == | |
563 // | idx = 2 | |
564 // | -> :method: GET | |
565 // 86 | == Indexed - Add == | |
566 // | idx = 6 | |
567 // | -> :scheme: http | |
568 // 84 | == Indexed - Add == | |
569 // | idx = 4 | |
570 // | -> :path: / | |
571 // 41 | == Literal indexed == | |
572 // | Indexed name (idx = 1) | |
573 // | :authority | |
574 // 8c | Literal value (len = 15) | |
575 // | Huffman encoded: | |
576 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k..... | |
577 // | Decoded: | |
578 // | www.example.com | |
579 // | -> :authority: www.example.com | |
580 SpdyString first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff"); | |
581 const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first); | |
582 | |
583 EXPECT_THAT( | |
584 first_header_set, | |
585 ElementsAre(Pair(":method", "GET"), Pair(":scheme", "http"), | |
586 Pair(":path", "/"), Pair(":authority", "www.example.com"))); | |
587 | |
588 expectEntry(62, 57, ":authority", "www.example.com"); | |
589 EXPECT_EQ(57u, decoder_peer_.header_table()->size()); | |
590 | |
591 // 82 | == Indexed - Add == | |
592 // | idx = 2 | |
593 // | -> :method: GET | |
594 // 86 | == Indexed - Add == | |
595 // | idx = 6 | |
596 // | -> :scheme: http | |
597 // 84 | == Indexed - Add == | |
598 // | idx = 4 | |
599 // | -> :path: / | |
600 // be | == Indexed - Add == | |
601 // | idx = 62 | |
602 // | -> :authority: www.example.com | |
603 // 58 | == Literal indexed == | |
604 // | Indexed name (idx = 24) | |
605 // | cache-control | |
606 // 86 | Literal value (len = 8) | |
607 // | Huffman encoded: | |
608 // a8eb 1064 9cbf | ...d.. | |
609 // | Decoded: | |
610 // | no-cache | |
611 // | -> cache-control: no-cache | |
612 | |
613 SpdyString second = a2b_hex("828684be5886a8eb10649cbf"); | |
614 const SpdyHeaderBlock& second_header_set = | |
615 DecodeBlockExpectingSuccess(second); | |
616 | |
617 EXPECT_THAT( | |
618 second_header_set, | |
619 ElementsAre(Pair(":method", "GET"), Pair(":scheme", "http"), | |
620 Pair(":path", "/"), Pair(":authority", "www.example.com"), | |
621 Pair("cache-control", "no-cache"))); | |
622 | |
623 expectEntry(62, 53, "cache-control", "no-cache"); | |
624 expectEntry(63, 57, ":authority", "www.example.com"); | |
625 EXPECT_EQ(110u, decoder_peer_.header_table()->size()); | |
626 | |
627 // 82 | == Indexed - Add == | |
628 // | idx = 2 | |
629 // | -> :method: GET | |
630 // 87 | == Indexed - Add == | |
631 // | idx = 7 | |
632 // | -> :scheme: https | |
633 // 85 | == Indexed - Add == | |
634 // | idx = 5 | |
635 // | -> :path: /index.html | |
636 // bf | == Indexed - Add == | |
637 // | idx = 63 | |
638 // | -> :authority: www.example.com | |
639 // 40 | == Literal indexed == | |
640 // 88 | Literal name (len = 10) | |
641 // | Huffman encoded: | |
642 // 25a8 49e9 5ba9 7d7f | %.I.[.}. | |
643 // | Decoded: | |
644 // | custom-key | |
645 // 89 | Literal value (len = 12) | |
646 // | Huffman encoded: | |
647 // 25a8 49e9 5bb8 e8b4 bf | %.I.[.... | |
648 // | Decoded: | |
649 // | custom-value | |
650 // | -> custom-key: custom-value | |
651 SpdyString third = a2b_hex( | |
652 "828785bf408825a849e95ba97d7f89" | |
653 "25a849e95bb8e8b4bf"); | |
654 const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third); | |
655 | |
656 EXPECT_THAT(third_header_set, | |
657 ElementsAre(Pair(":method", "GET"), Pair(":scheme", "https"), | |
658 Pair(":path", "/index.html"), | |
659 Pair(":authority", "www.example.com"), | |
660 Pair("custom-key", "custom-value"))); | |
661 | |
662 expectEntry(62, 54, "custom-key", "custom-value"); | |
663 expectEntry(63, 53, "cache-control", "no-cache"); | |
664 expectEntry(64, 57, ":authority", "www.example.com"); | |
665 EXPECT_EQ(164u, decoder_peer_.header_table()->size()); | |
666 } | |
667 | |
668 // RFC 7541, Section C.6: Response Examples with Huffman Coding | |
669 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.6 | |
670 TEST_P(HpackDecoderTest, SectionC6ResponseHuffmanExamples) { | |
671 decoder_.ApplyHeaderTableSizeSetting(256); | |
672 | |
673 // 48 | == Literal indexed == | |
674 // | Indexed name (idx = 8) | |
675 // | :status | |
676 // 82 | Literal value (len = 3) | |
677 // | Huffman encoded: | |
678 // 6402 | d. | |
679 // | Decoded: | |
680 // | 302 | |
681 // | -> :status: 302 | |
682 // 58 | == Literal indexed == | |
683 // | Indexed name (idx = 24) | |
684 // | cache-control | |
685 // 85 | Literal value (len = 7) | |
686 // | Huffman encoded: | |
687 // aec3 771a 4b | ..w.K | |
688 // | Decoded: | |
689 // | private | |
690 // | -> cache-control: private | |
691 // 61 | == Literal indexed == | |
692 // | Indexed name (idx = 33) | |
693 // | date | |
694 // 96 | Literal value (len = 29) | |
695 // | Huffman encoded: | |
696 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f | |
697 // e082 a62d 1bff | ...-.. | |
698 // | Decoded: | |
699 // | Mon, 21 Oct 2013 20:13:21 | |
700 // | GMT | |
701 // | -> date: Mon, 21 Oct 2013 | |
702 // | 20:13:21 GMT | |
703 // 6e | == Literal indexed == | |
704 // | Indexed name (idx = 46) | |
705 // | location | |
706 // 91 | Literal value (len = 23) | |
707 // | Huffman encoded: | |
708 // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C | |
709 // d3 | . | |
710 // | Decoded: | |
711 // | https://www.example.com | |
712 // | -> location: https://www.e | |
713 // | xample.com | |
714 | |
715 SpdyString first = a2b_hex( | |
716 "488264025885aec3771a4b6196d07abe" | |
717 "941054d444a8200595040b8166e082a6" | |
718 "2d1bff6e919d29ad171863c78f0b97c8" | |
719 "e9ae82ae43d3"); | |
720 const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first); | |
721 | |
722 EXPECT_THAT( | |
723 first_header_set, | |
724 ElementsAre(Pair(":status", "302"), Pair("cache-control", "private"), | |
725 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), | |
726 Pair("location", "https://www.example.com"))); | |
727 | |
728 expectEntry(62, 63, "location", "https://www.example.com"); | |
729 expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); | |
730 expectEntry(64, 52, "cache-control", "private"); | |
731 expectEntry(65, 42, ":status", "302"); | |
732 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); | |
733 | |
734 // 48 | == Literal indexed == | |
735 // | Indexed name (idx = 8) | |
736 // | :status | |
737 // 83 | Literal value (len = 3) | |
738 // | Huffman encoded: | |
739 // 640e ff | d.. | |
740 // | Decoded: | |
741 // | 307 | |
742 // | - evict: :status: 302 | |
743 // | -> :status: 307 | |
744 // c1 | == Indexed - Add == | |
745 // | idx = 65 | |
746 // | -> cache-control: private | |
747 // c0 | == Indexed - Add == | |
748 // | idx = 64 | |
749 // | -> date: Mon, 21 Oct 2013 | |
750 // | 20:13:21 GMT | |
751 // bf | == Indexed - Add == | |
752 // | idx = 63 | |
753 // | -> location: | |
754 // | https://www.example.com | |
755 SpdyString second = a2b_hex("4883640effc1c0bf"); | |
756 const SpdyHeaderBlock& second_header_set = | |
757 DecodeBlockExpectingSuccess(second); | |
758 | |
759 EXPECT_THAT( | |
760 second_header_set, | |
761 ElementsAre(Pair(":status", "307"), Pair("cache-control", "private"), | |
762 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), | |
763 Pair("location", "https://www.example.com"))); | |
764 | |
765 expectEntry(62, 42, ":status", "307"); | |
766 expectEntry(63, 63, "location", "https://www.example.com"); | |
767 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); | |
768 expectEntry(65, 52, "cache-control", "private"); | |
769 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); | |
770 | |
771 // 88 | == Indexed - Add == | |
772 // | idx = 8 | |
773 // | -> :status: 200 | |
774 // c1 | == Indexed - Add == | |
775 // | idx = 65 | |
776 // | -> cache-control: private | |
777 // 61 | == Literal indexed == | |
778 // | Indexed name (idx = 33) | |
779 // | date | |
780 // 96 | Literal value (len = 22) | |
781 // | Huffman encoded: | |
782 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f | |
783 // e084 a62d 1bff | ...-.. | |
784 // | Decoded: | |
785 // | Mon, 21 Oct 2013 20:13:22 | |
786 // | GMT | |
787 // | - evict: cache-control: | |
788 // | private | |
789 // | -> date: Mon, 21 Oct 2013 | |
790 // | 20:13:22 GMT | |
791 // c0 | == Indexed - Add == | |
792 // | idx = 64 | |
793 // | -> location: | |
794 // | https://www.example.com | |
795 // 5a | == Literal indexed == | |
796 // | Indexed name (idx = 26) | |
797 // | content-encoding | |
798 // 83 | Literal value (len = 3) | |
799 // | Huffman encoded: | |
800 // 9bd9 ab | ... | |
801 // | Decoded: | |
802 // | gzip | |
803 // | - evict: date: Mon, 21 Oct | |
804 // | 2013 20:13:21 GMT | |
805 // | -> content-encoding: gzip | |
806 // 77 | == Literal indexed == | |
807 // | Indexed name (idx = 55) | |
808 // | set-cookie | |
809 // ad | Literal value (len = 45) | |
810 // | Huffman encoded: | |
811 // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9` | |
812 // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)... | |
813 // 3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P. | |
814 // | Decoded: | |
815 // | foo=ASDJKHQKBZXOQWEOPIUAXQ | |
816 // | WEOIU; max-age=3600; versi | |
817 // | on=1 | |
818 // | - evict: location: | |
819 // | https://www.example.com | |
820 // | - evict: :status: 307 | |
821 // | -> set-cookie: foo=ASDJKHQ | |
822 // | KBZXOQWEOPIUAXQWEOIU; | |
823 // | max-age=3600; version=1 | |
824 SpdyString third = a2b_hex( | |
825 "88c16196d07abe941054d444a8200595" | |
826 "040b8166e084a62d1bffc05a839bd9ab" | |
827 "77ad94e7821dd7f2e6c7b335dfdfcd5b" | |
828 "3960d5af27087f3672c1ab270fb5291f" | |
829 "9587316065c003ed4ee5b1063d5007"); | |
830 const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third); | |
831 | |
832 EXPECT_THAT( | |
833 third_header_set, | |
834 ElementsAre(Pair(":status", "200"), Pair("cache-control", "private"), | |
835 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), | |
836 Pair("location", "https://www.example.com"), | |
837 Pair("content-encoding", "gzip"), | |
838 Pair("set-cookie", | |
839 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" | |
840 " max-age=3600; version=1"))); | |
841 | |
842 expectEntry(62, 98, "set-cookie", | |
843 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" | |
844 " max-age=3600; version=1"); | |
845 expectEntry(63, 52, "content-encoding", "gzip"); | |
846 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT"); | |
847 EXPECT_EQ(215u, decoder_peer_.header_table()->size()); | |
848 } | |
849 | |
850 } // namespace | |
851 } // namespace test | |
852 } // namespace net | |
OLD | NEW |