blob: 743d0d908861157e8913916480f5091d6d1e1420 [file] [log] [blame]
QUICHE teamfd50a402018-12-07 22:54:05 -05001// Copyright 2016 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/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h"
6
7// Tests of HpackDecoderState.
8
9#include <utility>
10#include <vector>
11
QUICHE teamfd50a402018-12-07 22:54:05 -050012#include "testing/gmock/include/gmock/gmock.h"
13#include "testing/gtest/include/gtest/gtest.h"
14#include "net/third_party/quiche/src/http2/hpack/hpack_string.h"
15#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
16#include "net/third_party/quiche/src/http2/http2_constants.h"
QUICHE team61940b42019-03-07 23:32:27 -050017#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
QUICHE teamfd50a402018-12-07 22:54:05 -050018#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
bnc74646d12019-12-13 09:21:19 -080019#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
QUICHE teamfd50a402018-12-07 22:54:05 -050020
21using ::testing::AssertionResult;
22using ::testing::AssertionSuccess;
23using ::testing::Eq;
24using ::testing::HasSubstr;
25using ::testing::Mock;
26using ::testing::StrictMock;
27
28namespace http2 {
29namespace test {
30class HpackDecoderStatePeer {
31 public:
32 static HpackDecoderTables* GetDecoderTables(HpackDecoderState* state) {
33 return &state->decoder_tables_;
34 }
35};
36
37namespace {
38
39class MockHpackDecoderListener : public HpackDecoderListener {
40 public:
41 MOCK_METHOD0(OnHeaderListStart, void());
42 MOCK_METHOD3(OnHeader,
43 void(HpackEntryType entry_type,
44 const HpackString& name,
45 const HpackString& value));
46 MOCK_METHOD0(OnHeaderListEnd, void());
bnc74646d12019-12-13 09:21:19 -080047 MOCK_METHOD1(OnHeaderErrorDetected,
48 void(quiche::QuicheStringPiece error_message));
QUICHE teamfd50a402018-12-07 22:54:05 -050049};
50
51enum StringBacking { STATIC, UNBUFFERED, BUFFERED };
52
53class HpackDecoderStateTest : public ::testing::Test {
54 protected:
55 HpackDecoderStateTest() : decoder_state_(&listener_) {}
56
57 HpackDecoderTables* GetDecoderTables() {
58 return HpackDecoderStatePeer::GetDecoderTables(&decoder_state_);
59 }
60
61 const HpackStringPair* Lookup(size_t index) {
62 return GetDecoderTables()->Lookup(index);
63 }
64
65 size_t current_header_table_size() {
66 return GetDecoderTables()->current_header_table_size();
67 }
68
69 size_t header_table_size_limit() {
70 return GetDecoderTables()->header_table_size_limit();
71 }
72
73 void set_header_table_size_limit(size_t size) {
74 GetDecoderTables()->DynamicTableSizeUpdate(size);
75 }
76
77 void SetStringBuffer(const char* s,
78 StringBacking backing,
79 HpackDecoderStringBuffer* string_buffer) {
80 switch (backing) {
81 case STATIC:
82 string_buffer->Set(s, true);
83 break;
84 case UNBUFFERED:
85 string_buffer->Set(s, false);
86 break;
87 case BUFFERED:
88 string_buffer->Set(s, false);
89 string_buffer->BufferStringIfUnbuffered();
90 break;
91 }
92 }
93
94 void SetName(const char* s, StringBacking backing) {
95 SetStringBuffer(s, backing, &name_buffer_);
96 }
97
98 void SetValue(const char* s, StringBacking backing) {
99 SetStringBuffer(s, backing, &value_buffer_);
100 }
101
102 void SendStartAndVerifyCallback() {
103 EXPECT_CALL(listener_, OnHeaderListStart());
104 decoder_state_.OnHeaderBlockStart();
105 Mock::VerifyAndClearExpectations(&listener_);
106 }
107
108 void SendSizeUpdate(size_t size) {
109 decoder_state_.OnDynamicTableSizeUpdate(size);
110 Mock::VerifyAndClearExpectations(&listener_);
111 }
112
113 void SendIndexAndVerifyCallback(size_t index,
114 HpackEntryType expected_type,
115 const char* expected_name,
116 const char* expected_value) {
117 EXPECT_CALL(listener_,
118 OnHeader(expected_type, Eq(expected_name), Eq(expected_value)));
119 decoder_state_.OnIndexedHeader(index);
120 Mock::VerifyAndClearExpectations(&listener_);
121 }
122
123 void SendValueAndVerifyCallback(size_t name_index,
124 HpackEntryType entry_type,
125 const char* name,
126 const char* value,
127 StringBacking value_backing) {
128 SetValue(value, value_backing);
129 EXPECT_CALL(listener_, OnHeader(entry_type, Eq(name), Eq(value)));
130 decoder_state_.OnNameIndexAndLiteralValue(entry_type, name_index,
131 &value_buffer_);
132 Mock::VerifyAndClearExpectations(&listener_);
133 }
134
135 void SendNameAndValueAndVerifyCallback(HpackEntryType entry_type,
136 const char* name,
137 StringBacking name_backing,
138 const char* value,
139 StringBacking value_backing) {
140 SetName(name, name_backing);
141 SetValue(value, value_backing);
142 EXPECT_CALL(listener_, OnHeader(entry_type, Eq(name), Eq(value)));
143 decoder_state_.OnLiteralNameAndValue(entry_type, &name_buffer_,
144 &value_buffer_);
145 Mock::VerifyAndClearExpectations(&listener_);
146 }
147
148 void SendEndAndVerifyCallback() {
149 EXPECT_CALL(listener_, OnHeaderListEnd());
150 decoder_state_.OnHeaderBlockEnd();
151 Mock::VerifyAndClearExpectations(&listener_);
152 }
153
154 // dynamic_index is one-based, because that is the way RFC 7541 shows it.
155 AssertionResult VerifyEntry(size_t dynamic_index,
156 const char* name,
157 const char* value) {
158 const HpackStringPair* entry =
159 Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
160 VERIFY_NE(entry, nullptr);
161 VERIFY_EQ(entry->name.ToStringPiece(), name);
162 VERIFY_EQ(entry->value.ToStringPiece(), value);
163 return AssertionSuccess();
164 }
165 AssertionResult VerifyNoEntry(size_t dynamic_index) {
166 const HpackStringPair* entry =
167 Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
168 VERIFY_EQ(entry, nullptr);
169 return AssertionSuccess();
170 }
171 AssertionResult VerifyDynamicTableContents(
172 const std::vector<std::pair<const char*, const char*>>& entries) {
173 size_t index = 1;
174 for (const auto& entry : entries) {
175 VERIFY_SUCCESS(VerifyEntry(index, entry.first, entry.second));
176 ++index;
177 }
178 VERIFY_SUCCESS(VerifyNoEntry(index));
179 return AssertionSuccess();
180 }
181
182 StrictMock<MockHpackDecoderListener> listener_;
183 HpackDecoderState decoder_state_;
184 HpackDecoderStringBuffer name_buffer_, value_buffer_;
185};
186
187// Test based on RFC 7541, section C.3: Request Examples without Huffman Coding.
188// This section shows several consecutive header lists, corresponding to HTTP
189// requests, on the same connection.
190TEST_F(HpackDecoderStateTest, C3_RequestExamples) {
191 // C.3.1 First Request
192 //
193 // Header list to encode:
194 //
195 // :method: GET
196 // :scheme: http
197 // :path: /
198 // :authority: www.example.com
199
200 SendStartAndVerifyCallback();
201 SendIndexAndVerifyCallback(2, HpackEntryType::kIndexedHeader, ":method",
202 "GET");
203 SendIndexAndVerifyCallback(6, HpackEntryType::kIndexedHeader, ":scheme",
204 "http");
205 SendIndexAndVerifyCallback(4, HpackEntryType::kIndexedHeader, ":path", "/");
206 SendValueAndVerifyCallback(1, HpackEntryType::kIndexedLiteralHeader,
207 ":authority", "www.example.com", UNBUFFERED);
208 SendEndAndVerifyCallback();
209
210 // Dynamic Table (after decoding):
211 //
212 // [ 1] (s = 57) :authority: www.example.com
213 // Table size: 57
214
215 ASSERT_TRUE(VerifyDynamicTableContents({{":authority", "www.example.com"}}));
216 ASSERT_EQ(57u, current_header_table_size());
217
218 // C.3.2 Second Request
219 //
220 // Header list to encode:
221 //
222 // :method: GET
223 // :scheme: http
224 // :path: /
225 // :authority: www.example.com
226 // cache-control: no-cache
227
228 SendStartAndVerifyCallback();
229 SendIndexAndVerifyCallback(2, HpackEntryType::kIndexedHeader, ":method",
230 "GET");
231 SendIndexAndVerifyCallback(6, HpackEntryType::kIndexedHeader, ":scheme",
232 "http");
233 SendIndexAndVerifyCallback(4, HpackEntryType::kIndexedHeader, ":path", "/");
234 SendIndexAndVerifyCallback(62, HpackEntryType::kIndexedHeader, ":authority",
235 "www.example.com");
236 SendValueAndVerifyCallback(24, HpackEntryType::kIndexedLiteralHeader,
237 "cache-control", "no-cache", UNBUFFERED);
238 SendEndAndVerifyCallback();
239
240 // Dynamic Table (after decoding):
241 //
242 // [ 1] (s = 53) cache-control: no-cache
243 // [ 2] (s = 57) :authority: www.example.com
244 // Table size: 110
245
246 ASSERT_TRUE(VerifyDynamicTableContents(
247 {{"cache-control", "no-cache"}, {":authority", "www.example.com"}}));
248 ASSERT_EQ(110u, current_header_table_size());
249
250 // C.3.3 Third Request
251 //
252 // Header list to encode:
253 //
254 // :method: GET
255 // :scheme: https
256 // :path: /index.html
257 // :authority: www.example.com
258 // custom-key: custom-value
259
260 SendStartAndVerifyCallback();
261 SendIndexAndVerifyCallback(2, HpackEntryType::kIndexedHeader, ":method",
262 "GET");
263 SendIndexAndVerifyCallback(7, HpackEntryType::kIndexedHeader, ":scheme",
264 "https");
265 SendIndexAndVerifyCallback(5, HpackEntryType::kIndexedHeader, ":path",
266 "/index.html");
267 SendIndexAndVerifyCallback(63, HpackEntryType::kIndexedHeader, ":authority",
268 "www.example.com");
269 SendNameAndValueAndVerifyCallback(HpackEntryType::kIndexedLiteralHeader,
270 "custom-key", UNBUFFERED, "custom-value",
271 UNBUFFERED);
272 SendEndAndVerifyCallback();
273
274 // Dynamic Table (after decoding):
275 //
276 // [ 1] (s = 54) custom-key: custom-value
277 // [ 2] (s = 53) cache-control: no-cache
278 // [ 3] (s = 57) :authority: www.example.com
279 // Table size: 164
280
281 ASSERT_TRUE(VerifyDynamicTableContents({{"custom-key", "custom-value"},
282 {"cache-control", "no-cache"},
283 {":authority", "www.example.com"}}));
284 ASSERT_EQ(164u, current_header_table_size());
285}
286
287// Test based on RFC 7541, section C.5: Response Examples without Huffman
288// Coding. This section shows several consecutive header lists, corresponding
289// to HTTP responses, on the same connection. The HTTP/2 setting parameter
290// SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 octets, causing
291// some evictions to occur.
292TEST_F(HpackDecoderStateTest, C5_ResponseExamples) {
293 set_header_table_size_limit(256);
294
295 // C.5.1 First Response
296 //
297 // Header list to encode:
298 //
299 // :status: 302
300 // cache-control: private
301 // date: Mon, 21 Oct 2013 20:13:21 GMT
302 // location: https://www.example.com
303
304 SendStartAndVerifyCallback();
305 SendValueAndVerifyCallback(8, HpackEntryType::kIndexedLiteralHeader,
306 ":status", "302", BUFFERED);
307 SendValueAndVerifyCallback(24, HpackEntryType::kIndexedLiteralHeader,
308 "cache-control", "private", UNBUFFERED);
309 SendValueAndVerifyCallback(33, HpackEntryType::kIndexedLiteralHeader, "date",
310 "Mon, 21 Oct 2013 20:13:21 GMT", UNBUFFERED);
311 SendValueAndVerifyCallback(46, HpackEntryType::kIndexedLiteralHeader,
312 "location", "https://www.example.com", UNBUFFERED);
313 SendEndAndVerifyCallback();
314
315 // Dynamic Table (after decoding):
316 //
317 // [ 1] (s = 63) location: https://www.example.com
318 // [ 2] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
319 // [ 3] (s = 52) cache-control: private
320 // [ 4] (s = 42) :status: 302
321 // Table size: 222
322
323 ASSERT_TRUE(
324 VerifyDynamicTableContents({{"location", "https://www.example.com"},
325 {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
326 {"cache-control", "private"},
327 {":status", "302"}}));
328 ASSERT_EQ(222u, current_header_table_size());
329
330 // C.5.2 Second Response
331 //
332 // The (":status", "302") header field is evicted from the dynamic table to
333 // free space to allow adding the (":status", "307") header field.
334 //
335 // Header list to encode:
336 //
337 // :status: 307
338 // cache-control: private
339 // date: Mon, 21 Oct 2013 20:13:21 GMT
340 // location: https://www.example.com
341
342 SendStartAndVerifyCallback();
343 SendValueAndVerifyCallback(8, HpackEntryType::kIndexedLiteralHeader,
344 ":status", "307", BUFFERED);
345 SendIndexAndVerifyCallback(65, HpackEntryType::kIndexedHeader,
346 "cache-control", "private");
347 SendIndexAndVerifyCallback(64, HpackEntryType::kIndexedHeader, "date",
348 "Mon, 21 Oct 2013 20:13:21 GMT");
349 SendIndexAndVerifyCallback(63, HpackEntryType::kIndexedHeader, "location",
350 "https://www.example.com");
351 SendEndAndVerifyCallback();
352
353 // Dynamic Table (after decoding):
354 //
355 // [ 1] (s = 42) :status: 307
356 // [ 2] (s = 63) location: https://www.example.com
357 // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
358 // [ 4] (s = 52) cache-control: private
359 // Table size: 222
360
361 ASSERT_TRUE(
362 VerifyDynamicTableContents({{":status", "307"},
363 {"location", "https://www.example.com"},
364 {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
365 {"cache-control", "private"}}));
366 ASSERT_EQ(222u, current_header_table_size());
367
368 // C.5.3 Third Response
369 //
370 // Several header fields are evicted from the dynamic table during the
371 // processing of this header list.
372 //
373 // Header list to encode:
374 //
375 // :status: 200
376 // cache-control: private
377 // date: Mon, 21 Oct 2013 20:13:22 GMT
378 // location: https://www.example.com
379 // content-encoding: gzip
380 // set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1
381
382 SendStartAndVerifyCallback();
383 SendIndexAndVerifyCallback(8, HpackEntryType::kIndexedHeader, ":status",
384 "200");
385 SendIndexAndVerifyCallback(65, HpackEntryType::kIndexedHeader,
386 "cache-control", "private");
387 SendValueAndVerifyCallback(33, HpackEntryType::kIndexedLiteralHeader, "date",
388 "Mon, 21 Oct 2013 20:13:22 GMT", BUFFERED);
389 SendIndexAndVerifyCallback(64, HpackEntryType::kIndexedHeader, "location",
390 "https://www.example.com");
391 SendValueAndVerifyCallback(26, HpackEntryType::kIndexedLiteralHeader,
392 "content-encoding", "gzip", UNBUFFERED);
393 SendValueAndVerifyCallback(
394 55, HpackEntryType::kIndexedLiteralHeader, "set-cookie",
395 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", BUFFERED);
396 SendEndAndVerifyCallback();
397
398 // Dynamic Table (after decoding):
399 //
400 // [ 1] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;
401 // max-age=3600; version=1
402 // [ 2] (s = 52) content-encoding: gzip
403 // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT
404 // Table size: 215
405
406 ASSERT_TRUE(VerifyDynamicTableContents(
407 {{"set-cookie",
408 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
409 {"content-encoding", "gzip"},
410 {"date", "Mon, 21 Oct 2013 20:13:22 GMT"}}));
411 ASSERT_EQ(215u, current_header_table_size());
412}
413
414// Confirm that the table size can be changed, but at most twice.
415TEST_F(HpackDecoderStateTest, OptionalTableSizeChanges) {
416 SendStartAndVerifyCallback();
417 EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
418 header_table_size_limit());
419 SendSizeUpdate(1024);
420 EXPECT_EQ(1024u, header_table_size_limit());
421 SendSizeUpdate(0);
422 EXPECT_EQ(0u, header_table_size_limit());
423
424 // Three updates aren't allowed.
425 EXPECT_CALL(listener_,
426 OnHeaderErrorDetected(HasSubstr("size update not allowed")));
427 SendSizeUpdate(0);
428}
429
430// Confirm that required size updates are indeed required before headers.
431TEST_F(HpackDecoderStateTest, RequiredTableSizeChangeBeforeHeader) {
432 decoder_state_.ApplyHeaderTableSizeSetting(1024);
433 decoder_state_.ApplyHeaderTableSizeSetting(2048);
434
435 // First provide the required update, and an allowed second update.
436 SendStartAndVerifyCallback();
437 EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
438 header_table_size_limit());
439 SendSizeUpdate(1024);
440 EXPECT_EQ(1024u, header_table_size_limit());
441 SendSizeUpdate(1500);
442 EXPECT_EQ(1500u, header_table_size_limit());
443 SendEndAndVerifyCallback();
444
445 // Another HPACK block, but this time missing the required size update.
446 decoder_state_.ApplyHeaderTableSizeSetting(1024);
447 SendStartAndVerifyCallback();
448 EXPECT_CALL(listener_, OnHeaderErrorDetected(
449 HasSubstr("Missing dynamic table size update")));
450 decoder_state_.OnIndexedHeader(1);
451
452 // Further decoded entries are ignored.
453 decoder_state_.OnIndexedHeader(1);
454 decoder_state_.OnDynamicTableSizeUpdate(1);
455 SetValue("value", UNBUFFERED);
456 decoder_state_.OnNameIndexAndLiteralValue(
457 HpackEntryType::kIndexedLiteralHeader, 4, &value_buffer_);
458 SetName("name", UNBUFFERED);
459 decoder_state_.OnLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
460 &name_buffer_, &value_buffer_);
461 decoder_state_.OnHeaderBlockEnd();
462 decoder_state_.OnHpackDecodeError("NOT FORWARDED");
463}
464
465// Confirm that required size updates are validated.
466TEST_F(HpackDecoderStateTest, InvalidRequiredSizeUpdate) {
467 // Require a size update, but provide one that isn't small enough.
468 decoder_state_.ApplyHeaderTableSizeSetting(1024);
469 SendStartAndVerifyCallback();
470 EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
471 header_table_size_limit());
472 EXPECT_CALL(listener_,
473 OnHeaderErrorDetected(HasSubstr("above low water mark")));
474 SendSizeUpdate(2048);
475}
476
477// Confirm that required size updates are indeed required before the end.
478TEST_F(HpackDecoderStateTest, RequiredTableSizeChangeBeforeEnd) {
479 decoder_state_.ApplyHeaderTableSizeSetting(1024);
480 SendStartAndVerifyCallback();
481 EXPECT_CALL(listener_, OnHeaderErrorDetected(
482 HasSubstr("Missing dynamic table size update")));
483 decoder_state_.OnHeaderBlockEnd();
484}
485
486// Confirm that optional size updates are validated.
487TEST_F(HpackDecoderStateTest, InvalidOptionalSizeUpdate) {
488 // Require a size update, but provide one that isn't small enough.
489 SendStartAndVerifyCallback();
490 EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
491 header_table_size_limit());
492 EXPECT_CALL(listener_,
493 OnHeaderErrorDetected(HasSubstr("size update is above")));
494 SendSizeUpdate(Http2SettingsInfo::DefaultHeaderTableSize() + 1);
495}
496
497TEST_F(HpackDecoderStateTest, InvalidStaticIndex) {
498 SendStartAndVerifyCallback();
499 EXPECT_CALL(listener_, OnHeaderErrorDetected(HasSubstr("Invalid index")));
500 decoder_state_.OnIndexedHeader(0);
501}
502
503TEST_F(HpackDecoderStateTest, InvalidDynamicIndex) {
504 SendStartAndVerifyCallback();
505 EXPECT_CALL(listener_, OnHeaderErrorDetected(HasSubstr("Invalid index")));
506 decoder_state_.OnIndexedHeader(kFirstDynamicTableIndex);
507}
508
509TEST_F(HpackDecoderStateTest, InvalidNameIndex) {
510 SendStartAndVerifyCallback();
511 EXPECT_CALL(listener_,
512 OnHeaderErrorDetected(HasSubstr("Invalid name index")));
513 SetValue("value", UNBUFFERED);
514 decoder_state_.OnNameIndexAndLiteralValue(
515 HpackEntryType::kIndexedLiteralHeader, kFirstDynamicTableIndex,
516 &value_buffer_);
517}
518
519TEST_F(HpackDecoderStateTest, ErrorsSuppressCallbacks) {
520 SendStartAndVerifyCallback();
bnc74646d12019-12-13 09:21:19 -0800521 EXPECT_CALL(listener_, OnHeaderErrorDetected(quiche::QuicheStringPiece(
522 "Huffman decode error.")));
QUICHE teamfd50a402018-12-07 22:54:05 -0500523 decoder_state_.OnHpackDecodeError("Huffman decode error.");
524
525 // Further decoded entries are ignored.
526 decoder_state_.OnIndexedHeader(1);
527 decoder_state_.OnDynamicTableSizeUpdate(1);
528 SetValue("value", UNBUFFERED);
529 decoder_state_.OnNameIndexAndLiteralValue(
530 HpackEntryType::kIndexedLiteralHeader, 4, &value_buffer_);
531 SetName("name", UNBUFFERED);
532 decoder_state_.OnLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
533 &name_buffer_, &value_buffer_);
534 decoder_state_.OnHeaderBlockEnd();
535 decoder_state_.OnHpackDecodeError("NOT FORWARDED");
536}
537
538} // namespace
539} // namespace test
540} // namespace http2