1 // Copyright (c) 2018 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 "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"
6 
7 #include <cstddef>
8 #include <string>
9 
10 #include "absl/base/macros.h"
11 #include "absl/strings/escaping.h"
12 #include "quiche/common/platform/api/quiche_test.h"
13 
14 namespace http2 {
15 namespace {
16 
17 class HuffmanEncoderTest : public quiche::test::QuicheTestWithParam<bool> {
18  protected:
HuffmanEncoderTest()19   HuffmanEncoderTest() : use_fast_encoder_(GetParam()) {}
20   virtual ~HuffmanEncoderTest() = default;
21 
Encode(absl::string_view input,size_t encoded_size,std::string * output)22   void Encode(absl::string_view input, size_t encoded_size,
23               std::string* output) {
24     use_fast_encoder_ ? HuffmanEncodeFast(input, encoded_size, output)
25                       : HuffmanEncode(input, encoded_size, output);
26   }
27 
28   const bool use_fast_encoder_;
29 };
30 
31 INSTANTIATE_TEST_SUITE_P(TwoEncoders, HuffmanEncoderTest, ::testing::Bool());
32 
TEST_P(HuffmanEncoderTest,Empty)33 TEST_P(HuffmanEncoderTest, Empty) {
34   std::string empty("");
35   size_t encoded_size = HuffmanSize(empty);
36   EXPECT_EQ(0u, encoded_size);
37 
38   std::string buffer;
39   Encode(empty, encoded_size, &buffer);
40   EXPECT_EQ("", buffer);
41 }
42 
TEST_P(HuffmanEncoderTest,SpecRequestExamples)43 TEST_P(HuffmanEncoderTest, SpecRequestExamples) {
44   std::string test_table[] = {
45       "f1e3c2e5f23a6ba0ab90f4ff",
46       "www.example.com",
47 
48       "a8eb10649cbf",
49       "no-cache",
50 
51       "25a849e95ba97d7f",
52       "custom-key",
53 
54       "25a849e95bb8e8b4bf",
55       "custom-value",
56   };
57   for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
58     std::string huffman_encoded;
59     ASSERT_TRUE(absl::HexStringToBytes(test_table[i], &huffman_encoded));
60     const std::string& plain_string(test_table[i + 1]);
61     size_t encoded_size = HuffmanSize(plain_string);
62     EXPECT_EQ(huffman_encoded.size(), encoded_size);
63     std::string buffer;
64     buffer.reserve(huffman_encoded.size());
65     Encode(plain_string, encoded_size, &buffer);
66     EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
67   }
68 }
69 
TEST_P(HuffmanEncoderTest,SpecResponseExamples)70 TEST_P(HuffmanEncoderTest, SpecResponseExamples) {
71   std::string test_table[] = {
72       "6402",
73       "302",
74 
75       "aec3771a4b",
76       "private",
77 
78       "d07abe941054d444a8200595040b8166e082a62d1bff",
79       "Mon, 21 Oct 2013 20:13:21 GMT",
80 
81       "9d29ad171863c78f0b97c8e9ae82ae43d3",
82       "https://www.example.com",
83 
84       "94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c0"
85       "03ed4ee5b1063d5007",
86       "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
87   };
88   for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
89     std::string huffman_encoded;
90     ASSERT_TRUE(absl::HexStringToBytes(test_table[i], &huffman_encoded));
91     const std::string& plain_string(test_table[i + 1]);
92     size_t encoded_size = HuffmanSize(plain_string);
93     EXPECT_EQ(huffman_encoded.size(), encoded_size);
94     std::string buffer;
95     Encode(plain_string, encoded_size, &buffer);
96     EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
97   }
98 }
99 
TEST_P(HuffmanEncoderTest,EncodedSizeAgreesWithEncodeString)100 TEST_P(HuffmanEncoderTest, EncodedSizeAgreesWithEncodeString) {
101   std::string test_table[] = {
102       "",
103       "Mon, 21 Oct 2013 20:13:21 GMT",
104       "https://www.example.com",
105       "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
106       std::string(1, '\0'),
107       std::string("foo\0bar", 7),
108       std::string(256, '\0'),
109   };
110   // Modify last |test_table| entry to cover all codes.
111   for (size_t i = 0; i != 256; ++i) {
112     test_table[ABSL_ARRAYSIZE(test_table) - 1][i] = static_cast<char>(i);
113   }
114 
115   for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); ++i) {
116     const std::string& plain_string = test_table[i];
117     size_t encoded_size = HuffmanSize(plain_string);
118     std::string huffman_encoded;
119     Encode(plain_string, encoded_size, &huffman_encoded);
120     EXPECT_EQ(encoded_size, huffman_encoded.size());
121   }
122 }
123 
124 // Test that encoding appends to output without overwriting it.
TEST_P(HuffmanEncoderTest,AppendToOutput)125 TEST_P(HuffmanEncoderTest, AppendToOutput) {
126   size_t encoded_size = HuffmanSize("foo");
127   std::string buffer;
128   Encode("foo", encoded_size, &buffer);
129   std::string expected_encoding;
130   ASSERT_TRUE(absl::HexStringToBytes("94e7", &expected_encoding));
131   EXPECT_EQ(expected_encoding, buffer);
132 
133   encoded_size = HuffmanSize("bar");
134   Encode("bar", encoded_size, &buffer);
135   ASSERT_TRUE(absl::HexStringToBytes("94e78c767f", &expected_encoding));
136   EXPECT_EQ(expected_encoding, buffer);
137 }
138 
139 }  // namespace
140 }  // namespace http2
141