OLD | NEW |
(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/spdy/hpack/hpack_huffman_decoder.h" |
| 6 |
| 7 #include <bitset> |
| 8 #include <limits> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "base/macros.h" |
| 12 #include "base/rand_util.h" |
| 13 #include "base/strings/string_piece.h" |
| 14 #include "net/spdy/hpack/hpack_constants.h" |
| 15 #include "net/spdy/hpack/hpack_huffman_table.h" |
| 16 #include "net/spdy/hpack/hpack_input_stream.h" |
| 17 #include "net/spdy/hpack/hpack_output_stream.h" |
| 18 #include "net/spdy/spdy_test_utils.h" |
| 19 #include "testing/gtest/include/gtest/gtest.h" |
| 20 |
| 21 using base::StringPiece; |
| 22 |
| 23 namespace net { |
| 24 namespace test { |
| 25 |
| 26 namespace { |
| 27 |
| 28 uint32_t RandUint32() { |
| 29 return static_cast<uint32_t>(base::RandUint64() & 0xffffffff); |
| 30 } |
| 31 |
| 32 } // anonymous namespace |
| 33 |
| 34 // Bits(HuffmanWord) constructs a bitset<32>, which produces nicely formatted |
| 35 // binary numbers when LOG'd. |
| 36 typedef std::bitset<32> Bits; |
| 37 |
| 38 typedef HpackHuffmanDecoder::HuffmanWord HuffmanWord; |
| 39 typedef HpackHuffmanDecoder::HuffmanCodeLength HuffmanCodeLength; |
| 40 |
| 41 class HpackHuffmanDecoderPeer { |
| 42 public: |
| 43 static HuffmanCodeLength CodeLengthOfPrefix(HuffmanWord value) { |
| 44 return HpackHuffmanDecoder::CodeLengthOfPrefix(value); |
| 45 } |
| 46 |
| 47 static HuffmanWord DecodeToCanonical(HuffmanCodeLength code_length, |
| 48 HuffmanWord bits) { |
| 49 return HpackHuffmanDecoder::DecodeToCanonical(code_length, bits); |
| 50 } |
| 51 |
| 52 static char CanonicalToSource(HuffmanWord canonical) { |
| 53 return HpackHuffmanDecoder::CanonicalToSource(canonical); |
| 54 } |
| 55 }; |
| 56 |
| 57 // Tests of the ability to decode the HPACK Huffman Code, defined in: |
| 58 // https://httpwg.github.io/specs/rfc7541.html#huffman.code |
| 59 class HpackHuffmanDecoderTest : public ::testing::Test { |
| 60 protected: |
| 61 HpackHuffmanDecoderTest() : table_(ObtainHpackHuffmanTable()) {} |
| 62 |
| 63 // Since kHpackHuffmanCode doesn't include the canonical symbol value, |
| 64 // this helper helps us to decode directly to the source symbol, allowing |
| 65 // for EOS. |
| 66 uint16_t DecodeToSource(HuffmanCodeLength code_length, HuffmanWord bits) { |
| 67 HuffmanWord canonical = |
| 68 HpackHuffmanDecoderPeer::DecodeToCanonical(code_length, bits); |
| 69 EXPECT_LE(canonical, 256u); |
| 70 if (canonical == 256u) { |
| 71 return canonical; |
| 72 } |
| 73 return static_cast<unsigned char>( |
| 74 HpackHuffmanDecoderPeer::CanonicalToSource(canonical)); |
| 75 } |
| 76 |
| 77 void EncodeString(StringPiece input, std::string* encoded) { |
| 78 HpackOutputStream output_stream; |
| 79 table_.EncodeString(input, &output_stream); |
| 80 encoded->clear(); |
| 81 output_stream.TakeString(encoded); |
| 82 // Verify EncodedSize() agrees with EncodeString(). |
| 83 EXPECT_EQ(encoded->size(), table_.EncodedSize(input)); |
| 84 } |
| 85 |
| 86 std::string EncodeString(StringPiece input) { |
| 87 std::string result; |
| 88 EncodeString(input, &result); |
| 89 return result; |
| 90 } |
| 91 |
| 92 const HpackHuffmanTable& table_; |
| 93 }; |
| 94 |
| 95 TEST_F(HpackHuffmanDecoderTest, CodeLengthOfPrefix) { |
| 96 for (const HpackHuffmanSymbol& entry : HpackHuffmanCode()) { |
| 97 // First confirm our assumption that the low order bits of entry.code |
| 98 // (those not part of the high order entry.length bits) are zero. |
| 99 uint32_t non_code_bits = 0xffffffff >> entry.length; |
| 100 EXPECT_EQ(0u, entry.code & non_code_bits); |
| 101 |
| 102 // entry.code has a code length of entry.length. |
| 103 EXPECT_EQ(entry.length, |
| 104 HpackHuffmanDecoderPeer::CodeLengthOfPrefix(entry.code)) |
| 105 << "Full code: " << Bits(entry.code) << "\n" |
| 106 << " ID: " << entry.id; |
| 107 |
| 108 // Let's try again with all the low order bits set to 1. |
| 109 uint32_t bits = entry.code | (0xffffffff >> entry.length); |
| 110 EXPECT_EQ(entry.length, HpackHuffmanDecoderPeer::CodeLengthOfPrefix(bits)) |
| 111 << "Full code: " << Bits(entry.code) << "\n" |
| 112 << " bits: " << Bits(bits) << "\n" |
| 113 << " ID: " << entry.id; |
| 114 |
| 115 // Let's try again with random low order bits. |
| 116 uint32_t rand = RandUint32() & (0xffffffff >> entry.length); |
| 117 bits = entry.code | rand; |
| 118 EXPECT_EQ(entry.length, HpackHuffmanDecoderPeer::CodeLengthOfPrefix(bits)) |
| 119 << "Full code: " << Bits(entry.code) << "\n" |
| 120 << " rand: " << Bits(rand) << "\n" |
| 121 << " bits: " << Bits(bits) << "\n" |
| 122 << " ID: " << entry.id; |
| 123 |
| 124 // If fewer bits are available and low order bits are zero after left |
| 125 // shifting (should be true), CodeLengthOfPrefix should never return |
| 126 // a value that is <= the number of available bits. |
| 127 for (uint8_t available = entry.length - 1; available > 0; --available) { |
| 128 uint32_t mask = 0xffffffff; |
| 129 uint32_t avail_mask = mask << (32 - available); |
| 130 bits = entry.code & avail_mask; |
| 131 EXPECT_LT(available, HpackHuffmanDecoderPeer::CodeLengthOfPrefix(bits)) |
| 132 << "Full code: " << Bits(entry.code) << "\n" |
| 133 << "availMask: " << Bits(avail_mask) << "\n" |
| 134 << " bits: " << Bits(bits) << "\n" |
| 135 << " ID: " << entry.id; |
| 136 } |
| 137 } |
| 138 } |
| 139 |
| 140 TEST_F(HpackHuffmanDecoderTest, DecodeToSource) { |
| 141 for (const HpackHuffmanSymbol& entry : HpackHuffmanCode()) { |
| 142 // Check that entry.code, which has all the low order bits set to 0, |
| 143 // decodes to entry.id. |
| 144 EXPECT_EQ(entry.id, DecodeToSource(entry.length, entry.code)) |
| 145 << " Length: " << entry.length << "\n" |
| 146 << "Full code: " << Bits(entry.code); |
| 147 |
| 148 // Let's try again with all the low order bits set to 1. |
| 149 uint32_t bits = entry.code | (0xffffffff >> entry.length); |
| 150 EXPECT_EQ(entry.id, DecodeToSource(entry.length, bits)) |
| 151 << " Length: " << entry.length << "\n" |
| 152 << "Full code: " << Bits(entry.code) << "\n" |
| 153 << " bits: " << Bits(bits); |
| 154 |
| 155 // Let's try again with random low order bits. |
| 156 uint32_t rand = RandUint32() & (0xffffffff >> entry.length); |
| 157 bits = entry.code | rand; |
| 158 EXPECT_EQ(entry.id, DecodeToSource(entry.length, bits)) |
| 159 << " Length: " << entry.length << "\n" |
| 160 << "Full code: " << Bits(entry.code) << "\n" |
| 161 << " rand: " << Bits(rand) << "\n" |
| 162 << " bits: " << Bits(bits); |
| 163 } |
| 164 } |
| 165 |
| 166 TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) { |
| 167 std::string buffer; |
| 168 std::string test_table[] = { |
| 169 a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"), |
| 170 "www.example.com", |
| 171 a2b_hex("a8eb10649cbf"), |
| 172 "no-cache", |
| 173 a2b_hex("25a849e95ba97d7f"), |
| 174 "custom-key", |
| 175 a2b_hex("25a849e95bb8e8b4bf"), |
| 176 "custom-value", |
| 177 }; |
| 178 // Round-trip each test example. |
| 179 for (size_t i = 0; i != arraysize(test_table); i += 2) { |
| 180 const std::string& encodedFixture(test_table[i]); |
| 181 const std::string& decodedFixture(test_table[i + 1]); |
| 182 HpackInputStream input_stream(std::numeric_limits<uint32_t>::max(), |
| 183 encodedFixture); |
| 184 EXPECT_TRUE(HpackHuffmanDecoder::DecodeString( |
| 185 &input_stream, decodedFixture.size(), &buffer)); |
| 186 EXPECT_EQ(decodedFixture, buffer); |
| 187 buffer = EncodeString(decodedFixture); |
| 188 EXPECT_EQ(encodedFixture, buffer); |
| 189 } |
| 190 } |
| 191 |
| 192 TEST_F(HpackHuffmanDecoderTest, TooLong) { |
| 193 std::string buffer; |
| 194 std::string test_table[] = { |
| 195 a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"), |
| 196 "www.example.com", |
| 197 a2b_hex("a8eb10649cbf"), |
| 198 "no-cache", |
| 199 a2b_hex("25a849e95ba97d7f"), |
| 200 "custom-key", |
| 201 a2b_hex("25a849e95bb8e8b4bf"), |
| 202 "custom-value", |
| 203 }; |
| 204 // Round-trip each test example, but with too small an output buffer. |
| 205 for (size_t i = 0; i != arraysize(test_table); i += 2) { |
| 206 const std::string& encodedFixture(test_table[i]); |
| 207 const std::string& decodedFixture(test_table[i + 1]); |
| 208 uint32_t limit = base::RandInt(0, decodedFixture.size() - 1); |
| 209 HpackInputStream strm(std::numeric_limits<uint32_t>::max(), encodedFixture); |
| 210 EXPECT_FALSE(HpackHuffmanDecoder::DecodeString(&strm, limit, &buffer)); |
| 211 |
| 212 // This is NOT a required test as it really tests an implementation detail, |
| 213 // i.e. the fact that it writes the first |limit| values into |buffer|, |
| 214 // then returns false leaving those chars in the buffer. |
| 215 EXPECT_EQ(decodedFixture.substr(0, limit), buffer); |
| 216 } |
| 217 } |
| 218 |
| 219 TEST_F(HpackHuffmanDecoderTest, SpecResponseExamples) { |
| 220 std::string buffer; |
| 221 // clang-format off |
| 222 std::string test_table[] = { |
| 223 a2b_hex("6402"), |
| 224 "302", |
| 225 a2b_hex("aec3771a4b"), |
| 226 "private", |
| 227 a2b_hex("d07abe941054d444a8200595040b8166" |
| 228 "e082a62d1bff"), |
| 229 "Mon, 21 Oct 2013 20:13:21 GMT", |
| 230 a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43" |
| 231 "d3"), |
| 232 "https://www.example.com", |
| 233 a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960" |
| 234 "d5af27087f3672c1ab270fb5291f9587" |
| 235 "316065c003ed4ee5b1063d5007"), |
| 236 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", |
| 237 }; |
| 238 // clang-format on |
| 239 // Round-trip each test example. |
| 240 for (size_t i = 0; i != arraysize(test_table); i += 2) { |
| 241 const std::string& encodedFixture(test_table[i]); |
| 242 const std::string& decodedFixture(test_table[i + 1]); |
| 243 HpackInputStream input_stream(std::numeric_limits<uint32_t>::max(), |
| 244 encodedFixture); |
| 245 EXPECT_TRUE(HpackHuffmanDecoder::DecodeString( |
| 246 &input_stream, decodedFixture.size(), &buffer)); |
| 247 EXPECT_EQ(decodedFixture, buffer); |
| 248 buffer = EncodeString(decodedFixture); |
| 249 EXPECT_EQ(encodedFixture, buffer); |
| 250 } |
| 251 } |
| 252 |
| 253 TEST_F(HpackHuffmanDecoderTest, RoundTripIndividualSymbols) { |
| 254 for (size_t i = 0; i != 256; i++) { |
| 255 char c = static_cast<char>(i); |
| 256 char storage[3] = {c, c, c}; |
| 257 StringPiece input(storage, arraysize(storage)); |
| 258 std::string buffer_in = EncodeString(input); |
| 259 std::string buffer_out; |
| 260 HpackInputStream input_stream(std::numeric_limits<uint32_t>::max(), |
| 261 buffer_in); |
| 262 EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, input.size(), |
| 263 &buffer_out)); |
| 264 EXPECT_EQ(input, buffer_out); |
| 265 } |
| 266 } |
| 267 |
| 268 // Creates 256 input strings, each with a unique byte value i used to sandwich |
| 269 // all the other higher byte values. |
| 270 TEST_F(HpackHuffmanDecoderTest, RoundTripSymbolSequences) { |
| 271 std::string input; |
| 272 std::string encoded; |
| 273 std::string decoded; |
| 274 for (size_t i = 0; i != 256; i++) { |
| 275 input.clear(); |
| 276 auto ic = static_cast<char>(i); |
| 277 input.push_back(ic); |
| 278 for (size_t j = i; j != 256; j++) { |
| 279 input.push_back(static_cast<char>(j)); |
| 280 input.push_back(ic); |
| 281 } |
| 282 EncodeString(input, &encoded); |
| 283 HpackInputStream input_stream(std::numeric_limits<uint32_t>::max(), |
| 284 encoded); |
| 285 EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, input.size(), |
| 286 &decoded)); |
| 287 EXPECT_EQ(input, decoded); |
| 288 } |
| 289 } |
| 290 |
| 291 } // namespace test |
| 292 } // namespace net |
OLD | NEW |