1 // Copyright 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/quic/core/qpack/qpack_instruction_encoder.h"
6
7 #include <string>
8
9 #include "absl/strings/escaping.h"
10 #include "absl/strings/string_view.h"
11 #include "quiche/quic/platform/api/quic_logging.h"
12 #include "quiche/quic/platform/api/quic_test.h"
13
14 namespace quic {
15 namespace test {
16
17 class QpackInstructionWithValuesPeer {
18 public:
CreateQpackInstructionWithValues(const QpackInstruction * instruction)19 static QpackInstructionWithValues CreateQpackInstructionWithValues(
20 const QpackInstruction* instruction) {
21 QpackInstructionWithValues instruction_with_values;
22 instruction_with_values.instruction_ = instruction;
23 return instruction_with_values;
24 }
25
set_s_bit(QpackInstructionWithValues * instruction_with_values,bool s_bit)26 static void set_s_bit(QpackInstructionWithValues* instruction_with_values,
27 bool s_bit) {
28 instruction_with_values->s_bit_ = s_bit;
29 }
30
set_varint(QpackInstructionWithValues * instruction_with_values,uint64_t varint)31 static void set_varint(QpackInstructionWithValues* instruction_with_values,
32 uint64_t varint) {
33 instruction_with_values->varint_ = varint;
34 }
35
set_varint2(QpackInstructionWithValues * instruction_with_values,uint64_t varint2)36 static void set_varint2(QpackInstructionWithValues* instruction_with_values,
37 uint64_t varint2) {
38 instruction_with_values->varint2_ = varint2;
39 }
40
set_name(QpackInstructionWithValues * instruction_with_values,absl::string_view name)41 static void set_name(QpackInstructionWithValues* instruction_with_values,
42 absl::string_view name) {
43 instruction_with_values->name_ = name;
44 }
45
set_value(QpackInstructionWithValues * instruction_with_values,absl::string_view value)46 static void set_value(QpackInstructionWithValues* instruction_with_values,
47 absl::string_view value) {
48 instruction_with_values->value_ = value;
49 }
50 };
51
52 namespace {
53
54 class QpackInstructionEncoderTest : public QuicTestWithParam<bool> {
55 protected:
QpackInstructionEncoderTest()56 QpackInstructionEncoderTest()
57 : encoder_(HuffmanEncoding()), verified_position_(0) {}
58 ~QpackInstructionEncoderTest() override = default;
59
DisableHuffmanEncoding()60 bool DisableHuffmanEncoding() { return GetParam(); }
HuffmanEncoding()61 HuffmanEncoding HuffmanEncoding() {
62 return DisableHuffmanEncoding() ? HuffmanEncoding::kDisabled
63 : HuffmanEncoding::kEnabled;
64 }
65
66 // Append encoded |instruction| to |output_|.
EncodeInstruction(const QpackInstructionWithValues & instruction_with_values)67 void EncodeInstruction(
68 const QpackInstructionWithValues& instruction_with_values) {
69 encoder_.Encode(instruction_with_values, &output_);
70 }
71
72 // Compare substring appended to |output_| since last EncodedSegmentMatches()
73 // call against hex-encoded argument.
EncodedSegmentMatches(absl::string_view hex_encoded_expected_substring)74 bool EncodedSegmentMatches(absl::string_view hex_encoded_expected_substring) {
75 auto recently_encoded =
76 absl::string_view(output_).substr(verified_position_);
77 std::string expected;
78 EXPECT_TRUE(
79 absl::HexStringToBytes(hex_encoded_expected_substring, &expected));
80 verified_position_ = output_.size();
81 return recently_encoded == expected;
82 }
83
84 private:
85 QpackInstructionEncoder encoder_;
86 std::string output_;
87 std::string::size_type verified_position_;
88 };
89
90 INSTANTIATE_TEST_SUITE_P(DisableHuffmanEncoding, QpackInstructionEncoderTest,
91 testing::Values(false, true));
92
TEST_P(QpackInstructionEncoderTest,Varint)93 TEST_P(QpackInstructionEncoderTest, Varint) {
94 const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80},
95 {{QpackInstructionFieldType::kVarint, 7}}};
96
97 auto instruction_with_values =
98 QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
99 &instruction);
100 QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5);
101 EncodeInstruction(instruction_with_values);
102 EXPECT_TRUE(EncodedSegmentMatches("05"));
103
104 QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 127);
105 EncodeInstruction(instruction_with_values);
106 EXPECT_TRUE(EncodedSegmentMatches("7f00"));
107 }
108
TEST_P(QpackInstructionEncoderTest,SBitAndTwoVarint2)109 TEST_P(QpackInstructionEncoderTest, SBitAndTwoVarint2) {
110 const QpackInstruction instruction{
111 QpackInstructionOpcode{0x80, 0xc0},
112 {{QpackInstructionFieldType::kSbit, 0x20},
113 {QpackInstructionFieldType::kVarint, 5},
114 {QpackInstructionFieldType::kVarint2, 8}}};
115
116 auto instruction_with_values =
117 QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
118 &instruction);
119 QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true);
120 QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5);
121 QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 200);
122 EncodeInstruction(instruction_with_values);
123 EXPECT_TRUE(EncodedSegmentMatches("a5c8"));
124
125 QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false);
126 QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 31);
127 QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 356);
128 EncodeInstruction(instruction_with_values);
129 EXPECT_TRUE(EncodedSegmentMatches("9f00ff65"));
130 }
131
TEST_P(QpackInstructionEncoderTest,SBitAndVarintAndValue)132 TEST_P(QpackInstructionEncoderTest, SBitAndVarintAndValue) {
133 const QpackInstruction instruction{QpackInstructionOpcode{0xc0, 0xc0},
134 {{QpackInstructionFieldType::kSbit, 0x20},
135 {QpackInstructionFieldType::kVarint, 5},
136 {QpackInstructionFieldType::kValue, 7}}};
137
138 auto instruction_with_values =
139 QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
140 &instruction);
141 QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true);
142 QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 100);
143 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo");
144 EncodeInstruction(instruction_with_values);
145 if (DisableHuffmanEncoding()) {
146 EXPECT_TRUE(EncodedSegmentMatches("ff4503666f6f"));
147 } else {
148 EXPECT_TRUE(EncodedSegmentMatches("ff458294e7"));
149 }
150
151 QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false);
152 QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 3);
153 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar");
154 EncodeInstruction(instruction_with_values);
155 EXPECT_TRUE(EncodedSegmentMatches("c303626172"));
156 }
157
TEST_P(QpackInstructionEncoderTest,Name)158 TEST_P(QpackInstructionEncoderTest, Name) {
159 const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0},
160 {{QpackInstructionFieldType::kName, 4}}};
161
162 auto instruction_with_values =
163 QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
164 &instruction);
165 QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "");
166 EncodeInstruction(instruction_with_values);
167 EXPECT_TRUE(EncodedSegmentMatches("e0"));
168
169 QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo");
170 EncodeInstruction(instruction_with_values);
171 if (DisableHuffmanEncoding()) {
172 EXPECT_TRUE(EncodedSegmentMatches("e3666f6f"));
173 } else {
174 EXPECT_TRUE(EncodedSegmentMatches("f294e7"));
175 }
176
177 QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "bar");
178 EncodeInstruction(instruction_with_values);
179 EXPECT_TRUE(EncodedSegmentMatches("e3626172"));
180 }
181
TEST_P(QpackInstructionEncoderTest,Value)182 TEST_P(QpackInstructionEncoderTest, Value) {
183 const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
184 {{QpackInstructionFieldType::kValue, 3}}};
185
186 auto instruction_with_values =
187 QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
188 &instruction);
189 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "");
190 EncodeInstruction(instruction_with_values);
191 EXPECT_TRUE(EncodedSegmentMatches("f0"));
192 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo");
193 EncodeInstruction(instruction_with_values);
194 if (DisableHuffmanEncoding()) {
195 EXPECT_TRUE(EncodedSegmentMatches("f3666f6f"));
196 } else {
197 EXPECT_TRUE(EncodedSegmentMatches("fa94e7"));
198 }
199
200 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar");
201 EncodeInstruction(instruction_with_values);
202 EXPECT_TRUE(EncodedSegmentMatches("f3626172"));
203 }
204
TEST_P(QpackInstructionEncoderTest,SBitAndNameAndValue)205 TEST_P(QpackInstructionEncoderTest, SBitAndNameAndValue) {
206 const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
207 {{QpackInstructionFieldType::kSbit, 0x08},
208 {QpackInstructionFieldType::kName, 2},
209 {QpackInstructionFieldType::kValue, 7}}};
210
211 auto instruction_with_values =
212 QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
213 &instruction);
214 QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false);
215 QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "");
216 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "");
217 EncodeInstruction(instruction_with_values);
218 EXPECT_TRUE(EncodedSegmentMatches("f000"));
219
220 QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true);
221 QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo");
222 QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar");
223 EncodeInstruction(instruction_with_values);
224 if (DisableHuffmanEncoding()) {
225 EXPECT_TRUE(EncodedSegmentMatches("fb00666f6f03626172"));
226 } else {
227 EXPECT_TRUE(EncodedSegmentMatches("fe94e703626172"));
228 }
229 }
230
231 } // namespace
232 } // namespace test
233 } // namespace quic
234