Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/qpack/qpack_decoder_test.cc b/quic/core/qpack/qpack_decoder_test.cc
new file mode 100644
index 0000000..7292c50
--- /dev/null
+++ b/quic/core/qpack/qpack_decoder_test.cc
@@ -0,0 +1,701 @@
+// Copyright (c) 2018 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+using ::testing::Eq;
+using ::testing::Sequence;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Header Acknowledgement decoder stream instruction with stream_id = 1.
+const char* const kHeaderAcknowledgement = "\x81";
+
+class QpackDecoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackDecoderTest()
+ : qpack_decoder_(&encoder_stream_error_delegate_,
+ &decoder_stream_sender_delegate_),
+ fragment_mode_(GetParam()) {
+ qpack_decoder_.SetMaximumDynamicTableCapacity(1024);
+ }
+
+ ~QpackDecoderTest() override = default;
+
+ void DecodeEncoderStreamData(QuicStringPiece data) {
+ qpack_decoder_.DecodeEncoderStreamData(data);
+ }
+
+ void DecodeHeaderBlock(QuicStringPiece data) {
+ auto fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+ auto progressive_decoder =
+ qpack_decoder_.DecodeHeaderBlock(/* stream_id = */ 1, &handler_);
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ progressive_decoder->Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ }
+ progressive_decoder->EndHeaderBlock();
+ }
+
+ StrictMock<MockEncoderStreamErrorDelegate> encoder_stream_error_delegate_;
+ StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_;
+ StrictMock<MockHeadersHandler> handler_;
+
+ private:
+ QpackDecoder qpack_decoder_;
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(, QpackDecoderTest, Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackDecoderTest, NoPrefix) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Incomplete header data prefix.")));
+
+ // Header Data Prefix is at least two bytes long.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00"));
+}
+
+TEST_P(QpackDecoderTest, EmptyHeaderBlock) {
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0000"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyName) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000"));
+}
+
+TEST_P(QpackDecoderTest, SimpleLiteralEntry) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172"));
+}
+
+TEST_P(QpackDecoderTest, MultipleLiteralEntries) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ QuicString str(127, 'a');
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str)));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000" // prefix
+ "23666f6f03626172" // foo: bar
+ "2700666f6f62616172" // 7 octet long header name, the smallest number
+ // that does not fit on a 3-bit prefix.
+ "7f0061616161616161" // 127 octet long header value, the smallest number
+ "616161616161616161" // that does not fit on a 7-bit prefix.
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "616161616161"));
+}
+
+// Name Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Encoded integer too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff"));
+}
+
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, NameLenExceedsLimit) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("String literal too long.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f"));
+}
+
+// Value Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Encoded integer too large.")));
+
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff"));
+}
+
+// Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, ValueLenExceedsLimit) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("String literal too long.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f"));
+}
+
+TEST_P(QpackDecoderTest, IncompleteHeaderBlock) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Incomplete header block.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanSimple) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")))
+ .Times(4);
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000" // Prefix.
+ "2f0125a849e95ba97d7f" // Huffman-encoded name.
+ "8925a849e95bb8e8b4bf" // Huffman-encoded value.
+ "2703637573746f6d2d6b6579" // Non-Huffman encoded name.
+ "0c637573746f6d2d76616c7565" // Non-Huffman encoded value.
+ "2f0125a849e95ba97d7f" // Huffman-encoded name.
+ "0c637573746f6d2d76616c7565" // Non-Huffman encoded value.
+ "2703637573746f6d2d6b6579" // Non-Huffman encoded name.
+ "8925a849e95bb8e8b4bf")); // Huffman-encoded value.
+}
+
+TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // 'y' ends in 0b0 on the most significant bit of the last byte.
+ // The remaining 7 bits must be a prefix of EOS, which is all 1s.
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7e8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte.
+ // The remaining 5 bits must be a prefix of EOS, which is all 1s.
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4be"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // The trailing EOS prefix must be at most 7 bits long. Appending one octet
+ // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a
+ // prefix of EOS.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "00002f0225a849e95ba97d7fff8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // The trailing EOS prefix must be at most 7 bits long. Appending one octet
+ // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a
+ // prefix of EOS.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "00002f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff"));
+}
+
+TEST_P(QpackDecoderTest, StaticTable) {
+ // A header name that has multiple entries with different values.
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("POST")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("TRACE")));
+
+ // A header name that has a single entry with non-empty value.
+ EXPECT_CALL(handler_,
+ OnHeaderDecoded(Eq("accept-encoding"), Eq("gzip, deflate, br")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("compress")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("")));
+
+ // A header name that has a single entry with empty value.
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo")));
+
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000"));
+}
+
+TEST_P(QpackDecoderTest, TooHighStaticTableIndex) {
+ // This is the last entry in the static table with index 98.
+ EXPECT_CALL(handler_,
+ OnHeaderDecoded(Eq("x-frame-options"), Eq("sameorigin")));
+
+ // Addressing entry 99 should trigger an error.
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Static table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24"));
+}
+
+TEST_P(QpackDecoderTest, DynamicTable) {
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "80035a5a5a" // Add entry with name of dynamic table entry index 0
+ // (relative index) and value "ZZZ".
+ "cf8294e7" // Add entry with name of static table entry index 15
+ // and value "foo".
+ "01")); // Duplicate entry with relative index 1.
+
+ // Now there are four entries in the dynamic table.
+ // Entry 0: "foo", "bar"
+ // Entry 1: "foo", "ZZZ"
+ // Entry 2: ":method", "foo"
+ // Entry 3: "foo", "ZZZ"
+
+ // Use a Sequence to test that mock methods are called in order.
+ Sequence s;
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "83" // Dynamic table entry with relative index 3, absolute index 0.
+ "82" // Dynamic table entry with relative index 2, absolute index 1.
+ "81" // Dynamic table entry with relative index 1, absolute index 2.
+ "80" // Dynamic table entry with relative index 0, absolute index 3.
+ "41025a5a")); // Name of entry 1 (relative index) from dynamic table,
+ // with value "ZZ".
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0502" // Required Insert Count 4 and Delta Base 2.
+ // Base is 4 + 2 = 6.
+ "85" // Dynamic table entry with relative index 5, absolute index 0.
+ "84" // Dynamic table entry with relative index 4, absolute index 1.
+ "83" // Dynamic table entry with relative index 3, absolute index 2.
+ "82" // Dynamic table entry with relative index 2, absolute index 3.
+ "43025a5a")); // Name of entry 3 (relative index) from dynamic table,
+ // with value "ZZ".
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0582" // Required Insert Count 4 and Delta Base 2 with sign bit set.
+ // Base is 4 - 2 - 1 = 1.
+ "80" // Dynamic table entry with relative index 0, absolute index 0.
+ "10" // Dynamic table entry with post-base index 0, absolute index 1.
+ "11" // Dynamic table entry with post-base index 1, absolute index 2.
+ "12" // Dynamic table entry with post-base index 2, absolute index 3.
+ "01025a5a")); // Name of entry 1 (post-base index) from dynamic table,
+ // with value "ZZ".
+}
+
+TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+
+ // Change dynamic table capacity to 32 bytes, smaller than the entry.
+ // This must cause the entry to be evicted.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f01"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Error inserting literal entry.")));
+
+ // Set dynamic table capacity to 34.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f03"));
+ // Add literal entry with name "foo" and value "bar", size is 32 + 3 + 3 = 38.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Invalid static table entry.")));
+
+ // Address invalid static table entry index 99.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("ff2400"));
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Dynamic table entry not found.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "8100")); // Address dynamic table entry with relative index 1. Such
+ // entry does not exist. The most recently added and only
+ // dynamic table entry has relative index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Dynamic table entry not found.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "01")); // Duplicate dynamic table entry with relative index 1. Such
+ // entry does not exist. The most recently added and only
+ // dynamic table entry has relative index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Encoded integer too large.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0280" // Required Insert Count is 1. Base 1 - 1 - 0 = 0 is explicitly
+ // permitted by the spec.
+ "80")); // However, addressing entry with relative index 0 would point to
+ // absolute index -1, which is invalid.
+}
+
+TEST_P(QpackDecoderTest, InvalidNegativeBase) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base.")));
+
+ // Required Insert Count 1, Delta Base 1 with sign bit set, Base would
+ // be 1 - 1 - 1 = -1, but it is not allowed to be negative.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0281"));
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "82")); // Indexed Header Field instruction addressing relative index 2.
+ // This is absolute index 1. Such entry does not exist.
+
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "84")); // Indexed Header Field instruction addressing relative index 4.
+ // This is absolute index -1, which is invalid.
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "4200")); // Literal Header Field with Name Reference instruction
+ // addressing relative index 2. This is absolute index 1. Such
+ // entry does not exist.
+
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "4400")); // Literal Header Field with Name Reference instruction
+ // addressing relative index 4. This is absolute index -1,
+ // which is invalid.
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set.
+ // Base is 2 - 0 - 1 = 1
+ "10")); // Indexed Header Field instruction addressing dynamic table
+ // entry with post-base index 0, absolute index 1. Such entry
+ // does not exist.
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set.
+ // Base is 2 - 0 - 1 = 1
+ "0000")); // Literal Header Field With Name Reference instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. Such entry does not exist.
+}
+
+TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) {
+ EXPECT_CALL(
+ encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Error updating dynamic table capacity.")));
+
+ // Try to update dynamic table capacity to 2048, which exceeds the maximum.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f"));
+}
+
+TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) {
+ // Update dynamic table capacity to 128, which does not exceed the maximum.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61"));
+}
+
+TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) {
+ // Maximum dynamic table capacity is 1024.
+ // MaxEntries is 1024 / 32 = 32.
+ // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64.
+ // A value of 1 cannot be encoded as 65 even though it has the same remainder.
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(
+ Eq("Error decoding Required Insert Count.")));
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("4100"));
+}
+
+TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) {
+ // Maximum dynamic table capacity is 1024.
+ // MaxEntries is 1024 / 32 = 32.
+
+ // Add literal entry with name "foo" and a 600 byte long value. This will fit
+ // in the dynamic table once but not twice.
+ DecodeEncoderStreamData(
+ QuicTextUtils::HexDecode("6294e7" // Name "foo".
+ "7fd903")); // Value length 600.
+ QuicString header_value(600, 'Z');
+ DecodeEncoderStreamData(header_value);
+
+ // Duplicate most recent entry 200 times.
+ DecodeEncoderStreamData(QuicString(200, '\x00'));
+
+ // Now there is only one entry in the dynamic table, with absolute index 200.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value)));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ // Send header block with Required Insert Count = 201.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0a00" // Encoded Required Insert Count 10, Required Insert Count 201,
+ // Delta Base 0, Base 201.
+ "80")); // Emit dynamic table entry with relative index 0.
+}
+
+TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count is 1.
+ "d1")); // But the only instruction references the static table.
+}
+
+TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) {
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0201" // Required Insert Count 1 and Delta Base 1.
+ // Base is 1 + 1 = 2.
+ "80")); // Indexed Header Field instruction addressing dynamic table
+ // entry with relative index 0, absolute index 1. This is not
+ // allowed by Required Insert Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0201" // Required Insert Count 1 and Delta Base 1.
+ // Base is 1 + 1 = 2.
+ "4000")); // Literal Header Field with Name Reference instruction
+ // addressing dynamic table entry with relative index 0,
+ // absolute index 1. This is not allowed by Required Index
+ // Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "10")); // Indexed Header Field with Post-Base Index instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. This is not allowed by Required Insert
+ // Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "0000")); // Literal Header Field with Post-Base Name Reference
+ // instruction addressing dynamic table entry with post-base
+ // index 0, absolute index 1. This is not allowed by Required
+ // Index Count.
+}
+
+TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+ // Duplicate entry.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("00"));
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0300" // Required Insert Count 2 and Delta Base 0.
+ // Base is 2 + 0 = 2.
+ "81")); // Indexed Header Field instruction addressing dynamic table
+ // entry with relative index 1, absolute index 0. Header block
+ // requires insert count of 1, even though Required Insert Count
+ // is 2.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0300" // Required Insert Count 2 and Delta Base 0.
+ // Base is 2 + 0 = 2.
+ "4100")); // Literal Header Field with Name Reference instruction
+ // addressing dynamic table entry with relative index 1,
+ // absolute index 0. Header block requires insert count of 1,
+ // even though Required Insert Count is 2.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set.
+ // Base is 3 - 1 - 1 = 1.
+ "10")); // Indexed Header Field with Post-Base Index instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. Header block requires insert count of 2,
+ // even though Required Insert Count is 3.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set.
+ // Base is 3 - 1 - 1 = 1.
+ "0000")); // Literal Header Field with Post-Base Name Reference
+ // instruction addressing dynamic table entry with post-base
+ // index 0, absolute index 1. Header block requires insert
+ // count of 2, even though Required Insert Count is 3.
+}
+
+} // namespace
+} // namespace test
+} // namespace quic