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

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

Issue 2832973003: Split net/spdy into core and chromium subdirectories. (Closed)
Patch Set: Fix some more build rules. Created 3 years, 8 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_table.cc ('k') | net/spdy/hpack/hpack_input_stream.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 2014 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_table.h"
6
7 #include <stdint.h>
8
9 #include <bitset>
10 #include <utility>
11
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "net/spdy/hpack/hpack_constants.h"
15 #include "net/spdy/hpack/hpack_huffman_decoder.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/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using testing::ElementsAreArray;
23 using testing::Pointwise;
24
25 namespace net {
26
27 namespace test {
28
29 typedef HpackHuffmanTable::DecodeEntry DecodeEntry;
30 typedef HpackHuffmanTable::DecodeTable DecodeTable;
31
32 class HpackHuffmanTablePeer {
33 public:
34 explicit HpackHuffmanTablePeer(const HpackHuffmanTable& table)
35 : table_(table) {}
36
37 const std::vector<uint32_t>& code_by_id() const { return table_.code_by_id_; }
38 const std::vector<uint8_t>& length_by_id() const {
39 return table_.length_by_id_;
40 }
41 const std::vector<DecodeTable>& decode_tables() const {
42 return table_.decode_tables_;
43 }
44 char pad_bits() const {
45 // Cast to match signed-ness of bits8().
46 return static_cast<char>(table_.pad_bits_);
47 }
48 uint16_t failed_symbol_id() const { return table_.failed_symbol_id_; }
49 std::vector<DecodeEntry> decode_entries(const DecodeTable& decode_table) {
50 std::vector<DecodeEntry>::const_iterator begin =
51 table_.decode_entries_.begin() + decode_table.entries_offset;
52 return std::vector<DecodeEntry>(begin, begin + decode_table.size());
53 }
54
55 private:
56 const HpackHuffmanTable& table_;
57 };
58
59 namespace {
60
61 // Tests of the ability to decode some canonical Huffman code,
62 // not just the one defined in the RFC 7541.
63 class GenericHuffmanTableTest : public ::testing::TestWithParam<bool> {
64 protected:
65 GenericHuffmanTableTest() : table_(), peer_(table_) {}
66
67 SpdyString EncodeString(SpdyStringPiece input) {
68 SpdyString result;
69 HpackOutputStream output_stream;
70 table_.EncodeString(input, &output_stream);
71
72 output_stream.TakeString(&result);
73 // Verify EncodedSize() agrees with EncodeString().
74 EXPECT_EQ(result.size(), table_.EncodedSize(input));
75 return result;
76 }
77
78 HpackHuffmanTable table_;
79 HpackHuffmanTablePeer peer_;
80 };
81
82 MATCHER(DecodeEntryEq, "") {
83 const DecodeEntry& lhs = std::tr1::get<0>(arg);
84 const DecodeEntry& rhs = std::tr1::get<1>(arg);
85 return lhs.next_table_index == rhs.next_table_index &&
86 lhs.length == rhs.length && lhs.symbol_id == rhs.symbol_id;
87 }
88
89 uint32_t bits32(const SpdyString& bitstring) {
90 return std::bitset<32>(bitstring).to_ulong();
91 }
92 char bits8(const SpdyString& bitstring) {
93 return static_cast<char>(std::bitset<8>(bitstring).to_ulong());
94 }
95
96 TEST_F(GenericHuffmanTableTest, InitializeEdgeCases) {
97 {
98 // Verify eight symbols can be encoded with 3 bits per symbol.
99 HpackHuffmanSymbol code[] = {
100 {bits32("00000000000000000000000000000000"), 3, 0},
101 {bits32("00100000000000000000000000000000"), 3, 1},
102 {bits32("01000000000000000000000000000000"), 3, 2},
103 {bits32("01100000000000000000000000000000"), 3, 3},
104 {bits32("10000000000000000000000000000000"), 3, 4},
105 {bits32("10100000000000000000000000000000"), 3, 5},
106 {bits32("11000000000000000000000000000000"), 3, 6},
107 {bits32("11100000000000000000000000000000"), 8, 7}};
108 HpackHuffmanTable table;
109 EXPECT_TRUE(table.Initialize(code, arraysize(code)));
110 }
111 {
112 // But using 2 bits with one symbol overflows the code.
113 HpackHuffmanSymbol code[] = {
114 {bits32("01000000000000000000000000000000"), 3, 0},
115 {bits32("01100000000000000000000000000000"), 3, 1},
116 {bits32("00000000000000000000000000000000"), 2, 2},
117 {bits32("10000000000000000000000000000000"), 3, 3},
118 {bits32("10100000000000000000000000000000"), 3, 4},
119 {bits32("11000000000000000000000000000000"), 3, 5},
120 {bits32("11100000000000000000000000000000"), 3, 6},
121 {bits32("00000000000000000000000000000000"), 8, 7}}; // Overflow.
122 HpackHuffmanTable table;
123 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
124 EXPECT_EQ(7, HpackHuffmanTablePeer(table).failed_symbol_id());
125 }
126 {
127 // Verify four symbols can be encoded with incremental bits per symbol.
128 HpackHuffmanSymbol code[] = {
129 {bits32("00000000000000000000000000000000"), 1, 0},
130 {bits32("10000000000000000000000000000000"), 2, 1},
131 {bits32("11000000000000000000000000000000"), 3, 2},
132 {bits32("11100000000000000000000000000000"), 8, 3}};
133 HpackHuffmanTable table;
134 EXPECT_TRUE(table.Initialize(code, arraysize(code)));
135 }
136 {
137 // But repeating a length overflows the code.
138 HpackHuffmanSymbol code[] = {
139 {bits32("00000000000000000000000000000000"), 1, 0},
140 {bits32("10000000000000000000000000000000"), 2, 1},
141 {bits32("11000000000000000000000000000000"), 2, 2},
142 {bits32("00000000000000000000000000000000"), 8, 3}}; // Overflow.
143 HpackHuffmanTable table;
144 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
145 EXPECT_EQ(3, HpackHuffmanTablePeer(table).failed_symbol_id());
146 }
147 {
148 // Symbol IDs must be assigned sequentially with no gaps.
149 HpackHuffmanSymbol code[] = {
150 {bits32("00000000000000000000000000000000"), 1, 0},
151 {bits32("10000000000000000000000000000000"), 2, 1},
152 {bits32("11000000000000000000000000000000"), 3, 1}, // Repeat.
153 {bits32("11100000000000000000000000000000"), 8, 3}};
154 HpackHuffmanTable table;
155 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
156 EXPECT_EQ(2, HpackHuffmanTablePeer(table).failed_symbol_id());
157 }
158 {
159 // Canonical codes must begin with zero.
160 HpackHuffmanSymbol code[] = {
161 {bits32("10000000000000000000000000000000"), 4, 0},
162 {bits32("10010000000000000000000000000000"), 4, 1},
163 {bits32("10100000000000000000000000000000"), 4, 2},
164 {bits32("10110000000000000000000000000000"), 8, 3}};
165 HpackHuffmanTable table;
166 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
167 EXPECT_EQ(0, HpackHuffmanTablePeer(table).failed_symbol_id());
168 }
169 {
170 // Codes must match the expected canonical sequence.
171 HpackHuffmanSymbol code[] = {
172 {bits32("00000000000000000000000000000000"), 2, 0},
173 {bits32("01000000000000000000000000000000"), 2, 1},
174 {bits32("11000000000000000000000000000000"), 2, 2}, // Not canonical.
175 {bits32("10000000000000000000000000000000"), 8, 3}};
176 HpackHuffmanTable table;
177 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
178 EXPECT_EQ(2, HpackHuffmanTablePeer(table).failed_symbol_id());
179 }
180 {
181 // At least one code must have a length of 8 bits (to ensure pad-ability).
182 HpackHuffmanSymbol code[] = {
183 {bits32("00000000000000000000000000000000"), 1, 0},
184 {bits32("10000000000000000000000000000000"), 2, 1},
185 {bits32("11000000000000000000000000000000"), 3, 2},
186 {bits32("11100000000000000000000000000000"), 7, 3}};
187 HpackHuffmanTable table;
188 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
189 }
190 }
191
192 TEST_F(GenericHuffmanTableTest, ValidateInternalsWithSmallCode) {
193 HpackHuffmanSymbol code[] = {
194 {bits32("01100000000000000000000000000000"), 4, 0}, // 3rd.
195 {bits32("01110000000000000000000000000000"), 4, 1}, // 4th.
196 {bits32("00000000000000000000000000000000"), 2, 2}, // 1st assigned code.
197 {bits32("01000000000000000000000000000000"), 3, 3}, // 2nd.
198 {bits32("10000000000000000000000000000000"), 5, 4}, // 5th.
199 {bits32("10001000000000000000000000000000"), 5, 5}, // 6th.
200 {bits32("10011000000000000000000000000000"), 8, 6}, // 8th.
201 {bits32("10010000000000000000000000000000"), 5, 7}}; // 7th.
202 EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
203 ASSERT_EQ(arraysize(code), peer_.code_by_id().size());
204 ASSERT_EQ(arraysize(code), peer_.length_by_id().size());
205 for (size_t i = 0; i < arraysize(code); ++i) {
206 EXPECT_EQ(code[i].code, peer_.code_by_id()[i]);
207 EXPECT_EQ(code[i].length, peer_.length_by_id()[i]);
208 }
209
210 EXPECT_EQ(1u, peer_.decode_tables().size());
211 {
212 std::vector<DecodeEntry> expected;
213 expected.resize(128, DecodeEntry(0, 2, 2)); // Fills 128.
214 expected.resize(192, DecodeEntry(0, 3, 3)); // Fills 64.
215 expected.resize(224, DecodeEntry(0, 4, 0)); // Fills 32.
216 expected.resize(256, DecodeEntry(0, 4, 1)); // Fills 32.
217 expected.resize(272, DecodeEntry(0, 5, 4)); // Fills 16.
218 expected.resize(288, DecodeEntry(0, 5, 5)); // Fills 16.
219 expected.resize(304, DecodeEntry(0, 5, 7)); // Fills 16.
220 expected.resize(306, DecodeEntry(0, 8, 6)); // Fills 2.
221 expected.resize(512, DecodeEntry()); // Remainder is empty.
222
223 EXPECT_THAT(peer_.decode_entries(peer_.decode_tables()[0]),
224 Pointwise(DecodeEntryEq(), expected));
225 }
226 EXPECT_EQ(bits8("10011000"), peer_.pad_bits());
227
228 char input_storage[] = {2, 3, 2, 7, 4};
229 SpdyStringPiece input(input_storage, arraysize(input_storage));
230 // By symbol: (2) 00 (3) 010 (2) 00 (7) 10010 (4) 10000 (6 as pad) 1001100.
231 char expect_storage[] = {bits8("00010001"), bits8("00101000"),
232 bits8("01001100")};
233 SpdyStringPiece expect(expect_storage, arraysize(expect_storage));
234
235 SpdyString buffer_in = EncodeString(input);
236 EXPECT_EQ(expect, buffer_in);
237
238 SpdyString buffer_out;
239 HpackInputStream input_stream(buffer_in);
240 EXPECT_TRUE(table_.GenericDecodeString(&input_stream, &buffer_out));
241 EXPECT_EQ(buffer_out, input);
242 }
243
244 TEST_F(GenericHuffmanTableTest, ValidateMultiLevelDecodeTables) {
245 HpackHuffmanSymbol code[] = {
246 {bits32("00000000000000000000000000000000"), 6, 0},
247 {bits32("00000100000000000000000000000000"), 6, 1},
248 {bits32("00001000000000000000000000000000"), 11, 2},
249 {bits32("00001000001000000000000000000000"), 11, 3},
250 {bits32("00001000010000000000000000000000"), 12, 4},
251 };
252 EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
253
254 EXPECT_EQ(2u, peer_.decode_tables().size());
255 {
256 std::vector<DecodeEntry> expected;
257 expected.resize(8, DecodeEntry(0, 6, 0)); // Fills 8.
258 expected.resize(16, DecodeEntry(0, 6, 1)); // Fills 8.
259 expected.resize(17, DecodeEntry(1, 12, 0)); // Pointer. Fills 1.
260 expected.resize(512, DecodeEntry()); // Remainder is empty.
261
262 const DecodeTable& decode_table = peer_.decode_tables()[0];
263 EXPECT_EQ(decode_table.prefix_length, 0);
264 EXPECT_EQ(decode_table.indexed_length, 9);
265 EXPECT_THAT(peer_.decode_entries(decode_table),
266 Pointwise(DecodeEntryEq(), expected));
267 }
268 {
269 std::vector<DecodeEntry> expected;
270 expected.resize(2, DecodeEntry(1, 11, 2)); // Fills 2.
271 expected.resize(4, DecodeEntry(1, 11, 3)); // Fills 2.
272 expected.resize(5, DecodeEntry(1, 12, 4)); // Fills 1.
273 expected.resize(8, DecodeEntry()); // Remainder is empty.
274
275 const DecodeTable& decode_table = peer_.decode_tables()[1];
276 EXPECT_EQ(decode_table.prefix_length, 9);
277 EXPECT_EQ(decode_table.indexed_length, 3);
278 EXPECT_THAT(peer_.decode_entries(decode_table),
279 Pointwise(DecodeEntryEq(), expected));
280 }
281 EXPECT_EQ(bits8("00001000"), peer_.pad_bits());
282 }
283
284 TEST_F(GenericHuffmanTableTest, DecodeWithBadInput) {
285 HpackHuffmanSymbol code[] = {
286 {bits32("01100000000000000000000000000000"), 4, 0},
287 {bits32("01110000000000000000000000000000"), 4, 1},
288 {bits32("00000000000000000000000000000000"), 2, 2},
289 {bits32("01000000000000000000000000000000"), 3, 3},
290 {bits32("10000000000000000000000000000000"), 5, 4},
291 {bits32("10001000000000000000000000000000"), 5, 5},
292 {bits32("10011000000000000000000000000000"), 6, 6},
293 {bits32("10010000000000000000000000000000"), 5, 7},
294 {bits32("10011100000000000000000000000000"), 16, 8}};
295 EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
296
297 SpdyString buffer;
298 {
299 // This example works: (2) 00 (3) 010 (2) 00 (6) 100110 (pad) 100.
300 char input_storage[] = {bits8("00010001"), bits8("00110100")};
301 SpdyStringPiece input(input_storage, arraysize(input_storage));
302
303 HpackInputStream input_stream(input);
304 EXPECT_TRUE(table_.GenericDecodeString(&input_stream, &buffer));
305 EXPECT_EQ(buffer, "\x02\x03\x02\x06");
306 }
307 {
308 // Expect to fail on an invalid code prefix.
309 // (2) 00 (3) 010 (2) 00 (too-large) 101000 (pad) 100.
310 char input_storage[] = {bits8("00010001"), bits8("01000111")};
311 SpdyStringPiece input(input_storage, arraysize(input_storage));
312
313 HpackInputStream input_stream(input);
314 EXPECT_FALSE(table_.GenericDecodeString(&input_stream, &buffer));
315 EXPECT_EQ(buffer, "\x02\x03\x02");
316 }
317 {
318 // Expect to fail if more than a byte of unconsumed input remains.
319 // (6) 100110 (8 truncated) 1001110000
320 char input_storage[] = {bits8("10011010"), bits8("01110000")};
321 SpdyStringPiece input(input_storage, arraysize(input_storage));
322
323 HpackInputStream input_stream(input);
324 EXPECT_FALSE(table_.GenericDecodeString(&input_stream, &buffer));
325 EXPECT_EQ(buffer, "\x06");
326 }
327 }
328
329 // Tests of the ability to decode the HPACK Huffman Code, defined in:
330 // https://httpwg.github.io/specs/rfc7541.html#huffman.code
331 class HpackHuffmanTableTest : public GenericHuffmanTableTest {
332 protected:
333 void SetUp() override {
334 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
335 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
336 EXPECT_TRUE(table_.IsInitialized());
337 }
338
339 void DecodeStringTwice(const SpdyString& encoded, SpdyString* out) {
340 // First decode with HpackHuffmanTable.
341 {
342 HpackInputStream input_stream(encoded);
343 EXPECT_TRUE(table_.GenericDecodeString(&input_stream, out));
344 }
345 // And decode again with the fixed decoder, confirming that the result is
346 // the same.
347 {
348 HpackInputStream input_stream(encoded);
349 SpdyString buf;
350 EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, &buf));
351 EXPECT_EQ(*out, buf);
352 }
353 }
354 };
355
356 TEST_F(HpackHuffmanTableTest, InitializeHpackCode) {
357 EXPECT_EQ(peer_.pad_bits(), '\xFF'); // First 8 bits of EOS.
358 }
359
360 TEST_F(HpackHuffmanTableTest, SpecRequestExamples) {
361 SpdyString buffer;
362 SpdyString test_table[] = {
363 a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
364 "www.example.com",
365 a2b_hex("a8eb10649cbf"),
366 "no-cache",
367 a2b_hex("25a849e95ba97d7f"),
368 "custom-key",
369 a2b_hex("25a849e95bb8e8b4bf"),
370 "custom-value",
371 };
372 // Round-trip each test example.
373 for (size_t i = 0; i != arraysize(test_table); i += 2) {
374 const SpdyString& encodedFixture(test_table[i]);
375 const SpdyString& decodedFixture(test_table[i + 1]);
376 DecodeStringTwice(encodedFixture, &buffer);
377 EXPECT_EQ(decodedFixture, buffer);
378 buffer = EncodeString(decodedFixture);
379 EXPECT_EQ(encodedFixture, buffer);
380 }
381 }
382
383 TEST_F(HpackHuffmanTableTest, SpecResponseExamples) {
384 SpdyString buffer;
385 SpdyString test_table[] = {
386 a2b_hex("6402"),
387 "302",
388 a2b_hex("aec3771a4b"),
389 "private",
390 a2b_hex("d07abe941054d444a8200595040b8166"
391 "e082a62d1bff"),
392 "Mon, 21 Oct 2013 20:13:21 GMT",
393 a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43"
394 "d3"),
395 "https://www.example.com",
396 a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960"
397 "d5af27087f3672c1ab270fb5291f9587"
398 "316065c003ed4ee5b1063d5007"),
399 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
400 };
401 // Round-trip each test example.
402 for (size_t i = 0; i != arraysize(test_table); i += 2) {
403 const SpdyString& encodedFixture(test_table[i]);
404 const SpdyString& decodedFixture(test_table[i + 1]);
405 DecodeStringTwice(encodedFixture, &buffer);
406 EXPECT_EQ(decodedFixture, buffer);
407 buffer = EncodeString(decodedFixture);
408 EXPECT_EQ(encodedFixture, buffer);
409 }
410 }
411
412 TEST_F(HpackHuffmanTableTest, RoundTripIndividualSymbols) {
413 for (size_t i = 0; i != 256; i++) {
414 char c = static_cast<char>(i);
415 char storage[3] = {c, c, c};
416 SpdyStringPiece input(storage, arraysize(storage));
417 SpdyString buffer_in = EncodeString(input);
418 SpdyString buffer_out;
419 DecodeStringTwice(buffer_in, &buffer_out);
420 EXPECT_EQ(input, buffer_out);
421 }
422 }
423
424 TEST_F(HpackHuffmanTableTest, RoundTripSymbolSequence) {
425 char storage[512];
426 for (size_t i = 0; i != 256; i++) {
427 storage[i] = static_cast<char>(i);
428 storage[511 - i] = static_cast<char>(i);
429 }
430 SpdyStringPiece input(storage, arraysize(storage));
431
432 SpdyString buffer_in = EncodeString(input);
433 SpdyString buffer_out;
434 DecodeStringTwice(buffer_in, &buffer_out);
435 EXPECT_EQ(input, buffer_out);
436 }
437
438 TEST_F(HpackHuffmanTableTest, EncodedSizeAgreesWithEncodeString) {
439 SpdyString test_table[] = {
440 "",
441 "Mon, 21 Oct 2013 20:13:21 GMT",
442 "https://www.example.com",
443 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
444 SpdyString(1, '\0'),
445 SpdyString("foo\0bar", 7),
446 SpdyString(256, '\0'),
447 };
448 for (size_t i = 0; i != 256; ++i) {
449 // Expand last |test_table| entry to cover all codes.
450 test_table[arraysize(test_table) - 1][i] = static_cast<char>(i);
451 }
452
453 HpackOutputStream output_stream;
454 SpdyString encoding;
455 for (size_t i = 0; i != arraysize(test_table); ++i) {
456 table_.EncodeString(test_table[i], &output_stream);
457 output_stream.TakeString(&encoding);
458 EXPECT_EQ(encoding.size(), table_.EncodedSize(test_table[i]));
459 }
460 }
461
462 } // namespace
463
464 } // namespace test
465
466 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/hpack/hpack_huffman_table.cc ('k') | net/spdy/hpack/hpack_input_stream.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698