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