Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(520)

Side by Side Diff: net/http2/hpack/decoder/hpack_decoder_state_test.cc

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

Powered by Google App Engine
This is Rietveld 408576698