QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 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/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h" |
| 6 | |
| 7 | // Tests of HpackBlockDecoder. |
| 8 | |
| 9 | #include <cstdint> |
bnc | 4790400 | 2019-08-16 11:49:48 -0700 | [diff] [blame] | 10 | #include <string> |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 11 | |
| 12 | #include "testing/gtest/include/gtest/gtest.h" |
| 13 | #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" |
| 14 | #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h" |
| 15 | #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" |
| 16 | #include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h" |
| 17 | #include "net/third_party/quiche/src/http2/hpack/tools/hpack_example.h" |
| 18 | #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" |
| 19 | #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" |
| 20 | #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" |
| 21 | #include "net/third_party/quiche/src/http2/tools/random_decoder_test.h" |
| 22 | |
| 23 | using ::testing::AssertionSuccess; |
| 24 | |
| 25 | namespace http2 { |
| 26 | namespace test { |
| 27 | namespace { |
| 28 | |
| 29 | class HpackBlockDecoderTest : public RandomDecoderTest { |
| 30 | protected: |
| 31 | HpackBlockDecoderTest() : listener_(&collector_), decoder_(&listener_) { |
| 32 | stop_decode_on_done_ = false; |
| 33 | decoder_.Reset(); |
| 34 | // Make sure logging doesn't crash. Not examining the result. |
| 35 | std::ostringstream strm; |
| 36 | strm << decoder_; |
| 37 | } |
| 38 | |
| 39 | DecodeStatus StartDecoding(DecodeBuffer* db) override { |
| 40 | collector_.Clear(); |
| 41 | decoder_.Reset(); |
| 42 | return ResumeDecoding(db); |
| 43 | } |
| 44 | |
| 45 | DecodeStatus ResumeDecoding(DecodeBuffer* db) override { |
| 46 | DecodeStatus status = decoder_.Decode(db); |
| 47 | |
| 48 | // Make sure logging doesn't crash. Not examining the result. |
| 49 | std::ostringstream strm; |
| 50 | strm << decoder_; |
| 51 | |
| 52 | return status; |
| 53 | } |
| 54 | |
| 55 | AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db, |
| 56 | const Validator& validator) { |
| 57 | bool return_non_zero_on_first = false; |
| 58 | return RandomDecoderTest::DecodeAndValidateSeveralWays( |
| 59 | db, return_non_zero_on_first, validator); |
| 60 | } |
| 61 | |
| 62 | AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb, |
| 63 | const Validator& validator) { |
| 64 | DecodeBuffer db(hbb.buffer()); |
| 65 | return DecodeAndValidateSeveralWays(&db, validator); |
| 66 | } |
| 67 | |
| 68 | AssertionResult DecodeHpackExampleAndValidateSeveralWays( |
| 69 | Http2StringPiece hpack_example, |
| 70 | Validator validator) { |
bnc | 4790400 | 2019-08-16 11:49:48 -0700 | [diff] [blame] | 71 | std::string input = HpackExampleToStringOrDie(hpack_example); |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 72 | DecodeBuffer db(input); |
| 73 | return DecodeAndValidateSeveralWays(&db, validator); |
| 74 | } |
| 75 | |
| 76 | uint8_t Rand8() { return Random().Rand8(); } |
| 77 | |
bnc | 4790400 | 2019-08-16 11:49:48 -0700 | [diff] [blame] | 78 | std::string Rand8String() { return Random().RandString(Rand8()); } |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 79 | |
| 80 | HpackBlockCollector collector_; |
| 81 | HpackEntryDecoderVLoggingListener listener_; |
| 82 | HpackBlockDecoder decoder_; |
| 83 | }; |
| 84 | |
| 85 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.1 |
| 86 | TEST_F(HpackBlockDecoderTest, SpecExample_C_2_1) { |
| 87 | auto do_check = [this]() { |
| 88 | VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralNameValueHeader( |
| 89 | HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false, |
| 90 | "custom-header")); |
| 91 | }; |
| 92 | const char hpack_example[] = R"( |
| 93 | 40 | == Literal indexed == |
| 94 | 0a | Literal name (len = 10) |
| 95 | 6375 7374 6f6d 2d6b 6579 | custom-key |
| 96 | 0d | Literal value (len = 13) |
| 97 | 6375 7374 6f6d 2d68 6561 6465 72 | custom-header |
| 98 | | -> custom-key: |
| 99 | | custom-header |
| 100 | )"; |
| 101 | EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays( |
| 102 | hpack_example, ValidateDoneAndEmpty(do_check))); |
| 103 | EXPECT_TRUE(do_check()); |
| 104 | } |
| 105 | |
| 106 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.2 |
| 107 | TEST_F(HpackBlockDecoderTest, SpecExample_C_2_2) { |
| 108 | auto do_check = [this]() { |
| 109 | VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralValueHeader( |
| 110 | HpackEntryType::kUnindexedLiteralHeader, 4, false, "/sample/path")); |
| 111 | }; |
| 112 | const char hpack_example[] = R"( |
| 113 | 04 | == Literal not indexed == |
| 114 | | Indexed name (idx = 4) |
| 115 | | :path |
| 116 | 0c | Literal value (len = 12) |
| 117 | 2f73 616d 706c 652f 7061 7468 | /sample/path |
| 118 | | -> :path: /sample/path |
| 119 | )"; |
| 120 | EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays( |
| 121 | hpack_example, ValidateDoneAndEmpty(do_check))); |
| 122 | EXPECT_TRUE(do_check()); |
| 123 | } |
| 124 | |
| 125 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.3 |
| 126 | TEST_F(HpackBlockDecoderTest, SpecExample_C_2_3) { |
| 127 | auto do_check = [this]() { |
| 128 | VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralNameValueHeader( |
| 129 | HpackEntryType::kNeverIndexedLiteralHeader, false, "password", false, |
| 130 | "secret")); |
| 131 | }; |
| 132 | const char hpack_example[] = R"( |
| 133 | 10 | == Literal never indexed == |
| 134 | 08 | Literal name (len = 8) |
| 135 | 7061 7373 776f 7264 | password |
| 136 | 06 | Literal value (len = 6) |
| 137 | 7365 6372 6574 | secret |
| 138 | | -> password: secret |
| 139 | )"; |
| 140 | EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays( |
| 141 | hpack_example, ValidateDoneAndEmpty(do_check))); |
| 142 | EXPECT_TRUE(do_check()); |
| 143 | } |
| 144 | |
| 145 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.4 |
| 146 | TEST_F(HpackBlockDecoderTest, SpecExample_C_2_4) { |
| 147 | auto do_check = [this]() { |
| 148 | VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleIndexedHeader(2)); |
| 149 | }; |
| 150 | const char hpack_example[] = R"( |
| 151 | 82 | == Indexed - Add == |
| 152 | | idx = 2 |
| 153 | | -> :method: GET |
| 154 | )"; |
| 155 | EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays( |
| 156 | hpack_example, ValidateDoneAndEmpty(do_check))); |
| 157 | EXPECT_TRUE(do_check()); |
| 158 | } |
| 159 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1 |
| 160 | TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) { |
bnc | 4790400 | 2019-08-16 11:49:48 -0700 | [diff] [blame] | 161 | std::string example = R"( |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 162 | 82 | == Indexed - Add == |
| 163 | | idx = 2 |
| 164 | | -> :method: GET |
| 165 | 86 | == Indexed - Add == |
| 166 | | idx = 6 |
| 167 | | -> :scheme: http |
| 168 | 84 | == Indexed - Add == |
| 169 | | idx = 4 |
| 170 | | -> :path: / |
| 171 | 41 | == Literal indexed == |
| 172 | | Indexed name (idx = 1) |
| 173 | | :authority |
| 174 | 0f | Literal value (len = 15) |
| 175 | 7777 772e 6578 616d 706c 652e 636f 6d | www.example.com |
| 176 | | -> :authority: |
| 177 | | www.example.com |
| 178 | )"; |
| 179 | HpackBlockCollector expected; |
| 180 | expected.ExpectIndexedHeader(2); |
| 181 | expected.ExpectIndexedHeader(6); |
| 182 | expected.ExpectIndexedHeader(4); |
| 183 | expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, |
| 184 | 1, false, "www.example.com"); |
| 185 | NoArgValidator do_check = [expected, this]() { |
| 186 | VERIFY_AND_RETURN_SUCCESS(collector_.VerifyEq(expected)); |
| 187 | }; |
| 188 | EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays( |
| 189 | example, ValidateDoneAndEmpty(do_check))); |
| 190 | EXPECT_TRUE(do_check()); |
| 191 | } |
| 192 | |
| 193 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.5.1 |
| 194 | TEST_F(HpackBlockDecoderTest, SpecExample_C_5_1) { |
bnc | 4790400 | 2019-08-16 11:49:48 -0700 | [diff] [blame] | 195 | std::string example = R"( |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 196 | 48 | == Literal indexed == |
| 197 | | Indexed name (idx = 8) |
| 198 | | :status |
| 199 | 03 | Literal value (len = 3) |
| 200 | 3330 32 | 302 |
| 201 | | -> :status: 302 |
| 202 | 58 | == Literal indexed == |
| 203 | | Indexed name (idx = 24) |
| 204 | | cache-control |
| 205 | 07 | Literal value (len = 7) |
| 206 | 7072 6976 6174 65 | private |
| 207 | | -> cache-control: private |
| 208 | 61 | == Literal indexed == |
| 209 | | Indexed name (idx = 33) |
| 210 | | date |
| 211 | 1d | Literal value (len = 29) |
| 212 | 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013 |
| 213 | 2032 303a 3133 3a32 3120 474d 54 | 20:13:21 GMT |
| 214 | | -> date: Mon, 21 Oct 2013 |
| 215 | | 20:13:21 GMT |
| 216 | 6e | == Literal indexed == |
| 217 | | Indexed name (idx = 46) |
| 218 | | location |
| 219 | 17 | Literal value (len = 23) |
| 220 | 6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam |
| 221 | 706c 652e 636f 6d | ple.com |
| 222 | | -> location: |
| 223 | | https://www.example.com |
| 224 | )"; |
| 225 | HpackBlockCollector expected; |
| 226 | expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, |
| 227 | 8, false, "302"); |
| 228 | expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, |
| 229 | 24, false, "private"); |
| 230 | expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, |
| 231 | 33, false, |
| 232 | "Mon, 21 Oct 2013 20:13:21 GMT"); |
| 233 | expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, |
| 234 | 46, false, "https://www.example.com"); |
| 235 | NoArgValidator do_check = [expected, this]() { |
| 236 | VERIFY_AND_RETURN_SUCCESS(collector_.VerifyEq(expected)); |
| 237 | }; |
| 238 | EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays( |
| 239 | example, ValidateDoneAndEmpty(do_check))); |
| 240 | EXPECT_TRUE(do_check()); |
| 241 | } |
| 242 | |
| 243 | // Generate a bunch of HPACK block entries to expect, use those expectations |
| 244 | // to generate an HPACK block, then decode it and confirm it matches those |
| 245 | // expectations. Some of these are invalid (such as Indexed, with index=0), |
| 246 | // but well-formed, and the decoder doesn't check for validity, just |
| 247 | // well-formedness. That includes the validity of the strings not being checked, |
| 248 | // such as lower-case ascii for the names, and valid Huffman encodings. |
| 249 | TEST_F(HpackBlockDecoderTest, Computed) { |
| 250 | HpackBlockCollector expected; |
| 251 | expected.ExpectIndexedHeader(0); |
| 252 | expected.ExpectIndexedHeader(1); |
| 253 | expected.ExpectIndexedHeader(126); |
| 254 | expected.ExpectIndexedHeader(127); |
| 255 | expected.ExpectIndexedHeader(128); |
| 256 | expected.ExpectDynamicTableSizeUpdate(0); |
| 257 | expected.ExpectDynamicTableSizeUpdate(1); |
| 258 | expected.ExpectDynamicTableSizeUpdate(14); |
| 259 | expected.ExpectDynamicTableSizeUpdate(15); |
| 260 | expected.ExpectDynamicTableSizeUpdate(30); |
| 261 | expected.ExpectDynamicTableSizeUpdate(31); |
| 262 | expected.ExpectDynamicTableSizeUpdate(4095); |
| 263 | expected.ExpectDynamicTableSizeUpdate(4096); |
| 264 | expected.ExpectDynamicTableSizeUpdate(8192); |
| 265 | for (auto type : {HpackEntryType::kIndexedLiteralHeader, |
| 266 | HpackEntryType::kUnindexedLiteralHeader, |
| 267 | HpackEntryType::kNeverIndexedLiteralHeader}) { |
| 268 | for (bool value_huffman : {false, true}) { |
| 269 | // An entry with an index for the name. Ensure the name index |
| 270 | // is not zero by adding one to the Rand8() result. |
| 271 | expected.ExpectNameIndexAndLiteralValue(type, Rand8() + 1, value_huffman, |
| 272 | Rand8String()); |
| 273 | // And two entries with literal names, one plain, one huffman encoded. |
| 274 | expected.ExpectLiteralNameAndValue(type, false, Rand8String(), |
| 275 | value_huffman, Rand8String()); |
| 276 | expected.ExpectLiteralNameAndValue(type, true, Rand8String(), |
| 277 | value_huffman, Rand8String()); |
| 278 | } |
| 279 | } |
| 280 | // Shuffle the entries and serialize them to produce an HPACK block. |
| 281 | expected.ShuffleEntries(RandomPtr()); |
| 282 | HpackBlockBuilder hbb; |
| 283 | expected.AppendToHpackBlockBuilder(&hbb); |
| 284 | |
| 285 | NoArgValidator do_check = [expected, this]() { |
| 286 | VERIFY_AND_RETURN_SUCCESS(collector_.VerifyEq(expected)); |
| 287 | }; |
| 288 | EXPECT_TRUE( |
| 289 | DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check))); |
| 290 | EXPECT_TRUE(do_check()); |
| 291 | } |
| 292 | |
| 293 | } // namespace |
| 294 | } // namespace test |
| 295 | } // namespace http2 |