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