diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/net.gyp | 6 | ||||
-rw-r--r-- | net/spdy/hpack_constants.h | 13 | ||||
-rw-r--r-- | net/spdy/hpack_decoder.cc | 86 | ||||
-rw-r--r-- | net/spdy/hpack_decoder.h | 64 | ||||
-rw-r--r-- | net/spdy/hpack_decoder_test.cc | 98 | ||||
-rw-r--r-- | net/spdy/hpack_input_stream.cc | 117 | ||||
-rw-r--r-- | net/spdy/hpack_input_stream.h | 79 | ||||
-rw-r--r-- | net/spdy/hpack_input_stream_test.cc | 490 | ||||
-rw-r--r-- | net/spdy/hpack_output_stream.cc | 29 | ||||
-rw-r--r-- | net/spdy/hpack_output_stream.h | 12 |
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 |