1 // Copyright (c) 2019 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_decoded_headers_accumulator.h"
6 
7 #include <cstring>
8 #include <string>
9 
10 #include "absl/strings/escaping.h"
11 #include "absl/strings/string_view.h"
12 #include "quiche/quic/core/qpack/qpack_decoder.h"
13 #include "quiche/quic/platform/api/quic_test.h"
14 #include "quiche/quic/test_tools/qpack/qpack_test_utils.h"
15 
16 using ::testing::_;
17 using ::testing::ElementsAre;
18 using ::testing::Eq;
19 using ::testing::Pair;
20 using ::testing::SaveArg;
21 using ::testing::StrictMock;
22 
23 namespace quic {
24 namespace test {
25 namespace {
26 
27 // Arbitrary stream ID used for testing.
28 QuicStreamId kTestStreamId = 1;
29 
30 // Limit on header list size.
31 const size_t kMaxHeaderListSize = 100;
32 
33 // Maximum dynamic table capacity.
34 const size_t kMaxDynamicTableCapacity = 100;
35 
36 // Maximum number of blocked streams.
37 const uint64_t kMaximumBlockedStreams = 1;
38 
39 // Header Acknowledgement decoder stream instruction with stream_id = 1.
40 const char* const kHeaderAcknowledgement = "\x81";
41 
42 class MockVisitor : public QpackDecodedHeadersAccumulator::Visitor {
43  public:
44   ~MockVisitor() override = default;
45   MOCK_METHOD(void, OnHeadersDecoded,
46               (QuicHeaderList headers, bool header_list_size_limit_exceeded),
47               (override));
48   MOCK_METHOD(void, OnHeaderDecodingError,
49               (QuicErrorCode error_code, absl::string_view error_message),
50               (override));
51 };
52 
53 }  // anonymous namespace
54 
55 class QpackDecodedHeadersAccumulatorTest : public QuicTest {
56  protected:
QpackDecodedHeadersAccumulatorTest()57   QpackDecodedHeadersAccumulatorTest()
58       : qpack_decoder_(kMaxDynamicTableCapacity, kMaximumBlockedStreams,
59                        &encoder_stream_error_delegate_),
60         accumulator_(kTestStreamId, &qpack_decoder_, &visitor_,
61                      kMaxHeaderListSize) {
62     qpack_decoder_.set_qpack_stream_sender_delegate(
63         &decoder_stream_sender_delegate_);
64   }
65 
66   NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_;
67   StrictMock<MockQpackStreamSenderDelegate> decoder_stream_sender_delegate_;
68   QpackDecoder qpack_decoder_;
69   StrictMock<MockVisitor> visitor_;
70   QpackDecodedHeadersAccumulator accumulator_;
71 };
72 
73 // HEADERS frame payload must have a complete Header Block Prefix.
TEST_F(QpackDecodedHeadersAccumulatorTest,EmptyPayload)74 TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) {
75   EXPECT_CALL(visitor_,
76               OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED,
77                                     Eq("Incomplete header data prefix.")));
78   accumulator_.EndHeaderBlock();
79 }
80 
81 // HEADERS frame payload must have a complete Header Block Prefix.
TEST_F(QpackDecodedHeadersAccumulatorTest,TruncatedHeaderBlockPrefix)82 TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) {
83   std::string encoded_data;
84   ASSERT_TRUE(absl::HexStringToBytes("00", &encoded_data));
85   accumulator_.Decode(encoded_data);
86 
87   EXPECT_CALL(visitor_,
88               OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED,
89                                     Eq("Incomplete header data prefix.")));
90   accumulator_.EndHeaderBlock();
91 }
92 
TEST_F(QpackDecodedHeadersAccumulatorTest,EmptyHeaderList)93 TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) {
94   std::string encoded_data;
95   ASSERT_TRUE(absl::HexStringToBytes("0000", &encoded_data));
96   accumulator_.Decode(encoded_data);
97 
98   QuicHeaderList header_list;
99   EXPECT_CALL(visitor_, OnHeadersDecoded(_, false))
100       .WillOnce(SaveArg<0>(&header_list));
101   accumulator_.EndHeaderBlock();
102 
103   EXPECT_EQ(0u, header_list.uncompressed_header_bytes());
104   EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes());
105   EXPECT_TRUE(header_list.empty());
106 }
107 
108 // This payload is the prefix of a valid payload, but EndHeaderBlock() is called
109 // before it can be completely decoded.
TEST_F(QpackDecodedHeadersAccumulatorTest,TruncatedPayload)110 TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) {
111   std::string encoded_data;
112   ASSERT_TRUE(absl::HexStringToBytes("00002366", &encoded_data));
113   accumulator_.Decode(encoded_data);
114 
115   EXPECT_CALL(visitor_, OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED,
116                                               Eq("Incomplete header block.")));
117   accumulator_.EndHeaderBlock();
118 }
119 
120 // This payload is invalid because it refers to a non-existing static entry.
TEST_F(QpackDecodedHeadersAccumulatorTest,InvalidPayload)121 TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) {
122   EXPECT_CALL(visitor_,
123               OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED,
124                                     Eq("Static table entry not found.")));
125   std::string encoded_data;
126   ASSERT_TRUE(absl::HexStringToBytes("0000ff23ff24", &encoded_data));
127   accumulator_.Decode(encoded_data);
128 }
129 
TEST_F(QpackDecodedHeadersAccumulatorTest,Success)130 TEST_F(QpackDecodedHeadersAccumulatorTest, Success) {
131   std::string encoded_data;
132   ASSERT_TRUE(absl::HexStringToBytes("000023666f6f03626172", &encoded_data));
133   accumulator_.Decode(encoded_data);
134 
135   QuicHeaderList header_list;
136   EXPECT_CALL(visitor_, OnHeadersDecoded(_, false))
137       .WillOnce(SaveArg<0>(&header_list));
138   accumulator_.EndHeaderBlock();
139 
140   EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar")));
141   EXPECT_EQ(strlen("foo") + strlen("bar"),
142             header_list.uncompressed_header_bytes());
143   EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes());
144 }
145 
146 // Test that Decode() calls are not ignored after header list limit is exceeded,
147 // otherwise decoding could fail with "incomplete header block" error.
TEST_F(QpackDecodedHeadersAccumulatorTest,ExceedLimitThenSplitInstruction)148 TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedLimitThenSplitInstruction) {
149   std::string encoded_data;
150   // Total length of header list exceeds kMaxHeaderListSize.
151   ASSERT_TRUE(absl::HexStringToBytes(
152       "0000"                                      // header block prefix
153       "26666f6f626172"                            // header key: "foobar"
154       "7d61616161616161616161616161616161616161"  // header value: 'a' 125 times
155       "616161616161616161616161616161616161616161616161616161616161616161616161"
156       "616161616161616161616161616161616161616161616161616161616161616161616161"
157       "61616161616161616161616161616161616161616161616161616161616161616161"
158       "ff",  // first byte of a two-byte long Indexed Header Field instruction
159       &encoded_data));
160   accumulator_.Decode(encoded_data);
161   ASSERT_TRUE(absl::HexStringToBytes(
162       "0f",  // second byte of a two-byte long Indexed Header Field instruction
163       &encoded_data));
164   accumulator_.Decode(encoded_data);
165 
166   EXPECT_CALL(visitor_, OnHeadersDecoded(_, true));
167   accumulator_.EndHeaderBlock();
168 }
169 
170 // Test that header list limit enforcement works with blocked encoding.
TEST_F(QpackDecodedHeadersAccumulatorTest,ExceedLimitBlocked)171 TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedLimitBlocked) {
172   std::string encoded_data;
173   // Total length of header list exceeds kMaxHeaderListSize.
174   ASSERT_TRUE(absl::HexStringToBytes(
175       "0200"            // header block prefix
176       "80"              // reference to dynamic table entry not yet received
177       "26666f6f626172"  // header key: "foobar"
178       "7d61616161616161616161616161616161616161"  // header value: 'a' 125 times
179       "616161616161616161616161616161616161616161616161616161616161616161616161"
180       "616161616161616161616161616161616161616161616161616161616161616161616161"
181       "61616161616161616161616161616161616161616161616161616161616161616161",
182       &encoded_data));
183   accumulator_.Decode(encoded_data);
184   accumulator_.EndHeaderBlock();
185 
186   // Set dynamic table capacity.
187   qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity);
188   // Adding dynamic table entry unblocks decoding.
189   EXPECT_CALL(decoder_stream_sender_delegate_,
190               WriteStreamData(Eq(kHeaderAcknowledgement)));
191 
192   EXPECT_CALL(visitor_, OnHeadersDecoded(_, true));
193   qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
194   if (GetQuicRestartFlag(quic_opport_bundle_qpack_decoder_data4)) {
195     qpack_decoder_.FlushDecoderStream();
196   }
197 }
198 
TEST_F(QpackDecodedHeadersAccumulatorTest,BlockedDecoding)199 TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecoding) {
200   std::string encoded_data;
201   // Reference to dynamic table entry not yet received.
202   ASSERT_TRUE(absl::HexStringToBytes("020080", &encoded_data));
203   accumulator_.Decode(encoded_data);
204   accumulator_.EndHeaderBlock();
205 
206   // Set dynamic table capacity.
207   qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity);
208   // Adding dynamic table entry unblocks decoding.
209   EXPECT_CALL(decoder_stream_sender_delegate_,
210               WriteStreamData(Eq(kHeaderAcknowledgement)));
211 
212   QuicHeaderList header_list;
213   EXPECT_CALL(visitor_, OnHeadersDecoded(_, false))
214       .WillOnce(SaveArg<0>(&header_list));
215   qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
216 
217   EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar")));
218   EXPECT_EQ(strlen("foo") + strlen("bar"),
219             header_list.uncompressed_header_bytes());
220   EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes());
221   if (GetQuicRestartFlag(quic_opport_bundle_qpack_decoder_data4)) {
222     qpack_decoder_.FlushDecoderStream();
223   }
224 }
225 
TEST_F(QpackDecodedHeadersAccumulatorTest,BlockedDecodingUnblockedBeforeEndOfHeaderBlock)226 TEST_F(QpackDecodedHeadersAccumulatorTest,
227        BlockedDecodingUnblockedBeforeEndOfHeaderBlock) {
228   std::string encoded_data;
229   // Reference to dynamic table entry not yet received.
230   ASSERT_TRUE(absl::HexStringToBytes("020080", &encoded_data));
231   accumulator_.Decode(encoded_data);
232 
233   // Set dynamic table capacity.
234   qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity);
235   // Adding dynamic table entry unblocks decoding.
236   qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
237 
238   // Rest of header block: same entry again.
239   EXPECT_CALL(decoder_stream_sender_delegate_,
240               WriteStreamData(Eq(kHeaderAcknowledgement)));
241   ASSERT_TRUE(absl::HexStringToBytes("80", &encoded_data));
242   accumulator_.Decode(encoded_data);
243 
244   QuicHeaderList header_list;
245   EXPECT_CALL(visitor_, OnHeadersDecoded(_, false))
246       .WillOnce(SaveArg<0>(&header_list));
247   accumulator_.EndHeaderBlock();
248 
249   EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar"), Pair("foo", "bar")));
250   if (GetQuicRestartFlag(quic_opport_bundle_qpack_decoder_data4)) {
251     qpack_decoder_.FlushDecoderStream();
252   }
253 }
254 
255 // Regression test for https://crbug.com/1024263.
TEST_F(QpackDecodedHeadersAccumulatorTest,BlockedDecodingUnblockedAndErrorBeforeEndOfHeaderBlock)256 TEST_F(QpackDecodedHeadersAccumulatorTest,
257        BlockedDecodingUnblockedAndErrorBeforeEndOfHeaderBlock) {
258   std::string encoded_data;
259   // Required Insert Count higher than number of entries causes decoding to be
260   // blocked.
261   ASSERT_TRUE(absl::HexStringToBytes("0200", &encoded_data));
262   accumulator_.Decode(encoded_data);
263   // Indexed Header Field instruction addressing dynamic table entry with
264   // relative index 0, absolute index 0.
265   ASSERT_TRUE(absl::HexStringToBytes("80", &encoded_data));
266   accumulator_.Decode(encoded_data);
267   // Relative index larger than or equal to Base is invalid.
268   ASSERT_TRUE(absl::HexStringToBytes("81", &encoded_data));
269   accumulator_.Decode(encoded_data);
270 
271   // Set dynamic table capacity.
272   qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity);
273 
274   // Adding dynamic table entry unblocks decoding.  Error is detected.
275   EXPECT_CALL(visitor_, OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED,
276                                               Eq("Invalid relative index.")));
277   qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
278 }
279 
280 }  // namespace test
281 }  // namespace quic
282