Index: net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc |
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3ad47351b54be3ee72a12e876ca323676f5096b0 |
--- /dev/null |
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc |
@@ -0,0 +1,292 @@ |
+// Copyright 2016 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/http2/hpack/huffman/http2_hpack_huffman_decoder.h" |
+ |
+// Tests of HpackHuffmanDecoder and HuffmanBitBuffer. |
+ |
+#include <iostream> |
+#include <string> |
+ |
+#include "base/bind.h" |
+#include "base/bind_helpers.h" |
+#include "base/macros.h" |
+#include "base/strings/string_piece.h" |
+#include "net/http2/decoder/decode_buffer.h" |
+#include "net/http2/decoder/decode_status.h" |
+#include "net/http2/tools/failure.h" |
+#include "net/http2/tools/random_decoder_test.h" |
+#include "net/spdy/spdy_test_utils.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using ::testing::AssertionResult; |
+using ::testing::AssertionSuccess; |
+using base::StringPiece; |
+using std::string; |
+ |
+namespace net { |
+namespace test { |
+namespace { |
+ |
+TEST(HuffmanBitBufferTest, Reset) { |
+ HuffmanBitBuffer bb; |
+ EXPECT_TRUE(bb.IsEmpty()); |
+ EXPECT_TRUE(bb.InputProperlyTerminated()); |
+ EXPECT_EQ(bb.count(), 0u); |
+ EXPECT_EQ(bb.free_count(), 64u); |
+ EXPECT_EQ(bb.value(), 0u); |
+} |
+ |
+TEST(HuffmanBitBufferTest, AppendBytesAligned) { |
+ string s; |
+ s.push_back('\x11'); |
+ s.push_back('\x22'); |
+ s.push_back('\x33'); |
+ StringPiece sp(s); |
+ |
+ HuffmanBitBuffer bb; |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_TRUE(sp.empty()); |
+ EXPECT_FALSE(bb.IsEmpty()) << bb; |
+ EXPECT_FALSE(bb.InputProperlyTerminated()); |
+ EXPECT_EQ(bb.count(), 24u) << bb; |
+ EXPECT_EQ(bb.free_count(), 40u) << bb; |
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 40) << bb; |
+ |
+ s.clear(); |
+ s.push_back('\x44'); |
+ sp = s; |
+ |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_TRUE(sp.empty()); |
+ EXPECT_EQ(bb.count(), 32u) << bb; |
+ EXPECT_EQ(bb.free_count(), 32u) << bb; |
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x11223344) << 32) << bb; |
+ |
+ s.clear(); |
+ s.push_back('\x55'); |
+ s.push_back('\x66'); |
+ s.push_back('\x77'); |
+ s.push_back('\x88'); |
+ s.push_back('\x99'); |
+ sp = s; |
+ |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_EQ(sp.size(), 1u); |
+ EXPECT_EQ('\x99', sp[0]); |
+ EXPECT_EQ(bb.count(), 64u) << bb; |
+ EXPECT_EQ(bb.free_count(), 0u) << bb; |
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb; |
+ |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_EQ(sp.size(), 1u); |
+ EXPECT_EQ('\x99', sp[0]); |
+ EXPECT_EQ(bb.count(), 64u) << bb; |
+ EXPECT_EQ(bb.free_count(), 0u) << bb; |
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb; |
+} |
+ |
+TEST(HuffmanBitBufferTest, ConsumeBits) { |
+ string s; |
+ s.push_back('\x11'); |
+ s.push_back('\x22'); |
+ s.push_back('\x33'); |
+ StringPiece sp(s); |
+ |
+ HuffmanBitBuffer bb; |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_TRUE(sp.empty()); |
+ |
+ bb.ConsumeBits(1); |
+ EXPECT_EQ(bb.count(), 23u) << bb; |
+ EXPECT_EQ(bb.free_count(), 41u) << bb; |
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 41) << bb; |
+ |
+ bb.ConsumeBits(20); |
+ EXPECT_EQ(bb.count(), 3u) << bb; |
+ EXPECT_EQ(bb.free_count(), 61u) << bb; |
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x3) << 61) << bb; |
+} |
+ |
+TEST(HuffmanBitBufferTest, AppendBytesUnaligned) { |
+ string s; |
+ s.push_back('\x11'); |
+ s.push_back('\x22'); |
+ s.push_back('\x33'); |
+ s.push_back('\x44'); |
+ s.push_back('\x55'); |
+ s.push_back('\x66'); |
+ s.push_back('\x77'); |
+ s.push_back('\x88'); |
+ s.push_back('\x99'); |
+ s.push_back('\xaa'); |
+ s.push_back('\xbb'); |
+ s.push_back('\xcc'); |
+ s.push_back('\xdd'); |
+ StringPiece sp(s); |
+ |
+ HuffmanBitBuffer bb; |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_EQ(sp.size(), 5u); |
+ EXPECT_FALSE(bb.InputProperlyTerminated()); |
+ |
+ bb.ConsumeBits(15); |
+ EXPECT_EQ(bb.count(), 49u) << bb; |
+ EXPECT_EQ(bb.free_count(), 15u) << bb; |
+ |
+ HuffmanAccumulator expected(0x1122334455667788); |
+ expected <<= 15; |
+ EXPECT_EQ(bb.value(), expected); |
+ |
+ sp.remove_prefix(bb.AppendBytes(sp)); |
+ EXPECT_EQ(sp.size(), 4u); |
+ EXPECT_EQ(bb.count(), 57u) << bb; |
+ EXPECT_EQ(bb.free_count(), 7u) << bb; |
+ |
+ expected |= (HuffmanAccumulator(0x99) << 7); |
+ EXPECT_EQ(bb.value(), expected) << bb << std::hex |
+ << "\n actual: " << bb.value() |
+ << "\n expected: " << expected; |
+} |
+ |
+enum class DecoderChoice { IF_TREE, SHORT_CODE }; |
+ |
+class HpackHuffmanDecoderTest |
+ : public RandomDecoderTest, |
+ public ::testing::WithParamInterface<DecoderChoice> { |
+ protected: |
+ HpackHuffmanDecoderTest() { |
+ // The decoder may return true, and its accumulator may be empty, at |
+ // many boundaries while decoding, and yet the whole string hasn't |
+ // been decoded. |
+ stop_decode_on_done_ = false; |
+ } |
+ |
+ DecodeStatus StartDecoding(DecodeBuffer* b) override { |
+ input_bytes_seen_ = 0; |
+ output_buffer_.clear(); |
+ decoder_.Reset(); |
+ return ResumeDecoding(b); |
+ } |
+ |
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override { |
+ input_bytes_seen_ += b->Remaining(); |
+ StringPiece sp(b->cursor(), b->Remaining()); |
+ if (DecodeFragment(sp)) { |
+ b->AdvanceCursor(b->Remaining()); |
+ // Successfully decoded (or buffered) the bytes in StringPiece. |
+ EXPECT_LE(input_bytes_seen_, input_bytes_expected_); |
+ // Have we reached the end of the encoded string? |
+ if (input_bytes_expected_ == input_bytes_seen_) { |
+ if (decoder_.InputProperlyTerminated()) { |
+ return DecodeStatus::kDecodeDone; |
+ } else { |
+ return DecodeStatus::kDecodeError; |
+ } |
+ } |
+ return DecodeStatus::kDecodeInProgress; |
+ } |
+ return DecodeStatus::kDecodeError; |
+ } |
+ |
+ bool DecodeFragment(StringPiece sp) { |
+ switch (GetParam()) { |
+ case DecoderChoice::IF_TREE: |
+ return decoder_.DecodeWithIfTreeAndStruct(sp, &output_buffer_); |
+ case DecoderChoice::SHORT_CODE: |
+ return decoder_.DecodeShortCodesFirst(sp, &output_buffer_); |
+ } |
+ |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ AssertionResult ValidatorForHuffmanDecodeAndValidateSeveralWays( |
+ StringPiece expected_plain) { |
+ VERIFY_EQ(output_buffer_.size(), expected_plain.size()); |
+ VERIFY_EQ(output_buffer_, expected_plain); |
+ return AssertionSuccess(); |
+ } |
+ |
+ AssertionResult HuffmanDecodeAndValidateSeveralWays( |
+ StringPiece encoded, |
+ StringPiece expected_plain) { |
+ input_bytes_expected_ = encoded.size(); |
+ DecodeBuffer db(encoded); |
+ bool return_non_zero_on_first = false; |
+ return DecodeAndValidateSeveralWays( |
+ &db, return_non_zero_on_first, |
+ ValidateDoneAndEmpty( |
+ base::Bind(&HpackHuffmanDecoderTest:: |
+ ValidatorForHuffmanDecodeAndValidateSeveralWays, |
+ base::Unretained(this), expected_plain))); |
+ } |
+ |
+ HpackHuffmanDecoder decoder_; |
+ string output_buffer_; |
+ size_t input_bytes_seen_; |
+ size_t input_bytes_expected_; |
+}; |
+INSTANTIATE_TEST_CASE_P(AllDecoders, |
+ HpackHuffmanDecoderTest, |
+ ::testing::Values(DecoderChoice::IF_TREE, |
+ DecoderChoice::SHORT_CODE)); |
+ |
+TEST_P(HpackHuffmanDecoderTest, SpecRequestExamples) { |
+ HpackHuffmanDecoder decoder; |
+ string test_table[] = { |
+ a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"), |
+ "www.example.com", |
+ a2b_hex("a8eb10649cbf"), |
+ "no-cache", |
+ a2b_hex("25a849e95ba97d7f"), |
+ "custom-key", |
+ a2b_hex("25a849e95bb8e8b4bf"), |
+ "custom-value", |
+ }; |
+ for (size_t i = 0; i != arraysize(test_table); i += 2) { |
+ const string& huffman_encoded(test_table[i]); |
+ const string& plain_string(test_table[i + 1]); |
+ string buffer; |
+ decoder.Reset(); |
+ EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder; |
+ EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder; |
+ EXPECT_EQ(buffer, plain_string); |
+ } |
+} |
+ |
+TEST_P(HpackHuffmanDecoderTest, SpecResponseExamples) { |
+ HpackHuffmanDecoder decoder; |
+ // clang-format off |
+ string test_table[] = { |
+ a2b_hex("6402"), |
+ "302", |
+ a2b_hex("aec3771a4b"), |
+ "private", |
+ a2b_hex("d07abe941054d444a8200595040b8166" |
+ "e082a62d1bff"), |
+ "Mon, 21 Oct 2013 20:13:21 GMT", |
+ a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43" |
+ "d3"), |
+ "https://www.example.com", |
+ a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960" |
+ "d5af27087f3672c1ab270fb5291f9587" |
+ "316065c003ed4ee5b1063d5007"), |
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", |
+ }; |
+ // clang-format on |
+ for (size_t i = 0; i != arraysize(test_table); i += 2) { |
+ const string& huffman_encoded(test_table[i]); |
+ const string& plain_string(test_table[i + 1]); |
+ string buffer; |
+ decoder.Reset(); |
+ EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder; |
+ EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder; |
+ EXPECT_EQ(buffer, plain_string); |
+ } |
+} |
+ |
+} // namespace |
+} // namespace test |
+} // namespace net |