| // Copyright 2016 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "http2/hpack/decoder/hpack_whole_entry_buffer.h" | |
| // Tests of HpackWholeEntryBuffer: does it buffer correctly, and does it | |
| // detect Huffman decoding errors and oversize string errors? | |
| #include "http2/platform/api/http2_test_helpers.h" | |
| #include "common/platform/api/quiche_test.h" | |
| using ::testing::_; | |
| using ::testing::AllOf; | |
| using ::testing::InSequence; | |
| using ::testing::Property; | |
| using ::testing::StrictMock; | |
| namespace http2 { | |
| namespace test { | |
| namespace { | |
| constexpr size_t kMaxStringSize = 20; | |
| class MockHpackWholeEntryListener : public HpackWholeEntryListener { | |
| public: | |
| ~MockHpackWholeEntryListener() override = default; | |
| MOCK_METHOD(void, OnIndexedHeader, (size_t index), (override)); | |
| MOCK_METHOD(void, | |
| OnNameIndexAndLiteralValue, | |
| (HpackEntryType entry_type, | |
| size_t name_index, | |
| HpackDecoderStringBuffer* value_buffer), | |
| (override)); | |
| MOCK_METHOD(void, | |
| OnLiteralNameAndValue, | |
| (HpackEntryType entry_type, | |
| HpackDecoderStringBuffer* name_buffer, | |
| HpackDecoderStringBuffer* value_buffer), | |
| (override)); | |
| MOCK_METHOD(void, OnDynamicTableSizeUpdate, (size_t size), (override)); | |
| MOCK_METHOD(void, | |
| OnHpackDecodeError, | |
| (HpackDecodingError error, std::string detailed_error), | |
| (override)); | |
| }; | |
| class HpackWholeEntryBufferTest : public QuicheTest { | |
| protected: | |
| HpackWholeEntryBufferTest() : entry_buffer_(&listener_, kMaxStringSize) {} | |
| ~HpackWholeEntryBufferTest() override = default; | |
| StrictMock<MockHpackWholeEntryListener> listener_; | |
| HpackWholeEntryBuffer entry_buffer_; | |
| }; | |
| // OnIndexedHeader is an immediate pass through. | |
| TEST_F(HpackWholeEntryBufferTest, OnIndexedHeader) { | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnIndexedHeader(17)); | |
| entry_buffer_.OnIndexedHeader(17); | |
| } | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnIndexedHeader(62)); | |
| entry_buffer_.OnIndexedHeader(62); | |
| } | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnIndexedHeader(62)); | |
| entry_buffer_.OnIndexedHeader(62); | |
| } | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnIndexedHeader(128)); | |
| entry_buffer_.OnIndexedHeader(128); | |
| } | |
| StrictMock<MockHpackWholeEntryListener> listener2; | |
| entry_buffer_.set_listener(&listener2); | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener2, OnIndexedHeader(100)); | |
| entry_buffer_.OnIndexedHeader(100); | |
| } | |
| } | |
| // OnDynamicTableSizeUpdate is an immediate pass through. | |
| TEST_F(HpackWholeEntryBufferTest, OnDynamicTableSizeUpdate) { | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(4096)); | |
| entry_buffer_.OnDynamicTableSizeUpdate(4096); | |
| } | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(0)); | |
| entry_buffer_.OnDynamicTableSizeUpdate(0); | |
| } | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(1024)); | |
| entry_buffer_.OnDynamicTableSizeUpdate(1024); | |
| } | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(1024)); | |
| entry_buffer_.OnDynamicTableSizeUpdate(1024); | |
| } | |
| StrictMock<MockHpackWholeEntryListener> listener2; | |
| entry_buffer_.set_listener(&listener2); | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(listener2, OnDynamicTableSizeUpdate(0)); | |
| entry_buffer_.OnDynamicTableSizeUpdate(0); | |
| } | |
| } | |
| TEST_F(HpackWholeEntryBufferTest, OnNameIndexAndLiteralValue) { | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kNeverIndexedLiteralHeader, | |
| 123); | |
| entry_buffer_.OnValueStart(false, 10); | |
| entry_buffer_.OnValueData("some data.", 10); | |
| // Force the value to be buffered. | |
| entry_buffer_.BufferStringsIfUnbuffered(); | |
| EXPECT_CALL( | |
| listener_, | |
| OnNameIndexAndLiteralValue( | |
| HpackEntryType::kNeverIndexedLiteralHeader, 123, | |
| AllOf(Property(&HpackDecoderStringBuffer::str, "some data."), | |
| Property(&HpackDecoderStringBuffer::BufferedLength, 10)))); | |
| entry_buffer_.OnValueEnd(); | |
| } | |
| TEST_F(HpackWholeEntryBufferTest, OnLiteralNameAndValue) { | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0); | |
| // Force the name to be buffered by delivering it in two pieces. | |
| entry_buffer_.OnNameStart(false, 9); | |
| entry_buffer_.OnNameData("some-", 5); | |
| entry_buffer_.OnNameData("name", 4); | |
| entry_buffer_.OnNameEnd(); | |
| entry_buffer_.OnValueStart(false, 12); | |
| entry_buffer_.OnValueData("Header Value", 12); | |
| EXPECT_CALL( | |
| listener_, | |
| OnLiteralNameAndValue( | |
| HpackEntryType::kIndexedLiteralHeader, | |
| AllOf(Property(&HpackDecoderStringBuffer::str, "some-name"), | |
| Property(&HpackDecoderStringBuffer::BufferedLength, 9)), | |
| AllOf(Property(&HpackDecoderStringBuffer::str, "Header Value"), | |
| Property(&HpackDecoderStringBuffer::BufferedLength, 0)))); | |
| entry_buffer_.OnValueEnd(); | |
| } | |
| // Verify that a name longer than the allowed size generates an error. | |
| TEST_F(HpackWholeEntryBufferTest, NameTooLong) { | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0); | |
| EXPECT_CALL(listener_, | |
| OnHpackDecodeError(HpackDecodingError::kNameTooLong, _)); | |
| entry_buffer_.OnNameStart(false, kMaxStringSize + 1); | |
| } | |
| // Verify that a value longer than the allowed size generates an error. | |
| TEST_F(HpackWholeEntryBufferTest, ValueTooLong) { | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0); | |
| EXPECT_CALL(listener_, | |
| OnHpackDecodeError( | |
| HpackDecodingError::kValueTooLong, | |
| "Value length (21) of [path] is longer than permitted (20)")); | |
| entry_buffer_.OnNameStart(false, 4); | |
| entry_buffer_.OnNameData("path", 4); | |
| entry_buffer_.OnNameEnd(); | |
| entry_buffer_.OnValueStart(false, kMaxStringSize + 1); | |
| } | |
| // Regression test for b/162141899. | |
| TEST_F(HpackWholeEntryBufferTest, ValueTooLongWithoutName) { | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 1); | |
| EXPECT_CALL(listener_, | |
| OnHpackDecodeError( | |
| HpackDecodingError::kValueTooLong, | |
| "Value length (21) of [] is longer than permitted (20)")); | |
| entry_buffer_.OnValueStart(false, kMaxStringSize + 1); | |
| } | |
| // Verify that a Huffman encoded name with an explicit EOS generates an error | |
| // for an explicit EOS. | |
| TEST_F(HpackWholeEntryBufferTest, NameHuffmanError) { | |
| const char data[] = "\xff\xff\xff"; | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kUnindexedLiteralHeader, | |
| 0); | |
| entry_buffer_.OnNameStart(true, 4); | |
| entry_buffer_.OnNameData(data, 3); | |
| EXPECT_CALL(listener_, | |
| OnHpackDecodeError(HpackDecodingError::kNameHuffmanError, _)); | |
| entry_buffer_.OnNameData(data, 1); | |
| // After an error is reported, the listener is not called again. | |
| EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(8096)).Times(0); | |
| entry_buffer_.OnDynamicTableSizeUpdate(8096); | |
| } | |
| // Verify that a Huffman encoded value that isn't properly terminated with | |
| // a partial EOS symbol generates an error. | |
| TEST_F(HpackWholeEntryBufferTest, ValueHuffmanError) { | |
| const char data[] = "\x00\x00\x00"; | |
| entry_buffer_.OnStartLiteralHeader(HpackEntryType::kNeverIndexedLiteralHeader, | |
| 61); | |
| entry_buffer_.OnValueStart(true, 3); | |
| entry_buffer_.OnValueData(data, 3); | |
| EXPECT_CALL(listener_, | |
| OnHpackDecodeError(HpackDecodingError::kValueHuffmanError, _)); | |
| entry_buffer_.OnValueEnd(); | |
| // After an error is reported, the listener is not called again. | |
| EXPECT_CALL(listener_, OnIndexedHeader(17)).Times(0); | |
| entry_buffer_.OnIndexedHeader(17); | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace http2 |