Commit cd7dc430 authored by Dan Zhang's avatar Dan Zhang Committed by Commit Bot

Synchronise internal QPACK implementation. Add qpack code into BUILD file.

Remove unnecessary forward declare of RandomBase in random_util_helper.h

Merge quic part internal change: 210537018, 211551817

R=rch@chromium.org

Change-Id: Idd96a1b6308dca93d68682490323d3ab7dee3f4f
Reviewed-on: https://chromium-review.googlesource.com/1228838
Commit-Queue: Dan Zhang <danzh@chromium.org>
Reviewed-by: default avatarBence Béky <bnc@chromium.org>
Reviewed-by: default avatarRyan Hamilton <rch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#592437}
parent 48d0cee6
...@@ -1385,6 +1385,13 @@ component("net") { ...@@ -1385,6 +1385,13 @@ component("net") {
"third_party/quic/core/http/spdy_utils.cc", "third_party/quic/core/http/spdy_utils.cc",
"third_party/quic/core/http/spdy_utils.h", "third_party/quic/core/http/spdy_utils.h",
"third_party/quic/core/packet_number_indexed_queue.h", "third_party/quic/core/packet_number_indexed_queue.h",
"third_party/quic/core/qpack/qpack_constants.h",
"third_party/quic/core/qpack/qpack_decoder.cc",
"third_party/quic/core/qpack/qpack_decoder.h",
"third_party/quic/core/qpack/qpack_encoder.cc",
"third_party/quic/core/qpack/qpack_encoder.h",
"third_party/quic/core/qpack/qpack_header_table.cc",
"third_party/quic/core/qpack/qpack_header_table.h",
"third_party/quic/core/quic_ack_listener_interface.cc", "third_party/quic/core/quic_ack_listener_interface.cc",
"third_party/quic/core/quic_ack_listener_interface.h", "third_party/quic/core/quic_ack_listener_interface.h",
"third_party/quic/core/quic_alarm.cc", "third_party/quic/core/quic_alarm.cc",
...@@ -5002,6 +5009,8 @@ test("net_unittests") { ...@@ -5002,6 +5009,8 @@ test("net_unittests") {
"third_party/quic/core/http/quic_header_list_test.cc", "third_party/quic/core/http/quic_header_list_test.cc",
"third_party/quic/core/http/quic_headers_stream_test.cc", "third_party/quic/core/http/quic_headers_stream_test.cc",
"third_party/quic/core/packet_number_indexed_queue_test.cc", "third_party/quic/core/packet_number_indexed_queue_test.cc",
"third_party/quic/core/qpack/qpack_decoder_test.cc",
"third_party/quic/core/qpack/qpack_encoder_test.cc",
"third_party/quic/core/quic_alarm_test.cc", "third_party/quic/core/quic_alarm_test.cc",
"third_party/quic/core/quic_arena_scoped_ptr_test.cc", "third_party/quic/core/quic_arena_scoped_ptr_test.cc",
"third_party/quic/core/quic_bandwidth_test.cc", "third_party/quic/core/quic_bandwidth_test.cc",
...@@ -5091,6 +5100,10 @@ test("net_unittests") { ...@@ -5091,6 +5100,10 @@ test("net_unittests") {
"third_party/quic/core/http/quic_spdy_session_test.cc", "third_party/quic/core/http/quic_spdy_session_test.cc",
"third_party/quic/core/http/quic_spdy_stream_test.cc", "third_party/quic/core/http/quic_spdy_stream_test.cc",
"third_party/quic/core/http/spdy_utils_test.cc", "third_party/quic/core/http/spdy_utils_test.cc",
"third_party/quic/core/qpack/qpack_header_table_test.cc",
"third_party/quic/core/qpack/qpack_round_trip_test.cc",
"third_party/quic/core/qpack/qpack_test_utils.cc",
"third_party/quic/core/qpack/qpack_test_utils.h",
"third_party/quic/core/quic_dispatcher_test.cc", "third_party/quic/core/quic_dispatcher_test.cc",
"third_party/quic/core/quic_lru_cache_test.cc", "third_party/quic/core/quic_lru_cache_test.cc",
"third_party/quic/core/quic_one_block_arena_test.cc", "third_party/quic/core/quic_one_block_arena_test.cc",
......
...@@ -7,8 +7,6 @@ namespace http2 { ...@@ -7,8 +7,6 @@ namespace http2 {
namespace test { namespace test {
class RandomBase;
inline Http2String RandomString(RandomBase* random, inline Http2String RandomString(RandomBase* random,
int len, int len,
Http2StringPiece alphabet) { Http2StringPiece alphabet) {
......
...@@ -15,6 +15,7 @@ namespace quic { ...@@ -15,6 +15,7 @@ namespace quic {
// 5.4.2.1. Indexed Header Field // 5.4.2.1. Indexed Header Field
const uint8_t kIndexedHeaderFieldOpcode = 0b10000000; const uint8_t kIndexedHeaderFieldOpcode = 0b10000000;
const uint8_t kIndexedHeaderFieldOpcodeMask = 0b10000000; const uint8_t kIndexedHeaderFieldOpcodeMask = 0b10000000;
const uint8_t kIndexedHeaderFieldStaticBit = 0b01000000;
const uint8_t kIndexedHeaderFieldPrefixLength = 6; const uint8_t kIndexedHeaderFieldPrefixLength = 6;
// 5.4.2.2. Indexed Header Field With Post-Base Index // 5.4.2.2. Indexed Header Field With Post-Base Index
...@@ -25,6 +26,7 @@ const uint8_t kIndexedHeaderFieldPostBasePrefixLength = 4; ...@@ -25,6 +26,7 @@ const uint8_t kIndexedHeaderFieldPostBasePrefixLength = 4;
// 5.4.2.3. Literal Header Field With Name Reference // 5.4.2.3. Literal Header Field With Name Reference
const uint8_t kLiteralHeaderFieldNameReferenceOpcode = 0b01000000; const uint8_t kLiteralHeaderFieldNameReferenceOpcode = 0b01000000;
const uint8_t kLiteralHeaderFieldNameReferenceOpcodeMask = 0b11000000; const uint8_t kLiteralHeaderFieldNameReferenceOpcodeMask = 0b11000000;
const uint8_t kLiteralHeaderFieldNameReferenceStaticBit = 0b00010000;
const uint8_t kLiteralHeaderFieldNameReferencePrefixLength = 4; const uint8_t kLiteralHeaderFieldNameReferencePrefixLength = 4;
// 5.4.2.4. Literal Header Field With Post-Base Name Reference // 5.4.2.4. Literal Header Field With Post-Base Name Reference
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h" #include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h"
#include "net/third_party/http2/hpack/varint/hpack_varint_decoder.h" #include "net/third_party/http2/hpack/varint/hpack_varint_decoder.h"
#include "net/third_party/quic/core/qpack/qpack_header_table.h"
#include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/quic/platform/api/quic_export.h"
#include "net/third_party/quic/platform/api/quic_string.h" #include "net/third_party/quic/platform/api/quic_string.h"
#include "net/third_party/quic/platform/api/quic_string_piece.h" #include "net/third_party/quic/platform/api/quic_string_piece.h"
...@@ -48,7 +49,11 @@ class QUIC_EXPORT_PRIVATE QpackDecoder { ...@@ -48,7 +49,11 @@ class QUIC_EXPORT_PRIVATE QpackDecoder {
// Class to decode a single header block. // Class to decode a single header block.
class QUIC_EXPORT_PRIVATE ProgressiveDecoder { class QUIC_EXPORT_PRIVATE ProgressiveDecoder {
public: public:
explicit ProgressiveDecoder(HeadersHandlerInterface* handler); ProgressiveDecoder() = delete;
ProgressiveDecoder(QpackHeaderTable* header_table,
HeadersHandlerInterface* handler);
ProgressiveDecoder(const ProgressiveDecoder&) = delete;
ProgressiveDecoder& operator=(const ProgressiveDecoder&) = delete;
~ProgressiveDecoder() = default; ~ProgressiveDecoder() = default;
// Provide a data fragment to decode. // Provide a data fragment to decode.
...@@ -60,24 +65,27 @@ class QUIC_EXPORT_PRIVATE QpackDecoder { ...@@ -60,24 +65,27 @@ class QUIC_EXPORT_PRIVATE QpackDecoder {
private: private:
enum class State { enum class State {
kParseOpcode, // Every instruction starts encoding an integer on the first octet:
kNameLengthStart, // either an index or the length of the name string literal.
kNameLengthResume, kStart,
kNameLengthDone, kVarintResume,
kVarintDone,
// This might be followed by the name as a string literal.
kNameString, kNameString,
// This might be followed by the length of the value.
kValueLengthStart, kValueLengthStart,
kValueLengthResume, kValueLengthResume,
kValueLengthDone, kValueLengthDone,
// This might be followed by the value as a string literal.
kValueString, kValueString,
kDone, kDone,
}; };
// One method for each state. Some take input data and return the number of // One method for each state. Some take input data and return the number of
// octets processed. Some only change internal state. // octets processed. Some only change internal state.
size_t DoParseOpcode(QuicStringPiece data); size_t DoStart(QuicStringPiece data);
size_t DoNameLengthStart(QuicStringPiece data); size_t DoVarintResume(QuicStringPiece data);
size_t DoNameLengthResume(QuicStringPiece data); void DoVarintDone();
void DoNameLengthDone();
size_t DoNameString(QuicStringPiece data); size_t DoNameString(QuicStringPiece data);
size_t DoValueLengthStart(QuicStringPiece data); size_t DoValueLengthStart(QuicStringPiece data);
size_t DoValueLengthResume(QuicStringPiece data); size_t DoValueLengthResume(QuicStringPiece data);
...@@ -87,6 +95,7 @@ class QUIC_EXPORT_PRIVATE QpackDecoder { ...@@ -87,6 +95,7 @@ class QUIC_EXPORT_PRIVATE QpackDecoder {
void OnError(QuicStringPiece error_message); void OnError(QuicStringPiece error_message);
const QpackHeaderTable* const header_table_;
HeadersHandlerInterface* handler_; HeadersHandlerInterface* handler_;
State state_; State state_;
http2::HpackVarintDecoder varint_decoder_; http2::HpackVarintDecoder varint_decoder_;
...@@ -98,19 +107,28 @@ class QUIC_EXPORT_PRIVATE QpackDecoder { ...@@ -98,19 +107,28 @@ class QUIC_EXPORT_PRIVATE QpackDecoder {
// True if a decoding error has been detected. // True if a decoding error has been detected.
bool error_detected_; bool error_detected_;
// Temporarily store decoded length for header name. // The following variables are used to carry information between states
// Must be reset when header name is completely parsed. // within a single header field. That is, a value assigned while decoding
// one header field shall never be used for decoding subsequent header
// fields.
// True if the header field name is encoded as a string literal.
bool literal_name_;
// True if the header field value is encoded as a string literal.
bool literal_value_;
// Decoded length for header name.
size_t name_length_; size_t name_length_;
// Temporarily store decoded length for header value. // Decoded length for header value.
// Must be reset when header value is completely parsed.
size_t value_length_; size_t value_length_;
// Temporarily store whether the currently parsed string (name or value) is // Whether the currently parsed string (name or value) is
// Huffman encoded. // Huffman encoded.
bool is_huffman_; bool is_huffman_;
// Temporarily store decoded header name and value. // Decoded header name and value.
QuicString name_; QuicString name_;
QuicString value_; QuicString value_;
}; };
...@@ -120,6 +138,9 @@ class QUIC_EXPORT_PRIVATE QpackDecoder { ...@@ -120,6 +138,9 @@ class QUIC_EXPORT_PRIVATE QpackDecoder {
// is destroyed or the decoder calls |handler->OnHeaderBlockEnd()|. // is destroyed or the decoder calls |handler->OnHeaderBlockEnd()|.
std::unique_ptr<ProgressiveDecoder> DecodeHeaderBlock( std::unique_ptr<ProgressiveDecoder> DecodeHeaderBlock(
HeadersHandlerInterface* handler); HeadersHandlerInterface* handler);
private:
QpackHeaderTable header_table_;
}; };
} // namespace quic } // namespace quic
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
#include "net/third_party/quic/platform/api/quic_test.h" #include "net/third_party/quic/platform/api/quic_test.h"
#include "net/third_party/quic/platform/api/quic_text_utils.h" #include "net/third_party/quic/platform/api/quic_text_utils.h"
#include "net/third_party/spdy/core/spdy_header_block.h" #include "net/third_party/spdy/core/spdy_header_block.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::StrictMock; using ::testing::StrictMock;
using ::testing::Values; using ::testing::Values;
...@@ -70,36 +68,43 @@ TEST_P(QpackDecoderTest, Empty) { ...@@ -70,36 +68,43 @@ TEST_P(QpackDecoderTest, Empty) {
} }
TEST_P(QpackDecoderTest, EmptyName) { TEST_P(QpackDecoderTest, EmptyName) {
EXPECT_CALL(handler_, OnHeaderDecoded("", "foo")); EXPECT_CALL(handler_,
OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece("foo")));
EXPECT_CALL(handler_, OnDecodingCompleted()); EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode("2003666f6f")); Decode(QuicTextUtils::HexDecode("2003666f6f"));
} }
TEST_P(QpackDecoderTest, EmptyValue) { TEST_P(QpackDecoderTest, EmptyValue) {
EXPECT_CALL(handler_, OnHeaderDecoded("foo", "")); EXPECT_CALL(handler_,
OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("")));
EXPECT_CALL(handler_, OnDecodingCompleted()); EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode("23666f6f00")); Decode(QuicTextUtils::HexDecode("23666f6f00"));
} }
TEST_P(QpackDecoderTest, EmptyNameAndValue) { TEST_P(QpackDecoderTest, EmptyNameAndValue) {
EXPECT_CALL(handler_, OnHeaderDecoded("", "")); EXPECT_CALL(handler_,
OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece("")));
EXPECT_CALL(handler_, OnDecodingCompleted()); EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode("2000")); Decode(QuicTextUtils::HexDecode("2000"));
} }
TEST_P(QpackDecoderTest, Simple) { TEST_P(QpackDecoderTest, Simple) {
EXPECT_CALL(handler_, OnHeaderDecoded("foo", "bar")); EXPECT_CALL(handler_,
OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar")));
EXPECT_CALL(handler_, OnDecodingCompleted()); EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode("23666f6f03626172")); Decode(QuicTextUtils::HexDecode("23666f6f03626172"));
} }
TEST_P(QpackDecoderTest, Multiple) { TEST_P(QpackDecoderTest, Multiple) {
EXPECT_CALL(handler_, OnHeaderDecoded("foo", "bar")); EXPECT_CALL(handler_,
EXPECT_CALL(handler_, OnHeaderDecoded("foobaar", std::string(127, 'a'))); OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar")));
QuicString str(127, 'a');
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foobaar"),
QuicStringPiece(str)));
EXPECT_CALL(handler_, OnDecodingCompleted()); EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode( Decode(QuicTextUtils::HexDecode(
...@@ -115,39 +120,56 @@ TEST_P(QpackDecoderTest, Multiple) { ...@@ -115,39 +120,56 @@ TEST_P(QpackDecoderTest, Multiple) {
} }
TEST_P(QpackDecoderTest, NameLenTooLarge) { TEST_P(QpackDecoderTest, NameLenTooLarge) {
EXPECT_CALL( EXPECT_CALL(handler_, OnDecodingErrorDetected(
handler_, QuicStringPiece("Encoded integer too large.")));
OnDecodingErrorDetected(
"NameLen too large in literal header field without reference."));
Decode(QuicTextUtils::HexDecode("27ffffffffff")); Decode(QuicTextUtils::HexDecode("27ffffffffff"));
} }
TEST_P(QpackDecoderTest, ValueLenTooLarge) { TEST_P(QpackDecoderTest, ValueLenTooLarge) {
EXPECT_CALL( EXPECT_CALL(handler_,
handler_, OnDecodingErrorDetected(QuicStringPiece("ValueLen too large.")));
OnDecodingErrorDetected(
"ValueLen too large in literal header field without reference."));
Decode(QuicTextUtils::HexDecode("23666f6f7fffffffffff")); Decode(QuicTextUtils::HexDecode("23666f6f7fffffffffff"));
} }
TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { TEST_P(QpackDecoderTest, IncompleteHeaderBlock) {
EXPECT_CALL(handler_, OnDecodingErrorDetected("Incomplete header block.")); EXPECT_CALL(handler_, OnDecodingErrorDetected(
QuicStringPiece("Incomplete header block.")));
Decode(QuicTextUtils::HexDecode("2366")); Decode(QuicTextUtils::HexDecode("2366"));
} }
TEST_P(QpackDecoderTest, HuffmanSimple) { TEST_P(QpackDecoderTest, HuffmanSimple) {
EXPECT_CALL(handler_, OnHeaderDecoded("custom-key", "custom-value")); EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"),
QuicStringPiece("custom-value")));
EXPECT_CALL(handler_, OnDecodingCompleted()); EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode("2f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); Decode(QuicTextUtils::HexDecode(
QuicStringPiece("2f0125a849e95ba97d7f8925a849e95bb8e8b4bf")));
}
TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) {
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"),
QuicStringPiece("custom-value")))
.Times(4);
EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode(
"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) { TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) {
EXPECT_CALL(handler_, EXPECT_CALL(handler_, OnDecodingErrorDetected(
OnDecodingErrorDetected("Error in Huffman-encoded name.")); QuicStringPiece("Error in Huffman-encoded name.")));
// 'y' ends in 0b0 on the most significant bit of the last byte. // '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. // The remaining 7 bits must be a prefix of EOS, which is all 1s.
...@@ -155,8 +177,8 @@ TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) { ...@@ -155,8 +177,8 @@ TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) {
} }
TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) {
EXPECT_CALL(handler_, EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
OnDecodingErrorDetected("Error in Huffman-encoded value.")); "Error in Huffman-encoded value.")));
// 'e' ends in 0b101, taking up the 3 most significant bits of the last byte. // '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. // The remaining 5 bits must be a prefix of EOS, which is all 1s.
...@@ -164,8 +186,8 @@ TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { ...@@ -164,8 +186,8 @@ TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) {
} }
TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) {
EXPECT_CALL(handler_, EXPECT_CALL(handler_, OnDecodingErrorDetected(
OnDecodingErrorDetected("Error in Huffman-encoded name.")); QuicStringPiece("Error in Huffman-encoded name.")));
// The trailing EOS prefix must be at most 7 bits long. Appending one octet // 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 // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a
...@@ -175,8 +197,8 @@ TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { ...@@ -175,8 +197,8 @@ TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) {
} }
TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) { TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) {
EXPECT_CALL(handler_, EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
OnDecodingErrorDetected("Error in Huffman-encoded value.")); "Error in Huffman-encoded value.")));
// The trailing EOS prefix must be at most 7 bits long. Appending one octet // 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 // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a
...@@ -185,6 +207,58 @@ TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) { ...@@ -185,6 +207,58 @@ TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) {
QuicTextUtils::HexDecode("2f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff")); QuicTextUtils::HexDecode("2f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff"));
} }
TEST_P(QpackDecoderTest, StaticTable) {
// A header name that has multiple entries with different values.
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
QuicStringPiece("GET")));
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
QuicStringPiece("POST")));
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
QuicStringPiece("CONNECT")));
// A header name that has a single entry with non-empty value.
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"),
QuicStringPiece("gzip, deflate")));
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"),
QuicStringPiece("brotli")));
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"),
QuicStringPiece("")));
// A header name that has a single entry with empty value.
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("cache-control"),
QuicStringPiece("")));
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("cache-control"),
QuicStringPiece("foo")));
EXPECT_CALL(handler_, OnDecodingCompleted());
Decode(QuicTextUtils::HexDecode(
"c2c35207434f4e4e454354d05f010662726f746c695f0100d85f0903666f6f"));
}
TEST_P(QpackDecoderTest, TooLowStaticTableIndex) {
// This is the first entry in the static table with index 1.
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":authority"),
QuicStringPiece("")));
// Addressing entry 0 should trigger an error.
EXPECT_CALL(handler_, OnDecodingErrorDetected(
QuicStringPiece("Invalid static table index.")));
Decode(QuicTextUtils::HexDecode("c1c0"));
}
TEST_P(QpackDecoderTest, TooHighStaticTableIndex) {
// This is the last entry in the static table with index 61.
EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("www-authenticate"),
QuicStringPiece("")));
// Addressing entry 62 should trigger an error.
EXPECT_CALL(handler_, OnDecodingErrorDetected(
QuicStringPiece("Invalid static table index.")));
Decode(QuicTextUtils::HexDecode("fdfe"));
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace quic } // namespace quic
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "net/third_party/quic/core/qpack/qpack_header_table.h"
#include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/quic/platform/api/quic_export.h"
#include "net/third_party/spdy/core/hpack/hpack_encoder.h" #include "net/third_party/spdy/core/hpack/hpack_encoder.h"
#include "net/third_party/spdy/core/spdy_header_block.h" #include "net/third_party/spdy/core/spdy_header_block.h"
...@@ -26,6 +27,9 @@ class QUIC_EXPORT_PRIVATE QpackEncoder { ...@@ -26,6 +27,9 @@ class QUIC_EXPORT_PRIVATE QpackEncoder {
// during the lifetime of the returned ProgressiveEncoder instance. // during the lifetime of the returned ProgressiveEncoder instance.
std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderSet( std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderSet(
const spdy::SpdyHeaderBlock* header_list); const spdy::SpdyHeaderBlock* header_list);
private:
QpackHeaderTable header_table_;
}; };
} // namespace quic } // namespace quic
......
...@@ -95,6 +95,36 @@ TEST_P(QpackEncoderTest, Multiple) { ...@@ -95,6 +95,36 @@ TEST_P(QpackEncoderTest, Multiple) {
output); output);
} }
TEST_P(QpackEncoderTest, StaticTable) {
{
spdy::SpdyHeaderBlock header_list;
header_list[":method"] = "GET";
header_list["accept-encoding"] = "gzip, deflate";
header_list["cache-control"] = "";
QuicString output = Encode(&header_list);
EXPECT_EQ(QuicTextUtils::HexDecode("c2d0d8"), output);
}
{
spdy::SpdyHeaderBlock header_list;
header_list[":method"] = "POST";
header_list["accept-encoding"] = "brotli";
header_list["cache-control"] = "foo";
QuicString output = Encode(&header_list);
DLOG(INFO) << QuicTextUtils::HexEncode(output);
EXPECT_EQ(QuicTextUtils::HexDecode("c35f01858ec3a6837f5f098294e7"), output);
}
{
spdy::SpdyHeaderBlock header_list;
header_list[":method"] = "CONNECT";
header_list["accept-encoding"] = "";
QuicString output = Encode(&header_list);
EXPECT_EQ(QuicTextUtils::HexDecode("5207434f4e4e4543545f0100"), output);
}
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace quic } // namespace quic
#include "net/third_party/quic/core/qpack/qpack_header_table.h"
#include "base/logging.h"
#include "net/third_party/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/spdy/core/hpack/hpack_entry.h"
#include "net/third_party/spdy/core/hpack/hpack_static_table.h"
namespace quic {
// Currently using HPACK static tables.
// TODO(bnc): QPACK is likely to get its own static table. When this happens,
// fork HpackStaticTable code and modify static table.
QpackHeaderTable::QpackHeaderTable()
: static_entries_(spdy::ObtainHpackStaticTable().GetStaticEntries()),
static_index_(spdy::ObtainHpackStaticTable().GetStaticIndex()),
static_name_index_(spdy::ObtainHpackStaticTable().GetStaticNameIndex()) {}
QpackHeaderTable::~QpackHeaderTable() = default;
const spdy::HpackEntry* QpackHeaderTable::LookupEntry(size_t index) const {
// Static table indexing starts with 1.
if (index == 0 || index > static_entries_.size()) {
return nullptr;
}
return &static_entries_[index - 1];
}
QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField(
QuicStringPiece name,
QuicStringPiece value,
size_t* index) const {
spdy::HpackEntry query(name, value);
UnorderedEntrySet::const_iterator static_index_it =
static_index_.find(&query);
if (static_index_it != static_index_.end()) {
DCHECK((*static_index_it)->IsStatic());
// Static table indexing starts with 1.
*index = (*static_index_it)->InsertionIndex() + 1;
return MatchType::kNameAndValue;
}
NameToEntryMap::const_iterator static_name_index_it =
static_name_index_.find(name);
if (static_name_index_it != static_name_index_.end()) {
DCHECK(static_name_index_it->second->IsStatic());
// Static table indexing starts with 1.
*index = static_name_index_it->second->InsertionIndex() + 1;
return MatchType::kName;
}
return MatchType::kNoMatch;
}
} // namespace quic
#ifndef NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
#define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
#include <cstddef>
#include "net/third_party/quic/platform/api/quic_export.h"
#include "net/third_party/quic/platform/api/quic_string_piece.h"
#include "net/third_party/spdy/core/hpack/hpack_header_table.h"
namespace quic {
// This class manages the QPACK static and dynamic tables.
// TODO(bnc): Implement dynamic table.
class QUIC_EXPORT_PRIVATE QpackHeaderTable {
public:
using EntryTable = spdy::HpackHeaderTable::EntryTable;
using EntryHasher = spdy::HpackHeaderTable::EntryHasher;
using EntriesEq = spdy::HpackHeaderTable::EntriesEq;
using UnorderedEntrySet = spdy::HpackHeaderTable::UnorderedEntrySet;
using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap;
// Result of header table lookup.
enum class MatchType { kNameAndValue, kName, kNoMatch };
QpackHeaderTable();
QpackHeaderTable(const QpackHeaderTable&) = delete;
QpackHeaderTable& operator=(const QpackHeaderTable&) = delete;
~QpackHeaderTable();
// Returns the entry at given index, or nullptr on error.
const spdy::HpackEntry* LookupEntry(size_t index) const;
// Returns the index of an entry with matching name and value if such exists,
// otherwise one with matching name is such exists.
MatchType FindHeaderField(QuicStringPiece name,
QuicStringPiece value,
size_t* index) const;
private:
// |static_entries_|, |static_index_|, |static_name_index_| are owned by
// HpackStaticTable singleton.
// Tracks HpackEntries by index.
const EntryTable& static_entries_;
// Tracks the unique HpackEntry for a given header name and value.
const UnorderedEntrySet& static_index_;
// Tracks the first static entry for each name in the static table.
const NameToEntryMap& static_name_index_;
};
} // namespace quic
#endif // NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
#include "net/third_party/quic/core/qpack/qpack_header_table.h"
#include "net/third_party/quic/platform/api/quic_test.h"
#include "net/third_party/spdy/core/hpack/hpack_entry.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace quic {
namespace test {
namespace {
class QpackHeaderTableTest : public QuicTest {
protected:
QpackHeaderTable table_;
};
TEST_F(QpackHeaderTableTest, LookupEntry) {
const auto* entry = table_.LookupEntry(0);
EXPECT_FALSE(entry);
entry = table_.LookupEntry(1);
EXPECT_EQ(":authority", entry->name());
EXPECT_EQ("", entry->value());
entry = table_.LookupEntry(2);
EXPECT_EQ(":method", entry->name());
EXPECT_EQ("GET", entry->value());
entry = table_.LookupEntry(61);
EXPECT_EQ("www-authenticate", entry->name());
EXPECT_EQ("", entry->value());
entry = table_.LookupEntry(62);
EXPECT_FALSE(entry);
}
TEST_F(QpackHeaderTableTest, FindHeaderField) {
// A header name that has multiple entries with different values.
size_t index = 0;
QpackHeaderTable::MatchType matchtype =
table_.FindHeaderField(":method", "GET", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kNameAndValue, matchtype);
EXPECT_EQ(2u, index);
matchtype = table_.FindHeaderField(":method", "POST", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kNameAndValue, matchtype);
EXPECT_EQ(3u, index);
matchtype = table_.FindHeaderField(":method", "CONNECT", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kName, matchtype);
EXPECT_EQ(2u, index);
// A header name that has a single entry with non-empty value.
matchtype =
table_.FindHeaderField("accept-encoding", "gzip, deflate", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kNameAndValue, matchtype);
EXPECT_EQ(16u, index);
matchtype = table_.FindHeaderField("accept-encoding", "brotli", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kName, matchtype);
EXPECT_EQ(16u, index);
matchtype = table_.FindHeaderField("accept-encoding", "", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kName, matchtype);
EXPECT_EQ(16u, index);
// A header name that has a single entry with empty value.
matchtype = table_.FindHeaderField("cache-control", "", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kNameAndValue, matchtype);
EXPECT_EQ(24u, index);
matchtype = table_.FindHeaderField("cache-control", "foo", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kName, matchtype);
EXPECT_EQ(24u, index);
// No matching header name.
matchtype = table_.FindHeaderField("foo", "", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kNoMatch, matchtype);
matchtype = table_.FindHeaderField("foo", "bar", &index);
EXPECT_EQ(QpackHeaderTable::MatchType::kNoMatch, matchtype);
}
} // namespace
} // namespace test
} // namespace quic
...@@ -90,6 +90,41 @@ TEST_P(QpackRoundTripTest, MultipleWithLongEntries) { ...@@ -90,6 +90,41 @@ TEST_P(QpackRoundTripTest, MultipleWithLongEntries) {
EXPECT_EQ(header_list, output); EXPECT_EQ(header_list, output);
} }
TEST_P(QpackRoundTripTest, StaticTable) {
{
spdy::SpdyHeaderBlock header_list;
header_list[":method"] = "GET";
header_list["accept-encoding"] = "gzip, deflate";
header_list["cache-control"] = "";
header_list["foo"] = "bar";
header_list[":path"] = "/";
spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
EXPECT_EQ(header_list, output);
}
{
spdy::SpdyHeaderBlock header_list;
header_list[":method"] = "POST";
header_list["accept-encoding"] = "brotli";
header_list["cache-control"] = "foo";
header_list["foo"] = "bar";
header_list[":path"] = "/";
spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
EXPECT_EQ(header_list, output);
}
{
spdy::SpdyHeaderBlock header_list;
header_list[":method"] = "CONNECT";
header_list["accept-encoding"] = "";
header_list["foo"] = "bar";
header_list[":path"] = "/";
spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
EXPECT_EQ(header_list, output);
}
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace quic } // namespace quic
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <limits> #include <limits>
#include "net/third_party/quic/core/qpack/qpack_encoder.h" #include "net/third_party/quic/core/qpack/qpack_encoder.h"
#include "net/third_party/quic/platform/api/quic_test.h"
namespace quic { namespace quic {
namespace test { namespace test {
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include "net/third_party/quic/platform/api/quic_string.h" #include "net/third_party/quic/platform/api/quic_string.h"
#include "net/third_party/quic/platform/api/quic_string_piece.h" #include "net/third_party/quic/platform/api/quic_string_piece.h"
#include "net/third_party/spdy/core/spdy_header_block.h" #include "net/third_party/spdy/core/spdy_header_block.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace quic { namespace quic {
namespace test { namespace test {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment