summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-04 06:04:55 +0000
committerjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-04 06:04:55 +0000
commit22e40462db62f6ec706f6c2ab77fc8a192482fd9 (patch)
tree9b6976dc5a46de239d6876d16a1ec4f16c3c38e1
parent19e273c317cee4a2952da8687b569ad7083d0c98 (diff)
downloadchromium_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.cc34
-rw-r--r--net/spdy/hpack_decoder.h29
-rw-r--r--net/spdy/hpack_decoder_test.cc385
-rw-r--r--net/spdy/hpack_huffman_table.cc8
-rw-r--r--net/spdy/hpack_huffman_table.h6
-rw-r--r--net/spdy/hpack_huffman_table_test.cc54
-rw-r--r--net/spdy/hpack_input_stream.cc45
-rw-r--r--net/spdy/hpack_input_stream.h12
-rw-r--r--net/spdy/hpack_input_stream_test.cc110
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();