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

Side by Side Diff: net/spdy/hpack/hpack_huffman_decoder_test.cc

Issue 1568423002: Implement better HPACK Huffman code decoder. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Do not use binary literals. Created 4 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/spdy/hpack/hpack_huffman_decoder.cc ('k') | net/spdy/hpack/hpack_huffman_table.h » ('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/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
OLDNEW
« no previous file with comments | « net/spdy/hpack/hpack_huffman_decoder.cc ('k') | net/spdy/hpack/hpack_huffman_table.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698