blob: 064ae44041a46a3b2cf4dcc7708c1d68f9a40f3d [file] [log] [blame]
// 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 "quiche/http2/hpack/decoder/hpack_block_decoder.h"
// Tests of HpackBlockDecoder.
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
#include "quiche/http2/decoder/decode_buffer.h"
#include "quiche/http2/hpack/http2_hpack_constants.h"
#include "quiche/http2/test_tools/hpack_block_builder.h"
#include "quiche/http2/test_tools/hpack_block_collector.h"
#include "quiche/http2/test_tools/hpack_example.h"
#include "quiche/http2/test_tools/http2_random.h"
#include "quiche/http2/test_tools/random_decoder_test_base.h"
#include "quiche/common/platform/api/quiche_expect_bug.h"
#include "quiche/common/platform/api/quiche_test.h"
namespace http2 {
namespace test {
namespace {
class HpackBlockDecoderTest : public RandomDecoderTest {
protected:
HpackBlockDecoderTest() : listener_(&collector_), decoder_(&listener_) {
stop_decode_on_done_ = false;
decoder_.Reset();
// Make sure logging doesn't crash. Not examining the result.
std::ostringstream strm;
strm << decoder_;
}
DecodeStatus StartDecoding(DecodeBuffer* db) override {
collector_.Clear();
decoder_.Reset();
return ResumeDecoding(db);
}
DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
DecodeStatus status = decoder_.Decode(db);
// Make sure logging doesn't crash. Not examining the result.
std::ostringstream strm;
strm << decoder_;
return status;
}
AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
const Validator& validator) {
bool return_non_zero_on_first = false;
return RandomDecoderTest::DecodeAndValidateSeveralWays(
db, return_non_zero_on_first, validator);
}
AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
const Validator& validator) {
DecodeBuffer db(hbb.buffer());
return DecodeAndValidateSeveralWays(&db, validator);
}
AssertionResult DecodeHpackExampleAndValidateSeveralWays(
absl::string_view hpack_example, Validator validator) {
std::string input = HpackExampleToStringOrDie(hpack_example);
DecodeBuffer db(input);
return DecodeAndValidateSeveralWays(&db, validator);
}
uint8_t Rand8() { return Random().Rand8(); }
std::string Rand8String() { return Random().RandString(Rand8()); }
HpackBlockCollector collector_;
HpackEntryDecoderVLoggingListener listener_;
HpackBlockDecoder decoder_;
};
// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.1
TEST_F(HpackBlockDecoderTest, SpecExample_C_2_1) {
auto do_check = [this]() {
return collector_.ValidateSoleLiteralNameValueHeader(
HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
"custom-header");
};
const char hpack_example[] = R"(
40 | == Literal indexed ==
0a | Literal name (len = 10)
6375 7374 6f6d 2d6b 6579 | custom-key
0d | Literal value (len = 13)
6375 7374 6f6d 2d68 6561 6465 72 | custom-header
| -> custom-key:
| custom-header
)";
EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
hpack_example, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.2
TEST_F(HpackBlockDecoderTest, SpecExample_C_2_2) {
auto do_check = [this]() {
return collector_.ValidateSoleLiteralValueHeader(
HpackEntryType::kUnindexedLiteralHeader, 4, false, "/sample/path");
};
const char hpack_example[] = R"(
04 | == Literal not indexed ==
| Indexed name (idx = 4)
| :path
0c | Literal value (len = 12)
2f73 616d 706c 652f 7061 7468 | /sample/path
| -> :path: /sample/path
)";
EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
hpack_example, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.3
TEST_F(HpackBlockDecoderTest, SpecExample_C_2_3) {
auto do_check = [this]() {
return collector_.ValidateSoleLiteralNameValueHeader(
HpackEntryType::kNeverIndexedLiteralHeader, false, "password", false,
"secret");
};
const char hpack_example[] = R"(
10 | == Literal never indexed ==
08 | Literal name (len = 8)
7061 7373 776f 7264 | password
06 | Literal value (len = 6)
7365 6372 6574 | secret
| -> password: secret
)";
EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
hpack_example, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.4
TEST_F(HpackBlockDecoderTest, SpecExample_C_2_4) {
auto do_check = [this]() { return collector_.ValidateSoleIndexedHeader(2); };
const char hpack_example[] = R"(
82 | == Indexed - Add ==
| idx = 2
| -> :method: GET
)";
EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
hpack_example, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
// http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1
TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) {
std::string example = R"(
82 | == Indexed - Add ==
| idx = 2
| -> :method: GET
86 | == Indexed - Add ==
| idx = 6
| -> :scheme: http
84 | == Indexed - Add ==
| idx = 4
| -> :path: /
41 | == Literal indexed ==
| Indexed name (idx = 1)
| :authority
0f | Literal value (len = 15)
7777 772e 6578 616d 706c 652e 636f 6d | www.example.com
| -> :authority:
| www.example.com
)";
HpackBlockCollector expected;
expected.ExpectIndexedHeader(2);
expected.ExpectIndexedHeader(6);
expected.ExpectIndexedHeader(4);
expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
1, false, "www.example.com");
NoArgValidator do_check = [expected, this]() {
return collector_.VerifyEq(expected);
};
EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
example, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
// http://httpwg.org/specs/rfc7541.html#rfc.section.C.5.1
TEST_F(HpackBlockDecoderTest, SpecExample_C_5_1) {
std::string example = R"(
48 | == Literal indexed ==
| Indexed name (idx = 8)
| :status
03 | Literal value (len = 3)
3330 32 | 302
| -> :status: 302
58 | == Literal indexed ==
| Indexed name (idx = 24)
| cache-control
07 | Literal value (len = 7)
7072 6976 6174 65 | private
| -> cache-control: private
61 | == Literal indexed ==
| Indexed name (idx = 33)
| date
1d | Literal value (len = 29)
4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
2032 303a 3133 3a32 3120 474d 54 | 20:13:21 GMT
| -> date: Mon, 21 Oct 2013
| 20:13:21 GMT
6e | == Literal indexed ==
| Indexed name (idx = 46)
| location
17 | Literal value (len = 23)
6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
706c 652e 636f 6d | ple.com
| -> location:
| https://www.example.com
)";
HpackBlockCollector expected;
expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
8, false, "302");
expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
24, false, "private");
expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
33, false,
"Mon, 21 Oct 2013 20:13:21 GMT");
expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
46, false, "https://www.example.com");
NoArgValidator do_check = [expected, this]() {
return collector_.VerifyEq(expected);
};
EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
example, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
// Generate a bunch of HPACK block entries to expect, use those expectations
// to generate an HPACK block, then decode it and confirm it matches those
// expectations. Some of these are invalid (such as Indexed, with index=0),
// but well-formed, and the decoder doesn't check for validity, just
// well-formedness. That includes the validity of the strings not being checked,
// such as lower-case ascii for the names, and valid Huffman encodings.
TEST_F(HpackBlockDecoderTest, Computed) {
HpackBlockCollector expected;
expected.ExpectIndexedHeader(0);
expected.ExpectIndexedHeader(1);
expected.ExpectIndexedHeader(126);
expected.ExpectIndexedHeader(127);
expected.ExpectIndexedHeader(128);
expected.ExpectDynamicTableSizeUpdate(0);
expected.ExpectDynamicTableSizeUpdate(1);
expected.ExpectDynamicTableSizeUpdate(14);
expected.ExpectDynamicTableSizeUpdate(15);
expected.ExpectDynamicTableSizeUpdate(30);
expected.ExpectDynamicTableSizeUpdate(31);
expected.ExpectDynamicTableSizeUpdate(4095);
expected.ExpectDynamicTableSizeUpdate(4096);
expected.ExpectDynamicTableSizeUpdate(8192);
for (auto type : {HpackEntryType::kIndexedLiteralHeader,
HpackEntryType::kUnindexedLiteralHeader,
HpackEntryType::kNeverIndexedLiteralHeader}) {
for (bool value_huffman : {false, true}) {
// An entry with an index for the name. Ensure the name index
// is not zero by adding one to the Rand8() result.
expected.ExpectNameIndexAndLiteralValue(type, Rand8() + 1, value_huffman,
Rand8String());
// And two entries with literal names, one plain, one huffman encoded.
expected.ExpectLiteralNameAndValue(type, false, Rand8String(),
value_huffman, Rand8String());
expected.ExpectLiteralNameAndValue(type, true, Rand8String(),
value_huffman, Rand8String());
}
}
// Shuffle the entries and serialize them to produce an HPACK block.
expected.ShuffleEntries(RandomPtr());
HpackBlockBuilder hbb;
expected.AppendToHpackBlockBuilder(&hbb);
NoArgValidator do_check = [expected, this]() {
return collector_.VerifyEq(expected);
};
EXPECT_TRUE(
DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
} // namespace
} // namespace test
} // namespace http2