diff options
author | jgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-04 06:04:55 +0000 |
---|---|---|
committer | jgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-04 06:04:55 +0000 |
commit | 22e40462db62f6ec706f6c2ab77fc8a192482fd9 (patch) | |
tree | 9b6976dc5a46de239d6876d16a1ec4f16c3c38e1 | |
parent | 19e273c317cee4a2952da8687b569ad7083d0c98 (diff) | |
download | chromium_src-22e40462db62f6ec706f6c2ab77fc8a192482fd9.zip chromium_src-22e40462db62f6ec706f6c2ab77fc8a192482fd9.tar.gz chromium_src-22e40462db62f6ec706f6c2ab77fc8a192482fd9.tar.bz2 |
Enable HPACK Huffman decoding.
HpackDecoder is now constructed with an HpackHuffmanTable instance, and
applies it in the decoding of Huffman-encoded literals.
New HpackDecoder & HpackInputStream tests added for Huffman decoding,
including the full set of Huffman-encoded request and response examples
from the draft specification.
This lands server change 61903931 by jgraettinger.
BUG=339578
Review URL: https://codereview.chromium.org/182393002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@254691 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/spdy/hpack_decoder.cc | 34 | ||||
-rw-r--r-- | net/spdy/hpack_decoder.h | 29 | ||||
-rw-r--r-- | net/spdy/hpack_decoder_test.cc | 385 | ||||
-rw-r--r-- | net/spdy/hpack_huffman_table.cc | 8 | ||||
-rw-r--r-- | net/spdy/hpack_huffman_table.h | 6 | ||||
-rw-r--r-- | net/spdy/hpack_huffman_table_test.cc | 54 | ||||
-rw-r--r-- | net/spdy/hpack_input_stream.cc | 45 | ||||
-rw-r--r-- | net/spdy/hpack_input_stream.h | 12 | ||||
-rw-r--r-- | net/spdy/hpack_input_stream_test.cc | 110 |
9 files changed, 531 insertions, 152 deletions
diff --git a/net/spdy/hpack_decoder.cc b/net/spdy/hpack_decoder.cc index 7826ddd..f44bcca5 100644 --- a/net/spdy/hpack_decoder.cc +++ b/net/spdy/hpack_decoder.cc @@ -11,12 +11,19 @@ namespace net { using base::StringPiece; +using std::string; -HpackDecoder::HpackDecoder(uint32 max_string_literal_size) - : max_string_literal_size_(max_string_literal_size) {} +HpackDecoder::HpackDecoder(const HpackHuffmanTable& table, + uint32 max_string_literal_size) + : max_string_literal_size_(max_string_literal_size), + huffman_table_(table) {} HpackDecoder::~HpackDecoder() {} +void HpackDecoder::SetMaxHeadersSize(uint32 max_size) { + context_.SetMaxSize(max_size); +} + bool HpackDecoder::DecodeHeaderSet(StringPiece input, HpackHeaderPairVector* header_list) { HpackInputStream input_stream(max_string_literal_size_, input); @@ -84,7 +91,7 @@ bool HpackDecoder::ProcessNextHeaderRepresentation( return false; StringPiece value; - if (!DecodeNextValue(input_stream, &value)) + if (!DecodeNextStringLiteral(input_stream, false, &value)) return false; header_list->push_back( @@ -99,7 +106,7 @@ bool HpackDecoder::ProcessNextHeaderRepresentation( return false; StringPiece value; - if (!DecodeNextValue(input_stream, &value)) + if (!DecodeNextStringLiteral(input_stream, false, &value)) return false; header_list->push_back( @@ -126,7 +133,7 @@ bool HpackDecoder::DecodeNextName( return false; if (index_or_zero == 0) - return input_stream->DecodeNextStringLiteral(next_name); + return DecodeNextStringLiteral(input_stream, true, next_name); uint32 index = index_or_zero; if (index > context_.GetEntryCount()) @@ -136,9 +143,20 @@ bool HpackDecoder::DecodeNextName( return true; } -bool HpackDecoder::DecodeNextValue( - HpackInputStream* input_stream, StringPiece* next_name) { - return input_stream->DecodeNextStringLiteral(next_name); +bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, + bool is_key, + StringPiece* output) { + if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { + string* buffer = is_key ? &huffman_key_buffer_ : &huffman_value_buffer_; + bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer); + *output = StringPiece(*buffer); + return result; + } else if (input_stream->MatchPrefixAndConsume( + kStringLiteralIdentityEncoded)) { + return input_stream->DecodeNextIdentityString(output); + } else { + return false; + } } } // namespace net diff --git a/net/spdy/hpack_decoder.h b/net/spdy/hpack_decoder.h index 3230fde..d8933c4 100644 --- a/net/spdy/hpack_decoder.h +++ b/net/spdy/hpack_decoder.h @@ -13,23 +13,32 @@ #include "base/strings/string_piece.h" #include "net/base/net_export.h" #include "net/spdy/hpack_encoding_context.h" -#include "net/spdy/hpack_input_stream.h" // For HpackHeaderPairVector. +#include "net/spdy/hpack_input_stream.h" namespace net { // An HpackDecoder decodes header sets as outlined in // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05 -// . + +class HpackHuffmanTable; + class NET_EXPORT_PRIVATE HpackDecoder { public: - explicit HpackDecoder(uint32 max_string_literal_size); + // |table| is an initialized HPACK request or response Huffman table, + // having an externally-managed lifetime which spans beyond HpackDecoder. + explicit HpackDecoder(const HpackHuffmanTable& table, + uint32 max_string_literal_size); ~HpackDecoder(); + // Set the maximum size of the headers table used by the decoder. + void SetMaxHeadersSize(uint32 max_size); + // Decodes the given string into the given header set. Returns // whether or not the decoding was successful. // - // TODO(akalin): Emit the headers via a callback/delegate instead of - // putting them into a vector. + // TODO(jgraettinger): Parse headers using incrementally-receieved data, + // and emit headers via a delegate. (See SpdyHeadersBlockParser and + // SpdyHeadersHandlerInterface). bool DecodeHeaderSet(base::StringPiece input, HpackHeaderPairVector* header_list); @@ -44,6 +53,11 @@ class NET_EXPORT_PRIVATE HpackDecoder { const uint32 max_string_literal_size_; HpackEncodingContext context_; + // Huffman table to be applied to decoded Huffman literals, + // and scratch space for storing those decoded literals. + const HpackHuffmanTable& huffman_table_; + std::string huffman_key_buffer_, huffman_value_buffer_; + // Tries to process the next header representation and maybe emit // headers into |header_list| according to it. Returns true if // successful, or false if an error was encountered. @@ -53,8 +67,9 @@ class NET_EXPORT_PRIVATE HpackDecoder { bool DecodeNextName(HpackInputStream* input_stream, base::StringPiece* next_name); - bool DecodeNextValue(HpackInputStream* input_stream, - base::StringPiece* next_name); + bool DecodeNextStringLiteral(HpackInputStream* input_stream, + bool is_header_key, // As distinct from a value. + base::StringPiece* output); DISALLOW_COPY_AND_ASSIGN(HpackDecoder); }; diff --git a/net/spdy/hpack_decoder_test.cc b/net/spdy/hpack_decoder_test.cc index 5fa7377..399cdc2 100644 --- a/net/spdy/hpack_decoder_test.cc +++ b/net/spdy/hpack_decoder_test.cc @@ -8,9 +8,11 @@ #include <string> #include "base/basictypes.h" +#include "base/logging.h" #include "base/strings/string_piece.h" #include "net/spdy/hpack_encoder.h" #include "net/spdy/hpack_input_stream.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -20,63 +22,89 @@ namespace { using base::StringPiece; using std::string; +using testing::ElementsAre; +using testing::Pair; + +const size_t kLiteralBound = 1024; + +class HpackDecoderTest : public ::testing::Test { + protected: + HpackDecoderTest() + : decoder_(huffman_table_, kLiteralBound) {} + + virtual void SetUp() { + } + + // Utility function to decode a string into a header set, assuming + // that the emitted headers have unique names. + std::map<string, string> DecodeUniqueHeaderSet(StringPiece str) { + HpackHeaderPairVector header_list; + EXPECT_TRUE(decoder_.DecodeHeaderSet(str, &header_list)); + std::map<string, string> header_set( + header_list.begin(), header_list.end()); + // Make sure |header_list| has no duplicates. + EXPECT_EQ(header_set.size(), header_list.size()); + return header_set; + } + + HpackHuffmanTable huffman_table_; + HpackDecoder decoder_; +}; + // Decoding an encoded name with a valid string literal should work. -TEST(HpackDecoderTest, DecodeNextNameLiteral) { - HpackDecoder decoder(kuint32max); - HpackInputStream input_stream(kuint32max, StringPiece("\x00\x04name", 6)); +TEST_F(HpackDecoderTest, DecodeNextNameLiteral) { + HpackInputStream input_stream(kLiteralBound, StringPiece("\x00\x04name", 6)); StringPiece string_piece; - EXPECT_TRUE(decoder.DecodeNextNameForTest(&input_stream, &string_piece)); + EXPECT_TRUE(decoder_.DecodeNextNameForTest(&input_stream, &string_piece)); EXPECT_EQ("name", string_piece); EXPECT_FALSE(input_stream.HasMoreData()); } +TEST_F(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) { + { + std::vector<HpackHuffmanSymbol> code = HpackRequestHuffmanCode(); + EXPECT_TRUE(huffman_table_.Initialize(&code[0], code.size())); + } + char input[] = "\x00\x88\x4e\xb0\x8b\x74\x97\x90\xfa\x7f"; + StringPiece foo(input, arraysize(input) - 1); + HpackInputStream input_stream(kLiteralBound, foo); + + StringPiece string_piece; + EXPECT_TRUE(decoder_.DecodeNextNameForTest(&input_stream, &string_piece)); + EXPECT_EQ("custom-key", string_piece); + EXPECT_FALSE(input_stream.HasMoreData()); +} + // Decoding an encoded name with a valid index should work. -TEST(HpackDecoderTest, DecodeNextNameIndexed) { - HpackDecoder decoder(kuint32max); - HpackInputStream input_stream(kuint32max, "\x01"); +TEST_F(HpackDecoderTest, DecodeNextNameIndexed) { + HpackInputStream input_stream(kLiteralBound, "\x01"); StringPiece string_piece; - EXPECT_TRUE(decoder.DecodeNextNameForTest(&input_stream, &string_piece)); + EXPECT_TRUE(decoder_.DecodeNextNameForTest(&input_stream, &string_piece)); EXPECT_EQ(":authority", string_piece); EXPECT_FALSE(input_stream.HasMoreData()); } // Decoding an encoded name with an invalid index should fail. -TEST(HpackDecoderTest, DecodeNextNameInvalidIndex) { +TEST_F(HpackDecoderTest, DecodeNextNameInvalidIndex) { // One more than the number of static table entries. - HpackDecoder decoder(kuint32max); - HpackInputStream input_stream(kuint32max, "\x3d"); + HpackInputStream input_stream(kLiteralBound, "\x3d"); StringPiece string_piece; - EXPECT_FALSE(decoder.DecodeNextNameForTest(&input_stream, &string_piece)); -} - -// Utility function to decode a string into a header set, assuming -// that the emitted headers have unique names. -std::map<string, string> DecodeUniqueHeaderSet( - HpackDecoder* decoder, StringPiece str) { - HpackHeaderPairVector header_list; - EXPECT_TRUE(decoder->DecodeHeaderSet(str, &header_list)); - std::map<string, string> header_set( - header_list.begin(), header_list.end()); - // Make sure |header_list| has no duplicates. - EXPECT_EQ(header_set.size(), header_list.size()); - return header_set; + EXPECT_FALSE(decoder_.DecodeNextNameForTest(&input_stream, &string_piece)); } // Decoding an indexed header should toggle the index's presence in // the reference set, making a copy of static table entries if // necessary. It should also emit the header if toggled on (and only // as many times as it was toggled on). -TEST(HpackDecoderTest, IndexedHeaderBasic) { - HpackDecoder decoder(kuint32max); - +TEST_F(HpackDecoderTest, IndexedHeaderBasic) { // Toggle on static table entry #2 (and make a copy at index #1), // then toggle on static table entry #5 (which is now #6 because of // the copy of #2). std::map<string, string> header_set1 = - DecodeUniqueHeaderSet(&decoder, "\x82\x86"); + DecodeUniqueHeaderSet("\x82\x86"); std::map<string, string> expected_header_set1; expected_header_set1[":method"] = "GET"; expected_header_set1[":path"] = "/index.html"; @@ -86,18 +114,16 @@ TEST(HpackDecoderTest, IndexedHeaderBasic) { expected_header_set2[":path"] = "/index.html"; // Toggle off the copy of static table entry #5. std::map<string, string> header_set2 = - DecodeUniqueHeaderSet(&decoder, "\x82"); + DecodeUniqueHeaderSet("\x82"); EXPECT_EQ(expected_header_set2, header_set2); } // Decoding an indexed header with index 0 should clear the reference // set. -TEST(HpackDecoderTest, IndexedHeaderZero) { - HpackDecoder decoder(kuint32max); - +TEST_F(HpackDecoderTest, IndexedHeaderZero) { // Toggle on a couple of headers. std::map<string, string> header_set1 = - DecodeUniqueHeaderSet(&decoder, "\x82\x86"); + DecodeUniqueHeaderSet("\x82\x86"); std::map<string, string> expected_header_set1; expected_header_set1[":method"] = "GET"; expected_header_set1[":path"] = "/index.html"; @@ -105,21 +131,20 @@ TEST(HpackDecoderTest, IndexedHeaderZero) { // Toggle index 0 to clear the reference set. std::map<string, string> header_set2 = - DecodeUniqueHeaderSet(&decoder, "\x80"); + DecodeUniqueHeaderSet("\x80"); std::map<string, string> expected_header_set2; EXPECT_EQ(expected_header_set2, header_set2); } // Decoding two valid encoded literal headers with no indexing should // work. -TEST(HpackDecoderTest, LiteralHeaderNoIndexing) { - HpackDecoder decoder(kuint32max); +TEST_F(HpackDecoderTest, LiteralHeaderNoIndexing) { HpackHeaderPairVector header_list; // First header with indexed name, second header with string literal // name. std::map<string, string> header_set = DecodeUniqueHeaderSet( - &decoder, "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2"); + "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2"); std::map<string, string> expected_header_set; expected_header_set[":path"] = "/sample/path"; @@ -130,10 +155,8 @@ TEST(HpackDecoderTest, LiteralHeaderNoIndexing) { // Decoding two valid encoded literal headers with incremental // indexing and string literal names should work and add the headers // to the reference set. -TEST(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { - HpackDecoder decoder(kuint32max); +TEST_F(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { std::map<string, string> header_set = DecodeUniqueHeaderSet( - &decoder, StringPiece("\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2", 37)); std::map<string, string> expected_header_set; @@ -142,33 +165,31 @@ TEST(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { EXPECT_EQ(expected_header_set, header_set); // Decoding an empty string should just return the reference set. - std::map<string, string> header_set2 = DecodeUniqueHeaderSet(&decoder, ""); + std::map<string, string> header_set2 = DecodeUniqueHeaderSet(""); EXPECT_EQ(expected_header_set, header_set2); } // Decoding literal headers with invalid indices should fail // gracefully. -TEST(HpackDecoderTest, LiteralHeaderInvalidIndices) { - HpackDecoder decoder(kuint32max); - +TEST_F(HpackDecoderTest, LiteralHeaderInvalidIndices) { HpackHeaderPairVector header_list; // No indexing. // One more than the number of static table entries. - EXPECT_FALSE(decoder.DecodeHeaderSet(StringPiece("\x7d", 1), &header_list)); - EXPECT_FALSE(decoder.DecodeHeaderSet(StringPiece("\x40", 1), &header_list)); + EXPECT_FALSE(decoder_.DecodeHeaderSet(StringPiece("\x7d", 1), &header_list)); + EXPECT_FALSE(decoder_.DecodeHeaderSet(StringPiece("\x40", 1), &header_list)); // Incremental indexing. // One more than the number of static table entries. - EXPECT_FALSE(decoder.DecodeHeaderSet(StringPiece("\x3d", 1), &header_list)); - EXPECT_FALSE(decoder.DecodeHeaderSet(StringPiece("\x00", 1), &header_list)); + EXPECT_FALSE(decoder_.DecodeHeaderSet(StringPiece("\x3d", 1), &header_list)); + EXPECT_FALSE(decoder_.DecodeHeaderSet(StringPiece("\x00", 1), &header_list)); } // Round-tripping the header set from E.2.1 should work. -TEST(HpackDecoderTest, BasicE21) { - HpackEncoder encoder(kuint32max); +TEST_F(HpackDecoderTest, BasicE21) { + HpackEncoder encoder(kLiteralBound); std::map<string, string> expected_header_set; expected_header_set[":method"] = "GET"; @@ -180,13 +201,271 @@ TEST(HpackDecoderTest, BasicE21) { EXPECT_TRUE(encoder.EncodeHeaderSet( expected_header_set, &encoded_header_set)); - HpackDecoder decoder(kuint32max); HpackHeaderPairVector header_list; - EXPECT_TRUE(decoder.DecodeHeaderSet(encoded_header_set, &header_list)); + EXPECT_TRUE(decoder_.DecodeHeaderSet(encoded_header_set, &header_list)); std::map<string, string> header_set(header_list.begin(), header_list.end()); EXPECT_EQ(expected_header_set, header_set); } +TEST_F(HpackDecoderTest, SectionD3RequestHuffmanExamples) { + { + std::vector<HpackHuffmanSymbol> code = HpackRequestHuffmanCode(); + EXPECT_TRUE(huffman_table_.Initialize(&code[0], code.size())); + } + std::map<string, string> header_set; + + // 82 | == Indexed - Add == + // | idx = 2 + // | -> :method: GET + // 87 | == Indexed - Add == + // | idx = 7 + // | -> :scheme: http + // 86 | == Indexed - Add == + // | idx = 6 + // | -> :path: / + // 04 | == Literal indexed == + // | Indexed name (idx = 4) + // | :authority + // 8b | Literal value (len = 15) + // | Huffman encoded: + // db6d 883e 68d1 cb12 25ba 7f | .m..h...%.. + // | Decoded: + // | www.example.com + // | -> :authority: www.example.com + char first[] = + "\x82\x87\x86\x04\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f"; + header_set = DecodeUniqueHeaderSet(StringPiece(first, arraysize(first)-1)); + + // TODO(jgraettinger): Create HpackEncodingContext and + // HpackDecoder peers, and inspect the header table here. + EXPECT_THAT(header_set, ElementsAre( + Pair(":authority", "www.example.com"), + Pair(":method", "GET"), + Pair(":path", "/"), + Pair(":scheme", "http"))); + + // 1b | == Literal indexed == + // | Indexed name (idx = 27) + // | cache-control + // 86 | Literal value (len = 8) + // | Huffman encoded: + // 6365 4a13 98ff | ceJ... + // | Decoded: + // | no-cache + // | -> cache-control: no-cache + char second[] = "\x1b\x86\x63\x65\x4a\x13\x98\xff"; + header_set = DecodeUniqueHeaderSet(StringPiece(second, arraysize(second)-1)); + + EXPECT_THAT(header_set, ElementsAre( + Pair(":authority", "www.example.com"), + Pair(":method", "GET"), + Pair(":path", "/"), + Pair(":scheme", "http"), + Pair("cache-control", "no-cache"))); + + // 80 | == Empty reference set == + // | idx = 0 + // | flag = 1 + // 85 | == Indexed - Add == + // | idx = 5 + // | -> :method: GET + // 8c | == Indexed - Add == + // | idx = 12 + // | -> :scheme: https + // 8b | == Indexed - Add == + // | idx = 11 + // | -> :path: /index.html + // 84 | == Indexed - Add == + // | idx = 4 + // | -> :authority: www.example.com + // 00 | == Literal indexed == + // 88 | Literal name (len = 10) + // | Huffman encoded: + // 4eb0 8b74 9790 fa7f | N..t.... + // | Decoded: + // | custom-key + // 89 | Literal value (len = 12) + // | Huffman encoded: + // 4eb0 8b74 979a 17a8 ff | N..t..... + // | Decoded: + // | custom-value + // | -> custom-key: custom-value + char third[] = + "\x80\x85\x8c\x8b\x84\x00\x88\x4e\xb0\x8b\x74\x97\x90\xfa\x7f\x89" + "\x4e\xb0\x8b\x74\x97\x9a\x17\xa8\xff"; + header_set = DecodeUniqueHeaderSet(StringPiece(third, arraysize(third)-1)); + + EXPECT_THAT(header_set, ElementsAre( + Pair(":authority", "www.example.com"), + Pair(":method", "GET"), + Pair(":path", "/index.html"), + Pair(":scheme", "https"), + Pair("custom-key", "custom-value"))); +} + +TEST_F(HpackDecoderTest, SectionD5ResponseHuffmanExamples) { + { + std::vector<HpackHuffmanSymbol> code = HpackResponseHuffmanCode(); + EXPECT_TRUE(huffman_table_.Initialize(&code[0], code.size())); + } + std::map<string, string> header_set; + decoder_.SetMaxHeadersSize(256); + + // 08 | == Literal indexed == + // | Indexed name (idx = 8) + // | :status + // 82 | Literal value (len = 3) + // | Huffman encoded: + // 409f | @. + // | Decoded: + // | 302 + // | -> :status: 302 + // 18 | == Literal indexed == + // | Indexed name (idx = 24) + // | cache-control + // 86 | Literal value (len = 7) + // | Huffman encoded: + // c31b 39bf 387f | ..9.8. + // | Decoded: + // | private + // | -> cache-control: private + // 22 | == Literal indexed == + // | Indexed name (idx = 34) + // | date + // 92 | Literal value (len = 29) + // | Huffman encoded: + // a2fb a203 20f2 ab30 3124 018b 490d 3209 | .... ..01$..I.2. + // e877 | .w + // | Decoded: + // | Mon, 21 Oct 2013 20:13:21 + // | GMT + // | -> date: Mon, 21 Oct 2013 + // | 20:13:21 GMT + // 30 | == Literal indexed == + // | Indexed name (idx = 48) + // | location + // 93 | Literal value (len = 23) + // | Huffman encoded: + // e39e 7864 dd7a fd3d 3d24 8747 db87 2849 | ..xd.z.==$.G..(I + // 55f6 ff | U.. + // | Decoded: + // | https://www.example.com + // | -> location: https://www.e + // | xample.com + char first[] = + "\x08\x82\x40\x9f\x18\x86\xc3\x1b\x39\xbf\x38\x7f\x22\x92\xa2\xfb" + "\xa2\x03\x20\xf2\xab\x30\x31\x24\x01\x8b\x49\x0d\x32\x09\xe8\x77" + "\x30\x93\xe3\x9e\x78\x64\xdd\x7a\xfd\x3d\x3d\x24\x87\x47\xdb\x87" + "\x28\x49\x55\xf6\xff"; + header_set = DecodeUniqueHeaderSet(StringPiece(first, arraysize(first)-1)); + + EXPECT_THAT(header_set, ElementsAre( + Pair(":status", "302"), + Pair("cache-control", "private"), + Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), + Pair("location", "https://www.example.com"))); + + // 84 | == Indexed - Remove == + // | idx = 4 + // | -> :status: 302 + // 8c | == Indexed - Add == + // | idx = 12 + // | - evict: :status: 302 + // | -> :status: 200 + char second[] = "\x84\x8c"; + header_set = DecodeUniqueHeaderSet(StringPiece(second, arraysize(second)-1)); + + EXPECT_THAT(header_set, ElementsAre( + Pair(":status", "200"), + Pair("cache-control", "private"), + Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), + Pair("location", "https://www.example.com"))); + + // 83 | == Indexed - Remove == + // | idx = 3 + // | -> date: Mon, 21 Oct 2013 + // | 20:13:21 GMT + // 84 | == Indexed - Remove == + // | idx = 4 + // | -> cache-control: private + // 84 | == Indexed - Add == + // | idx = 4 + // | -> cache-control: private + // 03 | == Literal indexed == + // | Indexed name (idx = 3) + // | date + // 92 | Literal value (len = 29) + // | Huffman encoded: + // a2fb a203 20f2 ab30 3124 018b 490d 3309 | .... ..01$..I.3. + // e877 | .w + // | Decoded: + // | Mon, 21 Oct 2013 20:13:22 GMT + // | - evict: cache-control: private + // | -> date: Mon, 21 Oct 2013 + // | 20:13:22 GMT + // 1d | == Literal indexed == + // | Indexed name (idx = 29) + // | content-encoding + // 84 | Literal value (len = 4) + // | Huffman encoded: + // e1fb b30f | .... + // | Decoded: + // | gzip + // | - evict: date: Mon, 21 Oct + // | 2013 20:13:21 GMT + // | -> content-encoding: gzip + // 84 | == Indexed - Remove == + // | idx = 4 + // | -> location: https://www.e + // | xample.com + // 84 | == Indexed - Add == + // | idx = 4 + // | -> location: https://www.e + // | xample.com + // 83 | == Indexed - Remove == + // | idx = 3 + // | -> :status: 200 + // 83 | == Indexed - Add == + // | idx = 3 + // | -> :status: 200 + // 3a | == Literal indexed == + // | Indexed name (idx = 58) + // | set-cookie + // b3 | Literal value (len = 56) + // | Huffman encoded: + // df7d fb36 d3d9 e1fc fc3f afe7 abfc fefc | .}.6.....?...... + // bfaf 3edf 2f97 7fd3 6ff7 fd79 f6f9 77fd | ..../...o..y..w. + // 3de1 6bfa 46fe 10d8 8944 7de1 ce18 e565 | =.k.F....D}....e + // f76c 2f | .l/ + // | Decoded: + // | foo=ASDJKHQKBZXOQWEOPIUAXQ + // | WEOIU; max-age=3600; versi + // | on=1 + // | - evict: location: https:/ + // | /www.example.com + // | - evict: :status: 200 + // | -> set-cookie: foo=ASDJKHQ + // | KBZXOQWEOPIUAXQWEOIU; ma + // | x-age=3600; version=1 + char third[] = + "\x83\x84\x84\x03\x92\xa2\xfb\xa2\x03\x20\xf2\xab\x30\x31\x24\x01" + "\x8b\x49\x0d\x33\x09\xe8\x77\x1d\x84\xe1\xfb\xb3\x0f\x84\x84\x83" + "\x83\x3a\xb3\xdf\x7d\xfb\x36\xd3\xd9\xe1\xfc\xfc\x3f\xaf\xe7\xab" + "\xfc\xfe\xfc\xbf\xaf\x3e\xdf\x2f\x97\x7f\xd3\x6f\xf7\xfd\x79\xf6" + "\xf9\x77\xfd\x3d\xe1\x6b\xfa\x46\xfe\x10\xd8\x89\x44\x7d\xe1\xce" + "\x18\xe5\x65\xf7\x6c\x2f"; + header_set = DecodeUniqueHeaderSet(StringPiece(third, arraysize(third)-1)); + + EXPECT_THAT(header_set, ElementsAre( + Pair(":status", "200"), + Pair("cache-control", "private"), + Pair("content-encoding", "gzip"), + Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), + Pair("location", "https://www.example.com"), + Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" + " max-age=3600; version=1"))); +} + } // namespace } // namespace net diff --git a/net/spdy/hpack_huffman_table.cc b/net/spdy/hpack_huffman_table.cc index 36bb9de..5372b97 100644 --- a/net/spdy/hpack_huffman_table.cc +++ b/net/spdy/hpack_huffman_table.cc @@ -251,7 +251,9 @@ void HpackHuffmanTable::EncodeString(StringPiece in, } } -bool HpackHuffmanTable::DecodeString(HpackInputStream* in, string* out) const { +bool HpackHuffmanTable::DecodeString(HpackInputStream* in, + size_t out_capacity, + string* out) const { out->clear(); // Current input, stored in the high |bits_available| bits of |bits|. @@ -284,8 +286,8 @@ bool HpackHuffmanTable::DecodeString(HpackInputStream* in, string* out) const { // The input is an invalid prefix, larger than any prefix in the table. return false; } else { - if (out->size() == out->capacity()) { - // This code would cause us to overflow |out|. + if (out->size() == out_capacity) { + // This code would cause us to overflow |out_capacity|. return false; } if (entry.symbol_id < 256) { diff --git a/net/spdy/hpack_huffman_table.h b/net/spdy/hpack_huffman_table.h index a2adab7..8f1ada9 100644 --- a/net/spdy/hpack_huffman_table.h +++ b/net/spdy/hpack_huffman_table.h @@ -81,8 +81,10 @@ class NET_EXPORT_PRIVATE HpackHuffmanTable { // to ensure |out| has a reserved a sufficient buffer to hold decoded output. // DecodeString() halts when |in| runs out of input, in which case true is // returned. It also halts (returning false) if an invalid Huffman code - // prefix is read, or if |out.capacity()| would otherwise be overflowed. - bool DecodeString(HpackInputStream* in, std::string* out) const; + // prefix is read, or if |out_capacity| would otherwise be overflowed. + bool DecodeString(HpackInputStream* in, + size_t out_capacity, + std::string* out) const; private: // Expects symbols ordered on length & ID ascending. diff --git a/net/spdy/hpack_huffman_table_test.cc b/net/spdy/hpack_huffman_table_test.cc index 3ce1a1f..8369f90 100644 --- a/net/spdy/hpack_huffman_table_test.cc +++ b/net/spdy/hpack_huffman_table_test.cc @@ -268,14 +268,14 @@ TEST(HpackHuffmanTableTest, ValidateInternalsWithSmallCode) { bits8("01001100")}; StringPiece expect(expect_storage, arraysize(expect_storage)); - string buffer_in, buffer_out(input.size(), '\0'); + string buffer_in, buffer_out; HpackOutputStream output_stream(kuint32max); table.EncodeString(input, &output_stream); output_stream.TakeString(&buffer_in); EXPECT_EQ(buffer_in, expect); HpackInputStream input_stream(kuint32max, buffer_in); - EXPECT_TRUE(table.DecodeString(&input_stream, &buffer_out)); + EXPECT_TRUE(table.DecodeString(&input_stream, input.size(), &buffer_out)); EXPECT_EQ(buffer_out, input); } @@ -335,14 +335,15 @@ TEST(HpackHuffmanTableTest, DecodeWithBadInput) { HpackHuffmanTable table; EXPECT_TRUE(table.Initialize(code, arraysize(code))); - string buffer(4, '\0'); + string buffer; + const size_t capacity = 4; { // This example works: (2) 00 (3) 010 (2) 00 (6) 100110 (pad) 100. char input_storage[] = {bits8("00010001"), bits8("00110100")}; StringPiece input(input_storage, arraysize(input_storage)); HpackInputStream input_stream(kuint32max, input); - EXPECT_TRUE(table.DecodeString(&input_stream, &buffer)); + EXPECT_TRUE(table.DecodeString(&input_stream, capacity, &buffer)); EXPECT_EQ(buffer, "\x02\x03\x02\x06"); } { @@ -352,21 +353,20 @@ TEST(HpackHuffmanTableTest, DecodeWithBadInput) { StringPiece input(input_storage, arraysize(input_storage)); HpackInputStream input_stream(kuint32max, input); - EXPECT_FALSE(table.DecodeString(&input_stream, &buffer)); + EXPECT_FALSE(table.DecodeString(&input_stream, capacity, &buffer)); EXPECT_EQ(buffer, "\x02\x03\x02"); } { // Repeat the shortest 00 code to overflow |buffer|. Expect to fail. - size_t capacity = buffer.capacity(); std::vector<char> input_storage(1 + capacity / 4, '\0'); StringPiece input(&input_storage[0], input_storage.size()); HpackInputStream input_stream(kuint32max, input); - EXPECT_FALSE(table.DecodeString(&input_stream, &buffer)); + EXPECT_FALSE(table.DecodeString(&input_stream, capacity, &buffer)); std::vector<char> expected(capacity, '\x02'); EXPECT_THAT(buffer, ElementsAreArray(expected)); - EXPECT_EQ(capacity, buffer.capacity()); + EXPECT_EQ(capacity, buffer.size()); } { // Expect to fail if more than a byte of unconsumed input remains. @@ -375,7 +375,7 @@ TEST(HpackHuffmanTableTest, DecodeWithBadInput) { StringPiece input(input_storage, arraysize(input_storage)); HpackInputStream input_stream(kuint32max, input); - EXPECT_FALSE(table.DecodeString(&input_stream, &buffer)); + EXPECT_FALSE(table.DecodeString(&input_stream, 4, &buffer)); EXPECT_EQ(buffer, "\x06"); } } @@ -399,17 +399,17 @@ TEST(HpackHuffmanTableTest, SpecRequestExamples) { }; // Round-trip each test example. for (size_t i = 0; i != arraysize(test_table); i += 2) { - const string& encodedFixture(test_table[i]); - const string& decodedFixture(test_table[i+1]); - HpackInputStream input_stream(kuint32max, encodedFixture); + const string& encoded(test_table[i]); + const string& decoded(test_table[i+1]); + HpackInputStream input_stream(kuint32max, encoded); HpackOutputStream output_stream(kuint32max); - buffer.reserve(decodedFixture.size()); - EXPECT_TRUE(table.DecodeString(&input_stream, &buffer)); - EXPECT_EQ(decodedFixture, buffer); - table.EncodeString(decodedFixture, &output_stream); + buffer.reserve(decoded.size()); + EXPECT_TRUE(table.DecodeString(&input_stream, decoded.size(), &buffer)); + EXPECT_EQ(decoded, buffer); + table.EncodeString(decoded, &output_stream); output_stream.TakeString(&buffer); - EXPECT_EQ(encodedFixture, buffer); + EXPECT_EQ(encoded, buffer); } } @@ -440,17 +440,17 @@ TEST(HpackHuffmanTableTest, SpecResponseExamples) { }; // Round-trip each test example. for (size_t i = 0; i != arraysize(test_table); i += 2) { - const string& encodedFixture(test_table[i]); - const string& decodedFixture(test_table[i+1]); - HpackInputStream input_stream(kuint32max, encodedFixture); + const string& encoded(test_table[i]); + const string& decoded(test_table[i+1]); + HpackInputStream input_stream(kuint32max, encoded); HpackOutputStream output_stream(kuint32max); - buffer.reserve(decodedFixture.size()); - EXPECT_TRUE(table.DecodeString(&input_stream, &buffer)); - EXPECT_EQ(decodedFixture, buffer); - table.EncodeString(decodedFixture, &output_stream); + buffer.reserve(decoded.size()); + EXPECT_TRUE(table.DecodeString(&input_stream, decoded.size(), &buffer)); + EXPECT_EQ(decoded, buffer); + table.EncodeString(decoded, &output_stream); output_stream.TakeString(&buffer); - EXPECT_EQ(encodedFixture, buffer); + EXPECT_EQ(encoded, buffer); } } @@ -471,7 +471,7 @@ TEST(HpackHuffmanTableTest, RoundTripIndvidualSymbols) { output_stream.TakeString(&buffer_in); HpackInputStream input_stream(kuint32max, buffer_in); - EXPECT_TRUE(table.DecodeString(&input_stream, &buffer_out)); + EXPECT_TRUE(table.DecodeString(&input_stream, input.size(), &buffer_out)); EXPECT_EQ(input, buffer_out); } } @@ -495,7 +495,7 @@ TEST(HpackHuffmanTableTest, RoundTripSymbolSequence) { output_stream.TakeString(&buffer_in); HpackInputStream input_stream(kuint32max, buffer_in); - EXPECT_TRUE(table.DecodeString(&input_stream, &buffer_out)); + EXPECT_TRUE(table.DecodeString(&input_stream, input.size(), &buffer_out)); EXPECT_EQ(input, buffer_out); } diff --git a/net/spdy/hpack_input_stream.cc b/net/spdy/hpack_input_stream.cc index 458215e..f7981a3 100644 --- a/net/spdy/hpack_input_stream.cc +++ b/net/spdy/hpack_input_stream.cc @@ -12,6 +12,7 @@ namespace net { using base::StringPiece; +using std::string; HpackInputStream::HpackInputStream(uint32 max_string_literal_size, StringPiece buffer) @@ -92,28 +93,38 @@ bool HpackInputStream::DecodeNextUint32(uint32* I) { return !has_more; } -// TODO(akalin): Figure out how to handle the storage for -// Huffman-encoded sequences. -bool HpackInputStream::DecodeNextStringLiteral(StringPiece* str) { - if (MatchPrefixAndConsume(kStringLiteralIdentityEncoded)) { - uint32 size = 0; - if (!DecodeNextUint32(&size)) - return false; +bool HpackInputStream::DecodeNextIdentityString(StringPiece* str) { + uint32 size = 0; + if (!DecodeNextUint32(&size)) + return false; - if (size > max_string_literal_size_) - return false; + if (size > max_string_literal_size_) + return false; - if (size > buffer_.size()) - return false; + if (size > buffer_.size()) + return false; - *str = StringPiece(buffer_.data(), size); - buffer_.remove_prefix(size); - return true; - } + *str = StringPiece(buffer_.data(), size); + buffer_.remove_prefix(size); + return true; +} - // TODO(akalin): Handle Huffman-encoded sequences. +bool HpackInputStream::DecodeNextHuffmanString(const HpackHuffmanTable& table, + string* str) { + uint32 encoded_size = 0; + if (!DecodeNextUint32(&encoded_size)) + return false; - return false; + if (encoded_size > buffer_.size()) + return false; + + HpackInputStream bounded_reader( + max_string_literal_size_, + StringPiece(buffer_.data(), encoded_size)); + buffer_.remove_prefix(encoded_size); + + // HpackHuffmanTable will not decode beyond |max_string_literal_size_|. + return table.DecodeString(&bounded_reader, max_string_literal_size_, str); } bool HpackInputStream::PeekBits(size_t* peeked_count, uint32* out) { diff --git a/net/spdy/hpack_input_stream.h b/net/spdy/hpack_input_stream.h index 99e4f27..1a38dc6 100644 --- a/net/spdy/hpack_input_stream.h +++ b/net/spdy/hpack_input_stream.h @@ -12,11 +12,11 @@ #include "base/macros.h" #include "base/strings/string_piece.h" #include "net/base/net_export.h" -#include "net/spdy/hpack_constants.h" // For HpackPrefix. +#include "net/spdy/hpack_constants.h" +#include "net/spdy/hpack_huffman_table.h" // All section references below are to // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05 -// . namespace net { @@ -46,7 +46,9 @@ class NET_EXPORT_PRIVATE HpackInputStream { // decoding was successful, or false if an error was encountered. bool DecodeNextUint32(uint32* I); - bool DecodeNextStringLiteral(base::StringPiece* str); + bool DecodeNextIdentityString(base::StringPiece* str); + bool DecodeNextHuffmanString(const HpackHuffmanTable& table, + std::string* str); // Stores input bits into the most-significant, unfilled bits of |out|. // |peeked_count| is the number of filled bits in |out| which have been @@ -72,10 +74,6 @@ class NET_EXPORT_PRIVATE HpackInputStream { return DecodeNextUint32(I); } - bool DecodeNextStringLiteralForTest(base::StringPiece *str) { - return DecodeNextStringLiteral(str); - } - private: const uint32 max_string_literal_size_; base::StringPiece buffer_; diff --git a/net/spdy/hpack_input_stream_test.cc b/net/spdy/hpack_input_stream_test.cc index d1e7a19..565a2fa 100644 --- a/net/spdy/hpack_input_stream_test.cc +++ b/net/spdy/hpack_input_stream_test.cc @@ -8,7 +8,9 @@ #include <string> #include <vector> +#include "base/logging.h" #include "base/strings/string_piece.h" +#include "net/spdy/hpack_constants.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -18,12 +20,34 @@ namespace { using base::StringPiece; using std::string; +const size_t kLiteralBound = 1024; + +class HpackInputStreamTest : public ::testing::Test { + virtual void SetUp() { + std::vector<HpackHuffmanSymbol> code = HpackResponseHuffmanCode(); + EXPECT_TRUE(huffman_table.Initialize(&code[0], code.size())); + } + + protected: + HpackHuffmanTable huffman_table; +}; + +const char kEncodedFixture[] = "\x33" // Length prefix. + "\xdf\x7d\xfb\x36\xd3\xd9\xe1\xfc\xfc\x3f\xaf" + "\xe7\xab\xfc\xfe\xfc\xbf\xaf\x3e\xdf\x2f" + "\x97\x7f\xd3\x6f\xf7\xfd\x79\xf6\xf9\x77" + "\xfd\x3d\xe1\x6b\xfa\x46\xfe\x10\xd8\x89" + "\x44\x7d\xe1\xce\x18\xe5\x65\xf7\x6c\x2f"; + +const char kDecodedFixture[] = + "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"; + // Utility function to decode an assumed-valid uint32 with an N-bit // prefix. uint32 DecodeValidUint32(uint8 N, StringPiece str) { EXPECT_GT(N, 0); EXPECT_LE(N, 8); - HpackInputStream input_stream(kuint32max, str); + HpackInputStream input_stream(kLiteralBound, str); input_stream.SetBitOffsetForTest(8 - N); uint32 I; EXPECT_TRUE(input_stream.DecodeNextUint32ForTest(&I)); @@ -35,7 +59,7 @@ uint32 DecodeValidUint32(uint8 N, StringPiece str) { void ExpectDecodeUint32Invalid(uint8 N, StringPiece str) { EXPECT_GT(N, 0); EXPECT_LE(N, 8); - HpackInputStream input_stream(kuint32max, str); + HpackInputStream input_stream(kLiteralBound, str); input_stream.SetBitOffsetForTest(8 - N); uint32 I; EXPECT_FALSE(input_stream.DecodeNextUint32ForTest(&I)); @@ -49,7 +73,7 @@ uint32 bits32(const string& bitstring) { // certain integers are decoded correctly with an 8-bit prefix in // exactly {Number} bytes. -TEST(HpackInputStreamTest, OneByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, OneByteIntegersEightBitPrefix) { // Minimum. EXPECT_EQ(0x00u, DecodeValidUint32(8, string("\x00", 1))); EXPECT_EQ(0x7fu, DecodeValidUint32(8, "\x7f")); @@ -59,7 +83,7 @@ TEST(HpackInputStreamTest, OneByteIntegersEightBitPrefix) { ExpectDecodeUint32Invalid(8, "\xff"); } -TEST(HpackInputStreamTest, TwoByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, TwoByteIntegersEightBitPrefix) { // Minimum. EXPECT_EQ(0xffu, DecodeValidUint32(8, string("\xff\x00", 2))); EXPECT_EQ(0x0100u, DecodeValidUint32(8, "\xff\x01")); @@ -70,7 +94,7 @@ TEST(HpackInputStreamTest, TwoByteIntegersEightBitPrefix) { ExpectDecodeUint32Invalid(8, "\xff\xff"); } -TEST(HpackInputStreamTest, ThreeByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, ThreeByteIntegersEightBitPrefix) { // Minimum. EXPECT_EQ(0x017fu, DecodeValidUint32(8, "\xff\x80\x01")); EXPECT_EQ(0x0fffu, DecodeValidUint32(8, "\xff\x80\x1e")); @@ -83,7 +107,7 @@ TEST(HpackInputStreamTest, ThreeByteIntegersEightBitPrefix) { ExpectDecodeUint32Invalid(8, "\xff\xff\xff"); } -TEST(HpackInputStreamTest, FourByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, FourByteIntegersEightBitPrefix) { // Minimum. EXPECT_EQ(0x40ffu, DecodeValidUint32(8, "\xff\x80\x80\x01")); EXPECT_EQ(0xffffu, DecodeValidUint32(8, "\xff\x80\xfe\x03")); @@ -96,7 +120,7 @@ TEST(HpackInputStreamTest, FourByteIntegersEightBitPrefix) { ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff"); } -TEST(HpackInputStreamTest, FiveByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, FiveByteIntegersEightBitPrefix) { // Minimum. EXPECT_EQ(0x002000ffu, DecodeValidUint32(8, "\xff\x80\x80\x80\x01")); EXPECT_EQ(0x00ffffffu, DecodeValidUint32(8, "\xff\x80\xfe\xff\x07")); @@ -109,7 +133,7 @@ TEST(HpackInputStreamTest, FiveByteIntegersEightBitPrefix) { ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff"); } -TEST(HpackInputStreamTest, SixByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, SixByteIntegersEightBitPrefix) { // Minimum. EXPECT_EQ(0x100000ffu, DecodeValidUint32(8, "\xff\x80\x80\x80\x80\x01")); // Maximum. @@ -122,7 +146,7 @@ TEST(HpackInputStreamTest, SixByteIntegersEightBitPrefix) { // There are no valid uint32 encodings that are greater than six // bytes. -TEST(HpackInputStreamTest, SevenByteIntegersEightBitPrefix) { +TEST_F(HpackInputStreamTest, SevenByteIntegersEightBitPrefix) { ExpectDecodeUint32Invalid(8, "\xff\x80\x80\x80\x80\x80\x00"); ExpectDecodeUint32Invalid(8, "\xff\x80\x80\x80\x80\x80\x01"); ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff\xff\xff"); @@ -132,7 +156,7 @@ TEST(HpackInputStreamTest, SevenByteIntegersEightBitPrefix) { // certain integers are encoded correctly with an N-bit prefix in // exactly {Number} bytes for N in {1, 2, ..., 7}. -TEST(HpackInputStreamTest, OneByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, OneByteIntegersOneToSevenBitPrefixes) { // Minimums. EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x00", 1))); EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x80", 1))); @@ -182,7 +206,7 @@ TEST(HpackInputStreamTest, OneByteIntegersOneToSevenBitPrefixes) { ExpectDecodeUint32Invalid(1, "\xff"); } -TEST(HpackInputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) { // Minimums. EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\x7f\x00", 2))); EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\xff\x00", 2))); @@ -232,7 +256,7 @@ TEST(HpackInputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) { ExpectDecodeUint32Invalid(1, "\xff\xff"); } -TEST(HpackInputStreamTest, ThreeByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, ThreeByteIntegersOneToSevenBitPrefixes) { // Minimums. EXPECT_EQ(0xffu, DecodeValidUint32(7, "\x7f\x80\x01")); EXPECT_EQ(0xffu, DecodeValidUint32(7, "\xff\x80\x01")); @@ -282,7 +306,7 @@ TEST(HpackInputStreamTest, ThreeByteIntegersOneToSevenBitPrefixes) { ExpectDecodeUint32Invalid(1, "\xff\xff\xff"); } -TEST(HpackInputStreamTest, FourByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, FourByteIntegersOneToSevenBitPrefixes) { // Minimums. EXPECT_EQ(0x407fu, DecodeValidUint32(7, "\x7f\x80\x80\x01")); EXPECT_EQ(0x407fu, DecodeValidUint32(7, "\xff\x80\x80\x01")); @@ -332,7 +356,7 @@ TEST(HpackInputStreamTest, FourByteIntegersOneToSevenBitPrefixes) { ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff"); } -TEST(HpackInputStreamTest, FiveByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, FiveByteIntegersOneToSevenBitPrefixes) { // Minimums. EXPECT_EQ(0x20007fu, DecodeValidUint32(7, "\x7f\x80\x80\x80\x01")); EXPECT_EQ(0x20007fu, DecodeValidUint32(7, "\xff\x80\x80\x80\x01")); @@ -382,7 +406,7 @@ TEST(HpackInputStreamTest, FiveByteIntegersOneToSevenBitPrefixes) { ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff\xff"); } -TEST(HpackInputStreamTest, SixByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, SixByteIntegersOneToSevenBitPrefixes) { // Minimums. EXPECT_EQ(0x1000007fu, DecodeValidUint32(7, "\x7f\x80\x80\x80\x80\x01")); EXPECT_EQ(0x1000007fu, DecodeValidUint32(7, "\xff\x80\x80\x80\x80\x01")); @@ -434,7 +458,7 @@ TEST(HpackInputStreamTest, SixByteIntegersOneToSevenBitPrefixes) { // There are no valid uint32 encodings that are greater than six // bytes. -TEST(HpackInputStreamTest, SevenByteIntegersOneToSevenBitPrefixes) { +TEST_F(HpackInputStreamTest, SevenByteIntegersOneToSevenBitPrefixes) { ExpectDecodeUint32Invalid(7, "\x7f\x80\x80\x80\x80\x80\x00"); ExpectDecodeUint32Invalid(7, "\x7f\x80\x80\x80\x80\x80\x01"); ExpectDecodeUint32Invalid(7, "\xff\xff\xff\xff\xff\xff\xff"); @@ -459,39 +483,69 @@ TEST(HpackInputStreamTest, SevenByteIntegersOneToSevenBitPrefixes) { } // Decoding a valid encoded string literal should work. -TEST(HpackInputStreamTest, DecodeNextStringLiteral) { - HpackInputStream input_stream(kuint32max, "\x0estring literal"); +TEST_F(HpackInputStreamTest, DecodeNextIdentityString) { + HpackInputStream input_stream(kLiteralBound, "\x0estring literal"); EXPECT_TRUE(input_stream.HasMoreData()); StringPiece string_piece; - EXPECT_TRUE(input_stream.DecodeNextStringLiteralForTest(&string_piece)); + EXPECT_TRUE(input_stream.DecodeNextIdentityString(&string_piece)); EXPECT_EQ("string literal", string_piece); EXPECT_FALSE(input_stream.HasMoreData()); } // Decoding an encoded string literal with size larger than // |max_string_literal_size_| should fail. -TEST(HpackInputStreamTest, DecodeNextStringLiteralSizeLimit) { +TEST_F(HpackInputStreamTest, DecodeNextIdentityStringSizeLimit) { HpackInputStream input_stream(13, "\x0estring literal"); EXPECT_TRUE(input_stream.HasMoreData()); StringPiece string_piece; - EXPECT_FALSE(input_stream.DecodeNextStringLiteralForTest(&string_piece)); + EXPECT_FALSE(input_stream.DecodeNextIdentityString(&string_piece)); } // Decoding an encoded string literal with size larger than the // remainder of the buffer should fail. -TEST(HpackInputStreamTest, DecodeNextStringLiteralInvalidSize) { +TEST_F(HpackInputStreamTest, DecodeNextIdentityStringNotEnoughInput) { // Set the length to be one more than it should be. - HpackInputStream input_stream(kuint32max, "\x0fstring literal"); + HpackInputStream input_stream(kLiteralBound, "\x0fstring literal"); EXPECT_TRUE(input_stream.HasMoreData()); StringPiece string_piece; - EXPECT_FALSE(input_stream.DecodeNextStringLiteralForTest(&string_piece)); + EXPECT_FALSE(input_stream.DecodeNextIdentityString(&string_piece)); +} + +TEST_F(HpackInputStreamTest, DecodeNextHuffmanString) { + string output, input(kEncodedFixture, arraysize(kEncodedFixture)-1); + HpackInputStream input_stream(arraysize(kDecodedFixture)-1, input); + + EXPECT_TRUE(input_stream.HasMoreData()); + EXPECT_TRUE(input_stream.DecodeNextHuffmanString(huffman_table, &output)); + EXPECT_EQ(kDecodedFixture, output); + EXPECT_FALSE(input_stream.HasMoreData()); +} + +TEST_F(HpackInputStreamTest, DecodeNextHuffmanStringSizeLimit) { + string output, input(kEncodedFixture, arraysize(kEncodedFixture)-1); + // Max string literal is one byte shorter than the decoded fixture. + HpackInputStream input_stream(arraysize(kDecodedFixture)-2, input); + + // Decoded string overflows the max string literal. + EXPECT_TRUE(input_stream.HasMoreData()); + EXPECT_FALSE(input_stream.DecodeNextHuffmanString(huffman_table, &output)); +} + +TEST_F(HpackInputStreamTest, DecodeNextHuffmanStringNotEnoughInput) { + string output, input(kEncodedFixture, arraysize(kEncodedFixture)-1); + input[0]++; // Input prefix is one byte larger than available input. + HpackInputStream input_stream(arraysize(kDecodedFixture)-1, input); + + // Not enough buffer for declared encoded length. + EXPECT_TRUE(input_stream.HasMoreData()); + EXPECT_FALSE(input_stream.DecodeNextHuffmanString(huffman_table, &output)); } -TEST(HpackInputStreamTest, PeekBitsAndConsume) { - HpackInputStream input_stream(kuint32max, "\xad\xab\xad\xab\xad"); +TEST_F(HpackInputStreamTest, PeekBitsAndConsume) { + HpackInputStream input_stream(kLiteralBound, "\xad\xab\xad\xab\xad"); uint32 bits = 0; size_t peeked_count = 0; @@ -552,8 +606,8 @@ TEST(HpackInputStreamTest, PeekBitsAndConsume) { EXPECT_FALSE(input_stream.HasMoreData()); } -TEST(HpackInputStreamTest, ConsumeByteRemainder) { - HpackInputStream input_stream(kuint32max, "\xad\xab"); +TEST_F(HpackInputStreamTest, ConsumeByteRemainder) { + HpackInputStream input_stream(kLiteralBound, "\xad\xab"); // Does nothing. input_stream.ConsumeByteRemainder(); |