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