• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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