text: // 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 "quiche/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 "quiche/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 quiche::test::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
  