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