1 // Copyright 2016 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/http2/hpack/decoder/hpack_whole_entry_buffer.h"
6 
7 // Tests of HpackWholeEntryBuffer: does it buffer correctly, and does it
8 // detect Huffman decoding errors and oversize string errors?
9 
10 #include "quiche/common/platform/api/quiche_test.h"
11 
12 using ::testing::_;
13 using ::testing::AllOf;
14 using ::testing::InSequence;
15 using ::testing::Property;
16 using ::testing::StrictMock;
17 
18 namespace http2 {
19 namespace test {
20 namespace {
21 
22 constexpr size_t kMaxStringSize = 20;
23 
24 class MockHpackWholeEntryListener : public HpackWholeEntryListener {
25  public:
26   ~MockHpackWholeEntryListener() override = default;
27 
28   MOCK_METHOD(void, OnIndexedHeader, (size_t index), (override));
29   MOCK_METHOD(void, OnNameIndexAndLiteralValue,
30               (HpackEntryType entry_type, size_t name_index,
31                HpackDecoderStringBuffer* value_buffer),
32               (override));
33   MOCK_METHOD(void, OnLiteralNameAndValue,
34               (HpackEntryType entry_type, HpackDecoderStringBuffer* name_buffer,
35                HpackDecoderStringBuffer* value_buffer),
36               (override));
37   MOCK_METHOD(void, OnDynamicTableSizeUpdate, (size_t size), (override));
38   MOCK_METHOD(void, OnHpackDecodeError, (HpackDecodingError error), (override));
39 };
40 
41 class HpackWholeEntryBufferTest : public quiche::test::QuicheTest {
42  protected:
HpackWholeEntryBufferTest()43   HpackWholeEntryBufferTest() : entry_buffer_(&listener_, kMaxStringSize) {}
44   ~HpackWholeEntryBufferTest() override = default;
45 
46   StrictMock<MockHpackWholeEntryListener> listener_;
47   HpackWholeEntryBuffer entry_buffer_;
48 };
49 
50 // OnIndexedHeader is an immediate pass through.
TEST_F(HpackWholeEntryBufferTest,OnIndexedHeader)51 TEST_F(HpackWholeEntryBufferTest, OnIndexedHeader) {
52   {
53     InSequence seq;
54     EXPECT_CALL(listener_, OnIndexedHeader(17));
55     entry_buffer_.OnIndexedHeader(17);
56   }
57   {
58     InSequence seq;
59     EXPECT_CALL(listener_, OnIndexedHeader(62));
60     entry_buffer_.OnIndexedHeader(62);
61   }
62   {
63     InSequence seq;
64     EXPECT_CALL(listener_, OnIndexedHeader(62));
65     entry_buffer_.OnIndexedHeader(62);
66   }
67   {
68     InSequence seq;
69     EXPECT_CALL(listener_, OnIndexedHeader(128));
70     entry_buffer_.OnIndexedHeader(128);
71   }
72   StrictMock<MockHpackWholeEntryListener> listener2;
73   entry_buffer_.set_listener(&listener2);
74   {
75     InSequence seq;
76     EXPECT_CALL(listener2, OnIndexedHeader(100));
77     entry_buffer_.OnIndexedHeader(100);
78   }
79 }
80 
81 // OnDynamicTableSizeUpdate is an immediate pass through.
TEST_F(HpackWholeEntryBufferTest,OnDynamicTableSizeUpdate)82 TEST_F(HpackWholeEntryBufferTest, OnDynamicTableSizeUpdate) {
83   {
84     InSequence seq;
85     EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(4096));
86     entry_buffer_.OnDynamicTableSizeUpdate(4096);
87   }
88   {
89     InSequence seq;
90     EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(0));
91     entry_buffer_.OnDynamicTableSizeUpdate(0);
92   }
93   {
94     InSequence seq;
95     EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(1024));
96     entry_buffer_.OnDynamicTableSizeUpdate(1024);
97   }
98   {
99     InSequence seq;
100     EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(1024));
101     entry_buffer_.OnDynamicTableSizeUpdate(1024);
102   }
103   StrictMock<MockHpackWholeEntryListener> listener2;
104   entry_buffer_.set_listener(&listener2);
105   {
106     InSequence seq;
107     EXPECT_CALL(listener2, OnDynamicTableSizeUpdate(0));
108     entry_buffer_.OnDynamicTableSizeUpdate(0);
109   }
110 }
111 
TEST_F(HpackWholeEntryBufferTest,OnNameIndexAndLiteralValue)112 TEST_F(HpackWholeEntryBufferTest, OnNameIndexAndLiteralValue) {
113   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kNeverIndexedLiteralHeader,
114                                      123);
115   entry_buffer_.OnValueStart(false, 10);
116   entry_buffer_.OnValueData("some data.", 10);
117 
118   // Force the value to be buffered.
119   entry_buffer_.BufferStringsIfUnbuffered();
120 
121   EXPECT_CALL(
122       listener_,
123       OnNameIndexAndLiteralValue(
124           HpackEntryType::kNeverIndexedLiteralHeader, 123,
125           AllOf(Property(&HpackDecoderStringBuffer::str, "some data."),
126                 Property(&HpackDecoderStringBuffer::BufferedLength, 10))));
127 
128   entry_buffer_.OnValueEnd();
129 }
130 
TEST_F(HpackWholeEntryBufferTest,OnLiteralNameAndValue)131 TEST_F(HpackWholeEntryBufferTest, OnLiteralNameAndValue) {
132   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0);
133   // Force the name to be buffered by delivering it in two pieces.
134   entry_buffer_.OnNameStart(false, 9);
135   entry_buffer_.OnNameData("some-", 5);
136   entry_buffer_.OnNameData("name", 4);
137   entry_buffer_.OnNameEnd();
138   entry_buffer_.OnValueStart(false, 12);
139   entry_buffer_.OnValueData("Header Value", 12);
140 
141   EXPECT_CALL(
142       listener_,
143       OnLiteralNameAndValue(
144           HpackEntryType::kIndexedLiteralHeader,
145           AllOf(Property(&HpackDecoderStringBuffer::str, "some-name"),
146                 Property(&HpackDecoderStringBuffer::BufferedLength, 9)),
147           AllOf(Property(&HpackDecoderStringBuffer::str, "Header Value"),
148                 Property(&HpackDecoderStringBuffer::BufferedLength, 0))));
149 
150   entry_buffer_.OnValueEnd();
151 }
152 
153 // Verify that a name longer than the allowed size generates an error.
TEST_F(HpackWholeEntryBufferTest,NameTooLong)154 TEST_F(HpackWholeEntryBufferTest, NameTooLong) {
155   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0);
156   EXPECT_CALL(listener_, OnHpackDecodeError(HpackDecodingError::kNameTooLong));
157   entry_buffer_.OnNameStart(false, kMaxStringSize + 1);
158 }
159 
160 // Verify that a value longer than the allowed size generates an error.
TEST_F(HpackWholeEntryBufferTest,ValueTooLong)161 TEST_F(HpackWholeEntryBufferTest, ValueTooLong) {
162   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0);
163   EXPECT_CALL(listener_, OnHpackDecodeError(HpackDecodingError::kValueTooLong));
164   entry_buffer_.OnNameStart(false, 4);
165   entry_buffer_.OnNameData("path", 4);
166   entry_buffer_.OnNameEnd();
167   entry_buffer_.OnValueStart(false, kMaxStringSize + 1);
168 }
169 
170 // Regression test for b/162141899.
TEST_F(HpackWholeEntryBufferTest,ValueTooLongWithoutName)171 TEST_F(HpackWholeEntryBufferTest, ValueTooLongWithoutName) {
172   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 1);
173   EXPECT_CALL(listener_, OnHpackDecodeError(HpackDecodingError::kValueTooLong));
174   entry_buffer_.OnValueStart(false, kMaxStringSize + 1);
175 }
176 
177 // Verify that a Huffman encoded name with an explicit EOS generates an error
178 // for an explicit EOS.
TEST_F(HpackWholeEntryBufferTest,NameHuffmanError)179 TEST_F(HpackWholeEntryBufferTest, NameHuffmanError) {
180   const char data[] = "\xff\xff\xff";
181   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kUnindexedLiteralHeader,
182                                      0);
183   entry_buffer_.OnNameStart(true, 4);
184   entry_buffer_.OnNameData(data, 3);
185 
186   EXPECT_CALL(listener_,
187               OnHpackDecodeError(HpackDecodingError::kNameHuffmanError));
188 
189   entry_buffer_.OnNameData(data, 1);
190 
191   // After an error is reported, the listener is not called again.
192   EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(8096)).Times(0);
193   entry_buffer_.OnDynamicTableSizeUpdate(8096);
194 }
195 
196 // Verify that a Huffman encoded value that isn't properly terminated with
197 // a partial EOS symbol generates an error.
TEST_F(HpackWholeEntryBufferTest,ValueHuffmanError)198 TEST_F(HpackWholeEntryBufferTest, ValueHuffmanError) {
199   const char data[] = "\x00\x00\x00";
200   entry_buffer_.OnStartLiteralHeader(HpackEntryType::kNeverIndexedLiteralHeader,
201                                      61);
202   entry_buffer_.OnValueStart(true, 3);
203   entry_buffer_.OnValueData(data, 3);
204 
205   EXPECT_CALL(listener_,
206               OnHpackDecodeError(HpackDecodingError::kValueHuffmanError));
207 
208   entry_buffer_.OnValueEnd();
209 
210   // After an error is reported, the listener is not called again.
211   EXPECT_CALL(listener_, OnIndexedHeader(17)).Times(0);
212   entry_buffer_.OnIndexedHeader(17);
213 }
214 
215 }  // namespace
216 }  // namespace test
217 }  // namespace http2
218