summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-30 22:44:20 +0000
committerjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-30 22:44:20 +0000
commite99d1287191bfdaad3448b7080b46ae39fb6c97a (patch)
tree698ac0b24d0fcb795aa08cc5ab18f61dda751616
parent630e8545ebed7170f0b0d263c9312a0594710a58 (diff)
downloadchromium_src-e99d1287191bfdaad3448b7080b46ae39fb6c97a.zip
chromium_src-e99d1287191bfdaad3448b7080b46ae39fb6c97a.tar.gz
chromium_src-e99d1287191bfdaad3448b7080b46ae39fb6c97a.tar.bz2
Implement a minimal decoder for HPACK (HTTP/2 compression)
The decoder assumes everything is encoded as literals without indexing and does not use Huffman encoding. The decoder will be expanded to handle everything in the spec in future CLs. This lands server change 60496510 by akalin. BUG=339578 Review URL: https://codereview.chromium.org/150453002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248064 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/net.gyp6
-rw-r--r--net/spdy/hpack_constants.h13
-rw-r--r--net/spdy/hpack_decoder.cc86
-rw-r--r--net/spdy/hpack_decoder.h64
-rw-r--r--net/spdy/hpack_decoder_test.cc98
-rw-r--r--net/spdy/hpack_input_stream.cc117
-rw-r--r--net/spdy/hpack_input_stream.h79
-rw-r--r--net/spdy/hpack_input_stream_test.cc490
-rw-r--r--net/spdy/hpack_output_stream.cc29
-rw-r--r--net/spdy/hpack_output_stream.h12
10 files changed, 974 insertions, 20 deletions
diff --git a/net/net.gyp b/net/net.gyp
index a4fc685..f499758 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -1012,6 +1012,8 @@
'spdy/buffered_spdy_framer.cc',
'spdy/buffered_spdy_framer.h',
'spdy/hpack_constants.h',
+ 'spdy/hpack_decoder.cc',
+ 'spdy/hpack_decoder.h',
'spdy/hpack_encoder.cc',
'spdy/hpack_encoder.h',
'spdy/hpack_encoding_context.cc',
@@ -1020,6 +1022,8 @@
'spdy/hpack_entry.h',
'spdy/hpack_header_table.cc',
'spdy/hpack_header_table.h',
+ 'spdy/hpack_input_stream.cc',
+ 'spdy/hpack_input_stream.h',
'spdy/hpack_output_stream.cc',
'spdy/hpack_output_stream.h',
'spdy/hpack_string_util.cc',
@@ -1964,10 +1968,12 @@
'socket_stream/socket_stream_metrics_unittest.cc',
'socket_stream/socket_stream_unittest.cc',
'spdy/buffered_spdy_framer_unittest.cc',
+ 'spdy/hpack_decoder_test.cc',
'spdy/hpack_encoder_test.cc',
'spdy/hpack_encoding_context_test.cc',
'spdy/hpack_entry_test.cc',
'spdy/hpack_header_table_test.cc',
+ 'spdy/hpack_input_stream_test.cc',
'spdy/hpack_output_stream_test.cc',
'spdy/hpack_string_util_test.cc',
'spdy/mock_spdy_framer_visitor.cc',
diff --git a/net/spdy/hpack_constants.h b/net/spdy/hpack_constants.h
index 3b37d3d..dd1f2c6 100644
--- a/net/spdy/hpack_constants.h
+++ b/net/spdy/hpack_constants.h
@@ -13,15 +13,20 @@
namespace net {
+// An HpackPrefix signifies |bits| stored in the top |bit_size| bits
+// of an octet.
+struct HpackPrefix {
+ uint8 bits;
+ size_t bit_size;
+};
+
// The marker for a string literal that is stored unmodified (i.e.,
// without Huffman encoding) (from 4.1.2).
-const uint8 kStringLiteralIdentityEncoded = 0x0;
-const uint8 kStringLiteralIdentityEncodedSize = 1;
+const HpackPrefix kStringLiteralIdentityEncoded = { 0x0, 1 };
// The opcode for a literal header field without indexing (from
// 4.3.1).
-const uint8 kLiteralNoIndexOpcode = 0x01;
-const uint8 kLiteralNoIndexOpcodeSize = 2;
+const HpackPrefix kLiteralNoIndexOpcode = { 0x01, 2 };
} // namespace net
diff --git a/net/spdy/hpack_decoder.cc b/net/spdy/hpack_decoder.cc
new file mode 100644
index 0000000..060a88c
--- /dev/null
+++ b/net/spdy/hpack_decoder.cc
@@ -0,0 +1,86 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack_decoder.h"
+
+#include "base/basictypes.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_output_stream.h"
+
+namespace net {
+
+using base::StringPiece;
+
+HpackDecoder::HpackDecoder(uint32 max_string_literal_size)
+ : max_string_literal_size_(max_string_literal_size) {}
+
+HpackDecoder::~HpackDecoder() {}
+
+bool HpackDecoder::DecodeHeaderSet(StringPiece input,
+ HpackHeaderPairVector* header_list) {
+ HpackInputStream input_stream(max_string_literal_size_, input);
+ while (input_stream.HasMoreData()) {
+ // May add to |header_list|.
+ if (!ProcessNextHeaderRepresentation(&input_stream, header_list))
+ return false;
+ }
+
+ // After processing opcodes, emit everything in the reference set
+ // that hasn't already been emitted.
+ for (size_t i = 1; i <= context_.GetMutableEntryCount(); ++i) {
+ if (context_.IsReferencedAt(i) &&
+ (context_.GetTouchCountAt(i) == HpackEncodingContext::kUntouched)) {
+ header_list->push_back(
+ HpackHeaderPair(context_.GetNameAt(i).as_string(),
+ context_.GetValueAt(i).as_string()));
+ }
+ context_.ClearTouchesAt(i);
+ }
+
+ return true;
+}
+
+bool HpackDecoder::ProcessNextHeaderRepresentation(
+ HpackInputStream* input_stream, HpackHeaderPairVector* header_list) {
+ // Implements 4.3.1. Literal Header Field without Indexing.
+ if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) {
+ StringPiece name;
+ if (!DecodeNextName(input_stream, &name))
+ return false;
+
+ StringPiece value;
+ if (!DecodeNextValue(input_stream, &value))
+ return false;
+
+ header_list->push_back(
+ HpackHeaderPair(name.as_string(), value.as_string()));
+ return true;
+ }
+
+ return false;
+}
+
+bool HpackDecoder::DecodeNextName(
+ HpackInputStream* input_stream, StringPiece* next_name) {
+ uint32 index_or_zero = 0;
+ if (!input_stream->DecodeNextUint32(&index_or_zero))
+ return false;
+
+ if (index_or_zero == 0)
+ return input_stream->DecodeNextStringLiteral(next_name);
+
+ uint32 index = index_or_zero;
+ if (index > context_.GetEntryCount())
+ return false;
+
+ *next_name = context_.GetNameAt(index_or_zero);
+ return true;
+}
+
+bool HpackDecoder::DecodeNextValue(
+ HpackInputStream* input_stream, StringPiece* next_name) {
+ return input_stream->DecodeNextStringLiteral(next_name);
+}
+
+} // namespace net
diff --git a/net/spdy/hpack_decoder.h b/net/spdy/hpack_decoder.h
new file mode 100644
index 0000000..3230fde
--- /dev/null
+++ b/net/spdy/hpack_decoder.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SPDY_HPACK_DECODER_H_
+#define NET_SPDY_HPACK_DECODER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#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.
+
+namespace net {
+
+// An HpackDecoder decodes header sets as outlined in
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05
+// .
+class NET_EXPORT_PRIVATE HpackDecoder {
+ public:
+ explicit HpackDecoder(uint32 max_string_literal_size);
+ ~HpackDecoder();
+
+ // 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.
+ bool DecodeHeaderSet(base::StringPiece input,
+ HpackHeaderPairVector* header_list);
+
+ // Accessors for testing.
+
+ bool DecodeNextNameForTest(HpackInputStream* input_stream,
+ base::StringPiece* next_name) {
+ return DecodeNextName(input_stream, next_name);
+ }
+
+ private:
+ const uint32 max_string_literal_size_;
+ HpackEncodingContext context_;
+
+ // 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.
+ bool ProcessNextHeaderRepresentation(
+ HpackInputStream* input_stream,
+ HpackHeaderPairVector* header_list);
+
+ bool DecodeNextName(HpackInputStream* input_stream,
+ base::StringPiece* next_name);
+ bool DecodeNextValue(HpackInputStream* input_stream,
+ base::StringPiece* next_name);
+
+ DISALLOW_COPY_AND_ASSIGN(HpackDecoder);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_DECODER_H_
diff --git a/net/spdy/hpack_decoder_test.cc b/net/spdy/hpack_decoder_test.cc
new file mode 100644
index 0000000..dcc5ad8
--- /dev/null
+++ b/net/spdy/hpack_decoder_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack_decoder.h"
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/spdy/hpack_encoder.h"
+#include "net/spdy/hpack_input_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using base::StringPiece;
+using std::string;
+
+// 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));
+
+ StringPiece string_piece;
+ EXPECT_TRUE(decoder.DecodeNextNameForTest(&input_stream, &string_piece));
+ EXPECT_EQ("name", 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");
+
+ StringPiece 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) {
+ // One more than the number of static table entries.
+ HpackDecoder decoder(kuint32max);
+ HpackInputStream input_stream(kuint32max, "\x3d");
+
+ StringPiece string_piece;
+ EXPECT_FALSE(decoder.DecodeNextNameForTest(&input_stream, &string_piece));
+}
+
+// Decoding two valid encoded literal headers with no indexing should
+// work.
+TEST(HpackDecoderTest, LiteralHeaderNoIndexing) {
+ HpackDecoder decoder(kuint32max);
+ HpackHeaderPairVector header_list;
+ // First header with indexed name, second header with string literal
+ // name.
+ EXPECT_TRUE(decoder.DecodeHeaderSet(
+ "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2",
+ &header_list));
+ std::map<string, string> header_set(header_list.begin(), header_list.end());
+
+ std::map<string, string> expected_header_set;
+ expected_header_set[":path"] = "/sample/path";
+ expected_header_set[":path2"] = "/sample/path/2";
+ EXPECT_EQ(expected_header_set, header_set);
+}
+
+// Round-tripping the header set from E.2.1 should work.
+TEST(HpackDecoderTest, BasicE21) {
+ HpackEncoder encoder(kuint32max);
+
+ std::map<string, string> expected_header_set;
+ expected_header_set[":method"] = "GET";
+ expected_header_set[":scheme"] = "http";
+ expected_header_set[":path"] = "/";
+ expected_header_set[":authority"] = "www.example.com";
+
+ string encoded_header_set;
+ 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));
+ std::map<string, string> header_set(header_list.begin(), header_list.end());
+ EXPECT_EQ(expected_header_set, header_set);
+}
+
+// TODO(akalin): Add test to exercise emission of the reference set
+// once we can decode opcodes that add to the reference set.
+
+} // namespace
+
+} // namespace net
diff --git a/net/spdy/hpack_input_stream.cc b/net/spdy/hpack_input_stream.cc
new file mode 100644
index 0000000..8dc30e1
--- /dev/null
+++ b/net/spdy/hpack_input_stream.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack_input_stream.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace net {
+
+using base::StringPiece;
+
+HpackInputStream::HpackInputStream(uint32 max_string_literal_size,
+ StringPiece buffer)
+ : max_string_literal_size_(max_string_literal_size),
+ buffer_(buffer),
+ bit_offset_(0) {}
+
+HpackInputStream::~HpackInputStream() {}
+
+bool HpackInputStream::HasMoreData() const {
+ return !buffer_.empty();
+}
+
+bool HpackInputStream::MatchPrefixAndConsume(HpackPrefix prefix) {
+ DCHECK_GT(prefix.bit_size, 0u);
+ DCHECK_LE(prefix.bit_size, 8u);
+
+ uint8 next_octet = 0;
+ if (!PeekNextOctet(&next_octet))
+ return false;
+
+ if ((next_octet >> (8 - prefix.bit_size)) == prefix.bits) {
+ bit_offset_ = prefix.bit_size;
+ return true;
+ }
+
+ return false;
+}
+
+bool HpackInputStream::PeekNextOctet(uint8* next_octet) {
+ if ((bit_offset_ > 0) || buffer_.empty())
+ return false;
+
+ *next_octet = buffer_[0];
+ return true;
+}
+
+bool HpackInputStream::DecodeNextOctet(uint8* next_octet) {
+ if (!PeekNextOctet(next_octet))
+ return false;
+
+ buffer_.remove_prefix(1);
+ return true;
+}
+
+bool HpackInputStream::DecodeNextUint32(uint32* I) {
+ size_t N = 8 - bit_offset_;
+ DCHECK_GT(N, 0u);
+ DCHECK_LE(N, 8u);
+
+ bit_offset_ = 0;
+
+ *I = 0;
+
+ uint8 next_marker = (1 << N) - 1;
+ uint8 next_octet = 0;
+ if (!DecodeNextOctet(&next_octet))
+ return false;
+ *I = next_octet & next_marker;
+
+ bool has_more = (*I == next_marker);
+ size_t shift = 0;
+ while (has_more && (shift < 32)) {
+ uint8 next_octet = 0;
+ if (!DecodeNextOctet(&next_octet))
+ return false;
+ has_more = (next_octet & 0x80) != 0;
+ next_octet &= 0x7f;
+ uint32 addend = next_octet << shift;
+ // Check for overflow.
+ if ((addend >> shift) != next_octet) {
+ return false;
+ }
+ *I += addend;
+ shift += 7;
+ }
+
+ 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;
+
+ if (size > max_string_literal_size_)
+ return false;
+
+ if (size > buffer_.size())
+ return false;
+
+ *str = StringPiece(buffer_.data(), size);
+ buffer_.remove_prefix(size);
+ return true;
+ }
+
+ // TODO(akalin): Handle Huffman-encoded sequences.
+
+ return false;
+}
+
+} // namespace net
diff --git a/net/spdy/hpack_input_stream.h b/net/spdy/hpack_input_stream.h
new file mode 100644
index 0000000..17a057a
--- /dev/null
+++ b/net/spdy/hpack_input_stream.h
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SPDY_HPACK_INPUT_STREAM_H_
+#define NET_SPDY_HPACK_INPUT_STREAM_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_constants.h" // For HpackPrefix.
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05
+// .
+
+namespace net {
+
+// TODO(akalin): When we use a callback/delegate instead of a vector,
+// use StringPiece instead of string.
+typedef std::pair<std::string, std::string> HpackHeaderPair;
+typedef std::vector<HpackHeaderPair> HpackHeaderPairVector;
+
+// An HpackInputStream handles all the low-level details of decoding
+// header fields.
+class NET_EXPORT_PRIVATE HpackInputStream {
+ public:
+ // |max_string_literal_size| is the largest that any one string
+ // literal (header name or header value) can be.
+ HpackInputStream(uint32 max_string_literal_size, base::StringPiece buffer);
+ ~HpackInputStream();
+
+ // Returns whether or not there is more data to process.
+ bool HasMoreData() const;
+
+ // If the next octet has the top |size| bits equal to |bits|,
+ // consumes it and returns true. Otherwise, consumes nothing and
+ // returns false.
+ bool MatchPrefixAndConsume(HpackPrefix prefix);
+
+ // The Decode* functions return true and fill in their arguments if
+ // decoding was successful, or false if an error was encountered.
+
+ bool DecodeNextUint32(uint32* I);
+ bool DecodeNextStringLiteral(base::StringPiece* str);
+
+ // Accessors for testing.
+
+ void SetBitOffsetForTest(size_t bit_offset) {
+ bit_offset_ = bit_offset;
+ }
+
+ bool DecodeNextUint32ForTest(uint32* I) {
+ return DecodeNextUint32(I);
+ }
+
+ bool DecodeNextStringLiteralForTest(base::StringPiece *str) {
+ return DecodeNextStringLiteral(str);
+ }
+
+ private:
+ const uint32 max_string_literal_size_;
+ base::StringPiece buffer_;
+ size_t bit_offset_;
+
+ bool PeekNextOctet(uint8* next_octet);
+
+ bool DecodeNextOctet(uint8* next_octet);
+
+ DISALLOW_COPY_AND_ASSIGN(HpackInputStream);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_INPUT_STREAM_H_
diff --git a/net/spdy/hpack_input_stream_test.cc b/net/spdy/hpack_input_stream_test.cc
new file mode 100644
index 0000000..049fc2cf
--- /dev/null
+++ b/net/spdy/hpack_input_stream_test.cc
@@ -0,0 +1,490 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack_input_stream.h"
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using base::StringPiece;
+using std::string;
+
+// 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);
+ input_stream.SetBitOffsetForTest(8 - N);
+ uint32 I;
+ EXPECT_TRUE(input_stream.DecodeNextUint32ForTest(&I));
+ return I;
+}
+
+// Utility function to decode an assumed-invalid uint32 with an N-bit
+// prefix.
+void ExpectDecodeUint32Invalid(uint8 N, StringPiece str) {
+ EXPECT_GT(N, 0);
+ EXPECT_LE(N, 8);
+ HpackInputStream input_stream(kuint32max, str);
+ input_stream.SetBitOffsetForTest(8 - N);
+ uint32 I;
+ EXPECT_FALSE(input_stream.DecodeNextUint32ForTest(&I));
+}
+
+// The {Number}ByteIntegersEightBitPrefix tests below test that
+// certain integers are decoded correctly with an 8-bit prefix in
+// exactly {Number} bytes.
+
+TEST(HpackInputStreamTest, OneByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x00u, DecodeValidUint32(8, string("\x00", 1)));
+ EXPECT_EQ(0x7fu, DecodeValidUint32(8, "\x7f"));
+ // Maximum.
+ EXPECT_EQ(0xfeu, DecodeValidUint32(8, "\xfe"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff");
+}
+
+TEST(HpackInputStreamTest, TwoByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0xffu, DecodeValidUint32(8, string("\xff\x00", 2)));
+ EXPECT_EQ(0x0100u, DecodeValidUint32(8, "\xff\x01"));
+ // Maximum.
+ EXPECT_EQ(0x017eu, DecodeValidUint32(8, "\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff");
+}
+
+TEST(HpackInputStreamTest, ThreeByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x017fu, DecodeValidUint32(8, "\xff\x80\x01"));
+ EXPECT_EQ(0x0fffu, DecodeValidUint32(8, "\xff\x80\x1e"));
+ // Maximum.
+ EXPECT_EQ(0x40feu, DecodeValidUint32(8, "\xff\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff");
+}
+
+TEST(HpackInputStreamTest, FourByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x40ffu, DecodeValidUint32(8, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0xffffu, DecodeValidUint32(8, "\xff\x80\xfe\x03"));
+ // Maximum.
+ EXPECT_EQ(0x002000feu, DecodeValidUint32(8, "\xff\xff\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\xff\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff");
+}
+
+TEST(HpackInputStreamTest, FiveByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x002000ffu, DecodeValidUint32(8, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x00ffffffu, DecodeValidUint32(8, "\xff\x80\xfe\xff\x07"));
+ // Maximum.
+ EXPECT_EQ(0x100000feu, DecodeValidUint32(8, "\xff\xff\xff\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff");
+}
+
+TEST(HpackInputStreamTest, SixByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x100000ffu, DecodeValidUint32(8, "\xff\x80\x80\x80\x80\x01"));
+ // Maximum.
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(8, "\xff\x80\xfe\xff\xff\x0f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\x80\xfe\xff\xff\x10");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff\xff");
+}
+
+// There are no valid uint32 encodings that are greater than six
+// bytes.
+TEST(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");
+}
+
+// The {Number}ByteIntegersOneToSevenBitPrefix tests below test that
+// certain integers are encoded correctly with an N-bit prefix in
+// exactly {Number} bytes for N in {1, 2, ..., 7}.
+
+TEST(HpackInputStreamTest, OneByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x80", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(6, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(6, string("\xc0", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(5, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(5, string("\xe0", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(4, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(4, string("\xf0", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(3, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(3, string("\xf8", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(2, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(2, string("\xfc", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\xfe", 1)));
+
+ // Maximums.
+ EXPECT_EQ(0x7eu, DecodeValidUint32(7, "\x7e"));
+ EXPECT_EQ(0x7eu, DecodeValidUint32(7, "\xfe"));
+ EXPECT_EQ(0x3eu, DecodeValidUint32(6, "\x3e"));
+ EXPECT_EQ(0x3eu, DecodeValidUint32(6, "\xfe"));
+ EXPECT_EQ(0x1eu, DecodeValidUint32(5, "\x1e"));
+ EXPECT_EQ(0x1eu, DecodeValidUint32(5, "\xfe"));
+ EXPECT_EQ(0x0eu, DecodeValidUint32(4, "\x0e"));
+ EXPECT_EQ(0x0eu, DecodeValidUint32(4, "\xfe"));
+ EXPECT_EQ(0x06u, DecodeValidUint32(3, "\x06"));
+ EXPECT_EQ(0x06u, DecodeValidUint32(3, "\xfe"));
+ EXPECT_EQ(0x02u, DecodeValidUint32(2, "\x02"));
+ EXPECT_EQ(0x02u, DecodeValidUint32(2, "\xfe"));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\xfe", 1)));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f");
+ ExpectDecodeUint32Invalid(7, "\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f");
+ ExpectDecodeUint32Invalid(6, "\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f");
+ ExpectDecodeUint32Invalid(5, "\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f");
+ ExpectDecodeUint32Invalid(4, "\xff");
+ ExpectDecodeUint32Invalid(3, "\x07");
+ ExpectDecodeUint32Invalid(3, "\xff");
+ ExpectDecodeUint32Invalid(2, "\x03");
+ ExpectDecodeUint32Invalid(2, "\xff");
+ ExpectDecodeUint32Invalid(1, "\x01");
+ ExpectDecodeUint32Invalid(1, "\xff");
+}
+
+TEST(HpackInputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\x7f\x00", 2)));
+ EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\xff\x00", 2)));
+ EXPECT_EQ(0x3fu, DecodeValidUint32(6, string("\x3f\x00", 2)));
+ EXPECT_EQ(0x3fu, DecodeValidUint32(6, string("\xff\x00", 2)));
+ EXPECT_EQ(0x1fu, DecodeValidUint32(5, string("\x1f\x00", 2)));
+ EXPECT_EQ(0x1fu, DecodeValidUint32(5, string("\xff\x00", 2)));
+ EXPECT_EQ(0x0fu, DecodeValidUint32(4, string("\x0f\x00", 2)));
+ EXPECT_EQ(0x0fu, DecodeValidUint32(4, string("\xff\x00", 2)));
+ EXPECT_EQ(0x07u, DecodeValidUint32(3, string("\x07\x00", 2)));
+ EXPECT_EQ(0x07u, DecodeValidUint32(3, string("\xff\x00", 2)));
+ EXPECT_EQ(0x03u, DecodeValidUint32(2, string("\x03\x00", 2)));
+ EXPECT_EQ(0x03u, DecodeValidUint32(2, string("\xff\x00", 2)));
+ EXPECT_EQ(0x01u, DecodeValidUint32(1, string("\x01\x00", 2)));
+ EXPECT_EQ(0x01u, DecodeValidUint32(1, string("\xff\x00", 2)));
+
+ // Maximums.
+ EXPECT_EQ(0xfeu, DecodeValidUint32(7, "\x7f\x7f"));
+ EXPECT_EQ(0xfeu, DecodeValidUint32(7, "\xff\x7f"));
+ EXPECT_EQ(0xbeu, DecodeValidUint32(6, "\x3f\x7f"));
+ EXPECT_EQ(0xbeu, DecodeValidUint32(6, "\xff\x7f"));
+ EXPECT_EQ(0x9eu, DecodeValidUint32(5, "\x1f\x7f"));
+ EXPECT_EQ(0x9eu, DecodeValidUint32(5, "\xff\x7f"));
+ EXPECT_EQ(0x8eu, DecodeValidUint32(4, "\x0f\x7f"));
+ EXPECT_EQ(0x8eu, DecodeValidUint32(4, "\xff\x7f"));
+ EXPECT_EQ(0x86u, DecodeValidUint32(3, "\x07\x7f"));
+ EXPECT_EQ(0x86u, DecodeValidUint32(3, "\xff\x7f"));
+ EXPECT_EQ(0x82u, DecodeValidUint32(2, "\x03\x7f"));
+ EXPECT_EQ(0x82u, DecodeValidUint32(2, "\xff\x7f"));
+ EXPECT_EQ(0x80u, DecodeValidUint32(1, "\x01\x7f"));
+ EXPECT_EQ(0x80u, DecodeValidUint32(1, "\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff");
+}
+
+TEST(HpackInputStreamTest, ThreeByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0xffu, DecodeValidUint32(7, "\x7f\x80\x01"));
+ EXPECT_EQ(0xffu, DecodeValidUint32(7, "\xff\x80\x01"));
+ EXPECT_EQ(0xbfu, DecodeValidUint32(6, "\x3f\x80\x01"));
+ EXPECT_EQ(0xbfu, DecodeValidUint32(6, "\xff\x80\x01"));
+ EXPECT_EQ(0x9fu, DecodeValidUint32(5, "\x1f\x80\x01"));
+ EXPECT_EQ(0x9fu, DecodeValidUint32(5, "\xff\x80\x01"));
+ EXPECT_EQ(0x8fu, DecodeValidUint32(4, "\x0f\x80\x01"));
+ EXPECT_EQ(0x8fu, DecodeValidUint32(4, "\xff\x80\x01"));
+ EXPECT_EQ(0x87u, DecodeValidUint32(3, "\x07\x80\x01"));
+ EXPECT_EQ(0x87u, DecodeValidUint32(3, "\xff\x80\x01"));
+ EXPECT_EQ(0x83u, DecodeValidUint32(2, "\x03\x80\x01"));
+ EXPECT_EQ(0x83u, DecodeValidUint32(2, "\xff\x80\x01"));
+ EXPECT_EQ(0x81u, DecodeValidUint32(1, "\x01\x80\x01"));
+ EXPECT_EQ(0x81u, DecodeValidUint32(1, "\xff\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0x407eu, DecodeValidUint32(7, "\x7f\xff\x7f"));
+ EXPECT_EQ(0x407eu, DecodeValidUint32(7, "\xff\xff\x7f"));
+ EXPECT_EQ(0x403eu, DecodeValidUint32(6, "\x3f\xff\x7f"));
+ EXPECT_EQ(0x403eu, DecodeValidUint32(6, "\xff\xff\x7f"));
+ EXPECT_EQ(0x401eu, DecodeValidUint32(5, "\x1f\xff\x7f"));
+ EXPECT_EQ(0x401eu, DecodeValidUint32(5, "\xff\xff\x7f"));
+ EXPECT_EQ(0x400eu, DecodeValidUint32(4, "\x0f\xff\x7f"));
+ EXPECT_EQ(0x400eu, DecodeValidUint32(4, "\xff\xff\x7f"));
+ EXPECT_EQ(0x4006u, DecodeValidUint32(3, "\x07\xff\x7f"));
+ EXPECT_EQ(0x4006u, DecodeValidUint32(3, "\xff\xff\x7f"));
+ EXPECT_EQ(0x4002u, DecodeValidUint32(2, "\x03\xff\x7f"));
+ EXPECT_EQ(0x4002u, DecodeValidUint32(2, "\xff\xff\x7f"));
+ EXPECT_EQ(0x4000u, DecodeValidUint32(1, "\x01\xff\x7f"));
+ EXPECT_EQ(0x4000u, DecodeValidUint32(1, "\xff\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\xff\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xff\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xff\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xff\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xff\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xff\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xff\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff");
+}
+
+TEST(HpackInputStreamTest, FourByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x407fu, DecodeValidUint32(7, "\x7f\x80\x80\x01"));
+ EXPECT_EQ(0x407fu, DecodeValidUint32(7, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x403fu, DecodeValidUint32(6, "\x3f\x80\x80\x01"));
+ EXPECT_EQ(0x403fu, DecodeValidUint32(6, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x401fu, DecodeValidUint32(5, "\x1f\x80\x80\x01"));
+ EXPECT_EQ(0x401fu, DecodeValidUint32(5, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x400fu, DecodeValidUint32(4, "\x0f\x80\x80\x01"));
+ EXPECT_EQ(0x400fu, DecodeValidUint32(4, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x4007u, DecodeValidUint32(3, "\x07\x80\x80\x01"));
+ EXPECT_EQ(0x4007u, DecodeValidUint32(3, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x4003u, DecodeValidUint32(2, "\x03\x80\x80\x01"));
+ EXPECT_EQ(0x4003u, DecodeValidUint32(2, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x4001u, DecodeValidUint32(1, "\x01\x80\x80\x01"));
+ EXPECT_EQ(0x4001u, DecodeValidUint32(1, "\xff\x80\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0x20007eu, DecodeValidUint32(7, "\x7f\xff\xff\x7f"));
+ EXPECT_EQ(0x20007eu, DecodeValidUint32(7, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x20003eu, DecodeValidUint32(6, "\x3f\xff\xff\x7f"));
+ EXPECT_EQ(0x20003eu, DecodeValidUint32(6, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x20001eu, DecodeValidUint32(5, "\x1f\xff\xff\x7f"));
+ EXPECT_EQ(0x20001eu, DecodeValidUint32(5, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x20000eu, DecodeValidUint32(4, "\x0f\xff\xff\x7f"));
+ EXPECT_EQ(0x20000eu, DecodeValidUint32(4, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x200006u, DecodeValidUint32(3, "\x07\xff\xff\x7f"));
+ EXPECT_EQ(0x200006u, DecodeValidUint32(3, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x200002u, DecodeValidUint32(2, "\x03\xff\xff\x7f"));
+ EXPECT_EQ(0x200002u, DecodeValidUint32(2, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x200000u, DecodeValidUint32(1, "\x01\xff\xff\x7f"));
+ EXPECT_EQ(0x200000u, DecodeValidUint32(1, "\xff\xff\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xff\xff\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xff\xff\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xff\xff\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff");
+}
+
+TEST(HpackInputStreamTest, FiveByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x20007fu, DecodeValidUint32(7, "\x7f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20007fu, DecodeValidUint32(7, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20003fu, DecodeValidUint32(6, "\x3f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20003fu, DecodeValidUint32(6, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20001fu, DecodeValidUint32(5, "\x1f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20001fu, DecodeValidUint32(5, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20000fu, DecodeValidUint32(4, "\x0f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20000fu, DecodeValidUint32(4, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200007u, DecodeValidUint32(3, "\x07\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200007u, DecodeValidUint32(3, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200003u, DecodeValidUint32(2, "\x03\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200003u, DecodeValidUint32(2, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200001u, DecodeValidUint32(1, "\x01\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200001u, DecodeValidUint32(1, "\xff\x80\x80\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0x1000007eu, DecodeValidUint32(7, "\x7f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000007eu, DecodeValidUint32(7, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000003eu, DecodeValidUint32(6, "\x3f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000003eu, DecodeValidUint32(6, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000001eu, DecodeValidUint32(5, "\x1f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000001eu, DecodeValidUint32(5, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000000eu, DecodeValidUint32(4, "\x0f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000000eu, DecodeValidUint32(4, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000006u, DecodeValidUint32(3, "\x07\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000006u, DecodeValidUint32(3, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000002u, DecodeValidUint32(2, "\x03\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000002u, DecodeValidUint32(2, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000000u, DecodeValidUint32(1, "\x01\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000000u, DecodeValidUint32(1, "\xff\xff\xff\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff\xff");
+}
+
+TEST(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"));
+ EXPECT_EQ(0x1000003fu, DecodeValidUint32(6, "\x3f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000003fu, DecodeValidUint32(6, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000001fu, DecodeValidUint32(5, "\x1f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000001fu, DecodeValidUint32(5, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000000fu, DecodeValidUint32(4, "\x0f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000000fu, DecodeValidUint32(4, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000007u, DecodeValidUint32(3, "\x07\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000007u, DecodeValidUint32(3, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000003u, DecodeValidUint32(2, "\x03\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000003u, DecodeValidUint32(2, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000001u, DecodeValidUint32(1, "\x01\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000001u, DecodeValidUint32(1, "\xff\x80\x80\x80\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(7, "\x7f\x80\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(7, "\xff\x80\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(6, "\x3f\xc0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(6, "\xff\xc0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(5, "\x1f\xe0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(5, "\xff\xe0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(4, "\x0f\xf0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(4, "\xff\xf0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(3, "\x07\xf8\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(3, "\xff\xf8\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(2, "\x03\xfc\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(2, "\xff\xfc\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(1, "\x01\xfe\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(1, "\xff\xfe\xff\xff\xff\x0f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\x80\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(7, "\xff\x80\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xc0\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(6, "\xff\xc0\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xe0\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(5, "\xff\xe0\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xf0\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(4, "\xff\xf0\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xf8\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(3, "\xff\xf8\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xfc\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(2, "\xff\xfc\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xfe\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(1, "\xff\xfe\xff\xff\xff\xff");
+}
+
+// There are no valid uint32 encodings that are greater than six
+// bytes.
+TEST(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");
+ ExpectDecodeUint32Invalid(6, "\x3f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(6, "\x3f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(5, "\x1f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(4, "\x0f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(3, "\x07\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(2, "\x03\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(1, "\x01\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff\xff\xff\xff");
+}
+
+// Decoding a valid encoded string literal should work.
+TEST(HpackInputStreamTest, DecodeNextStringLiteral) {
+ HpackInputStream input_stream(kuint32max, "\x0estring literal");
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ StringPiece string_piece;
+ EXPECT_TRUE(input_stream.DecodeNextStringLiteralForTest(&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) {
+ HpackInputStream input_stream(13, "\x0estring literal");
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ StringPiece string_piece;
+ EXPECT_FALSE(input_stream.DecodeNextStringLiteralForTest(&string_piece));
+}
+
+// Decoding an encoded string literal with size larger than the
+// remainder of the buffer should fail.
+TEST(HpackInputStreamTest, DecodeNextStringLiteralInvalidSize) {
+ // Set the length to be one more than it should be.
+ HpackInputStream input_stream(kuint32max, "\x0fstring literal");
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ StringPiece string_piece;
+ EXPECT_FALSE(input_stream.DecodeNextStringLiteralForTest(&string_piece));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/spdy/hpack_output_stream.cc b/net/spdy/hpack_output_stream.cc
index 0a20d96..e499fbf 100644
--- a/net/spdy/hpack_output_stream.cc
+++ b/net/spdy/hpack_output_stream.cc
@@ -5,7 +5,8 @@
#include "net/spdy/hpack_output_stream.h"
#include "base/logging.h"
-#include "net/spdy/hpack_constants.h"
+
+using base::StringPiece;
namespace net {
@@ -18,9 +19,9 @@ HpackOutputStream::HpackOutputStream(uint32 max_string_literal_size)
HpackOutputStream::~HpackOutputStream() {}
bool HpackOutputStream::AppendLiteralHeaderNoIndexingWithName(
- base::StringPiece name, base::StringPiece value) {
- AppendBits(kLiteralNoIndexOpcode, kLiteralNoIndexOpcodeSize);
- AppendBits(0x0, 8 - kLiteralNoIndexOpcodeSize);
+ StringPiece name, StringPiece value) {
+ AppendPrefix(kLiteralNoIndexOpcode);
+ AppendBits(0x0, 8 - kLiteralNoIndexOpcode.bit_size);
if (!AppendStringLiteral(name))
return false;
if (!AppendStringLiteral(value))
@@ -37,15 +38,15 @@ void HpackOutputStream::TakeString(string* output) {
bit_offset_ = 0;
}
-void HpackOutputStream::AppendBits(uint8 bits, size_t size) {
- DCHECK_GT(size, 0u);
- DCHECK_LE(size, 8u);
- DCHECK_EQ(bits >> size, 0);
- size_t new_bit_offset = bit_offset_ + size;
+void HpackOutputStream::AppendBits(uint8 bits, size_t bit_size) {
+ DCHECK_GT(bit_size, 0u);
+ DCHECK_LE(bit_size, 8u);
+ DCHECK_EQ(bits >> bit_size, 0);
+ size_t new_bit_offset = bit_offset_ + bit_size;
if (bit_offset_ == 0) {
// Buffer ends on a byte boundary.
- DCHECK_LE(size, 8u);
- buffer_.append(1, bits << (8 - size));
+ DCHECK_LE(bit_size, 8u);
+ buffer_.append(1, bits << (8 - bit_size));
} else if (new_bit_offset <= 8) {
// Buffer does not end on a byte boundary but the given bits fit
// in the remainder of the last byte.
@@ -59,6 +60,10 @@ void HpackOutputStream::AppendBits(uint8 bits, size_t size) {
bit_offset_ = new_bit_offset % 8;
}
+void HpackOutputStream::AppendPrefix(HpackPrefix prefix) {
+ AppendBits(prefix.bits, prefix.bit_size);
+}
+
void HpackOutputStream::AppendUint32(uint32 I) {
// The algorithm below is adapted from the pseudocode in 4.1.1.
size_t N = 8 - bit_offset_;
@@ -79,7 +84,7 @@ void HpackOutputStream::AppendUint32(uint32 I) {
bool HpackOutputStream::AppendStringLiteral(base::StringPiece str) {
DCHECK_EQ(bit_offset_, 0u);
// TODO(akalin): Implement Huffman encoding.
- AppendBits(kStringLiteralIdentityEncoded, kStringLiteralIdentityEncodedSize);
+ AppendPrefix(kStringLiteralIdentityEncoded);
if (str.size() > max_string_literal_size_)
return false;
AppendUint32(static_cast<uint32>(str.size()));
diff --git a/net/spdy/hpack_output_stream.h b/net/spdy/hpack_output_stream.h
index f041171..f82ec92 100644
--- a/net/spdy/hpack_output_stream.h
+++ b/net/spdy/hpack_output_stream.h
@@ -12,6 +12,7 @@
#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_encoding_context.h"
// All section references below are to
@@ -54,11 +55,14 @@ class NET_EXPORT_PRIVATE HpackOutputStream {
}
private:
- // Appends the lower |size| bits of |bits| to the internal buffer.
+ // Appends the lower |bit_size| bits of |bits| to the internal buffer.
//
- // |size| must be > 0 and <= 8. |bits| must not have any bits set
- // |other than the lower |size| bits.
- void AppendBits(uint8 bits, size_t size);
+ // |bit_size| must be > 0 and <= 8. |bits| must not have any bits
+ // set other than the lower |bit_size| bits.
+ void AppendBits(uint8 bits, size_t bit_size);
+
+ // Simply forwards to AppendBits(prefix.bits, prefix.bit-size).
+ void AppendPrefix(HpackPrefix prefix);
// Appends the given integer using the representation described in
// 4.1.1. If the internal buffer ends on a byte boundary, the prefix