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