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 |