blob: a91f952cecb3ba16b4b073993f759efc8ab285a6 [file] [log] [blame]
// Tests decoding all of the fixed size HTTP/2 structures that are in the frame
// payload (i.e. those defined in quiche/http2/core/http2_structures.h,
// except Http2FrameHeader) using Http2StructureDecoder, which handles buffering
// of structures split across input buffer boundaries, and in turn uses DoDecode
// when it has all of a structure in a contiguous buffer.
#include <stddef.h>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/flags/flag.h"
#include "absl/strings/string_view.h"
#include "quiche/http2/core/http2_constants.h"
#include "quiche/http2/core/http2_structures.h"
#include "quiche/http2/decoder/decode_buffer.h"
#include "quiche/http2/decoder/decode_status.h"
#include "quiche/http2/decoder/http2_structure_decoder.h"
#include "quiche/http2/test_tools/http2_structure_decoder_test_util.h"
#include "quiche/http2/test_tools/http2_structures_test_util.h"
#include "quiche/http2/test_tools/random_decoder_test_base.h"
#include "quiche/http2/test_tools/verify_macros.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_text_utils.h"
ABSL_FLAG(uint64_t /* allow-non-std-int */, random_decode_count, 100,
"Number of randomized structures of each type to decode.");
using ::testing::AssertionSuccess;
namespace http2 {
namespace test {
namespace {
// A template providing the base for all of the type-specific tests.
template <class Structure>
class Http2StructureDecoderRemainingPayloadTest : public RandomDecoderTest {
protected:
Http2StructureDecoderRemainingPayloadTest() {
// IF the test adds more data after the encoded structure, stop as
// soon as the structure is decoded.
stop_decode_on_done_ = true;
}
void RandomizeStructure(Structure* ptr) {
http2::test::Randomize(ptr, RandomPtr());
}
DecodeStatus StartDecoding(DecodeBuffer* b) override {
// Overwrite the current contents of |structure_|, into which we'll
// decode the buffer, so that we can be confident that we really decoded
// the structure every time.
structure_ = std::make_unique<Structure>();
// Do the same with the decoder so that we can be sure the decoder
// is not depending on or affected by the state from the previous
// decoding calls.
Http2StructureDecoderPeer::Randomize(&structure_decoder_, RandomPtr());
const uint32_t avail = b->Remaining();
QUICHE_VLOG(2) << "StartDecoding, avail=" << b->Remaining()
<< "; needed=" << Structure::EncodedSize();
remaining_payload_ = remaining_payload_at_start_;
DecodeStatus status =
structure_decoder_.Start(structure_.get(), b, &remaining_payload_);
const uint32_t consumed_payload =
remaining_payload_at_start_ - remaining_payload_;
const uint32_t consumed_input = avail - b->Remaining();
EXPECT_EQ(consumed_payload, consumed_input);
if (status == DecodeStatus::kDecodeDone) {
EXPECT_EQ(consumed_payload, Structure::EncodedSize());
++fast_decode_count_;
} else if (status == DecodeStatus::kDecodeInProgress) {
EXPECT_LT(avail, Structure::EncodedSize());
EXPECT_EQ(0, b->Remaining());
EXPECT_EQ(avail, structure_decoder_.offset());
EXPECT_GT(remaining_payload_, 0);
++incomplete_start_count_;
} else {
EXPECT_EQ(status, DecodeStatus::kDecodeError);
EXPECT_EQ(remaining_payload_, 0);
++error_start_count_;
}
return status;
}
DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
const uint32_t old_offset = structure_decoder_.offset();
EXPECT_LT(old_offset, Structure::EncodedSize());
const uint32_t needed = Structure::EncodedSize() - old_offset;
const uint32_t avail = b->Remaining();
QUICHE_VLOG(2) << "ResumeDecoding, avail=" << b->Remaining()
<< "; needed=" << needed;
const uint32_t old_remaining_payload = remaining_payload_;
if (structure_decoder_.Resume(structure_.get(), b, &remaining_payload_)) {
EXPECT_EQ(structure_decoder_.offset(), Structure::EncodedSize());
EXPECT_GT(old_remaining_payload, remaining_payload_);
const uint32_t consumed_payload =
old_remaining_payload - remaining_payload_;
const uint32_t consumed_input = avail - b->Remaining();
EXPECT_EQ(needed, consumed_payload);
EXPECT_EQ(needed, consumed_input);
++slow_decode_count_;
return DecodeStatus::kDecodeDone;
} else if (remaining_payload_ == 0) {
EXPECT_GT(needed, old_remaining_payload);
++error_resume_count_;
return DecodeStatus::kDecodeError;
} else {
// There is more payload to come, whether or not it is sufficient to
// meet the needs of decoding a Structure.
EXPECT_GE(old_remaining_payload, remaining_payload_);
EXPECT_EQ(0, b->Remaining());
const uint32_t consumed = old_remaining_payload - remaining_payload_;
const uint32_t copied = structure_decoder_.offset() - old_offset;
EXPECT_EQ(copied, avail);
EXPECT_EQ(copied, consumed);
EXPECT_LT(avail, needed);
EXPECT_LT(structure_decoder_.offset(), Structure::EncodedSize());
++incomplete_resume_count_;
return DecodeStatus::kDecodeInProgress;
}
}
// Fully decodes the structure at the start of data, and confirms it matches
// *expected (if provided) if initial_remaining_payload is big enough. Note
// that data is expected to be big enough to include an instance of the
// Structure, and possibly more than that.
AssertionResult DecodeLeadingStructure(const Structure* expected,
absl::string_view data,
size_t initial_remaining_payload) {
QUICHE_VLOG(2) << "DecodeLeadingStructure: " << data.size()
<< " bytes of data: "
<< quiche::QuicheTextUtils::HexDump(data)
<< "\n initial_remaining_payload="
<< initial_remaining_payload
<< "\n needed=" << Structure::EncodedSize();
if (expected != nullptr) {
QUICHE_VLOG(2) << "DecodeLeadingStructure: expected: " << *expected;
}
remaining_payload_at_start_ = initial_remaining_payload;
HTTP2_VERIFY_LE(Structure::EncodedSize(), data.size());
DecodeBuffer original(data.substr(0, initial_remaining_payload));
// The validator is called after each of the several times that the input
// DecodeBuffer is decoded, each with a different segmentation of the input.
// Create an appropriate validator based on whether the payload is big
// enough or not.
Validator validator;
if (initial_remaining_payload >= Structure::EncodedSize()) {
// We expect complete decoding.
NoArgValidator inner_validator = [expected, this]() -> AssertionResult {
// remaining_payload_at_start_ should have been large enough.
HTTP2_VERIFY_LE(Structure::EncodedSize(), remaining_payload_at_start_);
// Remaining_payload_ should have dropped by Structure::EncodedSize().
HTTP2_VERIFY_EQ(remaining_payload_ + Structure::EncodedSize(),
remaining_payload_at_start_);
// Validate that structure_ matches the expected value, if provided.
if (expected != nullptr) {
HTTP2_VERIFY_EQ(*expected, *structure_);
}
return AssertionSuccess();
};
// First validate that decoding is done and that we've advanced the cursor
// the expected amount (i.e. the size of the encoded structure).
validator = ValidateDoneAndOffset(Structure::EncodedSize(),
std::move(inner_validator));
} else {
// Not enough payload left, so decoding should fail.
// We shouldn't have an expected value if we can't decode the structure.
HTTP2_VERIFY_LT(initial_remaining_payload, Structure::EncodedSize());
validator = [this](const DecodeBuffer& /*db*/,
DecodeStatus status) -> AssertionResult {
HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeError);
HTTP2_VERIFY_GT(Structure::EncodedSize(), remaining_payload_at_start_);
HTTP2_VERIFY_EQ(0, remaining_payload_);
// Should have filled the structure_decoder_'s buffer with all of the
// available data.
HTTP2_VERIFY_EQ(remaining_payload_at_start_,
structure_decoder_.offset());
return AssertionSuccess();
};
}
// Decode several times, with several segmentations of the input buffer.
fast_decode_count_ = 0;
slow_decode_count_ = 0;
error_start_count_ = 0;
error_resume_count_ = 0;
incomplete_start_count_ = 0;
incomplete_resume_count_ = 0;
HTTP2_VERIFY_SUCCESS(DecodeAndValidateSeveralWays(
&original, false /*return_non_zero_on_first*/, validator));
// Check that both the fast and slow decode or error paths have been tested,
// and if successful that the expected structure has been decoded.
if (initial_remaining_payload >= Structure::EncodedSize()) {
// Should have advanced the original DecodeBuffer to the end of the
// encoded structure.
HTTP2_VERIFY_EQ(Structure::EncodedSize(), original.Offset());
// Should have done both a fast decode, where StartDecoding was passed
// the entire input, and also a slow decode, where StartDecoding didn't
// get the whole thing, including an empty buffer.
HTTP2_VERIFY_LT(0, fast_decode_count_);
HTTP2_VERIFY_LT(0, slow_decode_count_);
HTTP2_VERIFY_LE(0, incomplete_start_count_);
// There should have been no errors during decoding.
HTTP2_VERIFY_EQ(0, error_start_count_);
HTTP2_VERIFY_EQ(0, error_resume_count_);
// If the input is at least two bytes long, then at least one segmentation
// of the input will result in ResumeDecoding being called twice, so we
// should have counted at least one incomplete resume.
if (Structure::EncodedSize() >= 2) {
HTTP2_VERIFY_LE(0, incomplete_resume_count_);
}
// Double check that the decoded structure has the expected value.
if (expected != nullptr) {
HTTP2_VERIFY_EQ(*expected, *structure_);
}
} else {
// We didn't have enough remaining payload, so we should have buffered
// all that was available in the decoder, and reached the end of the
// payload.
HTTP2_VERIFY_EQ(initial_remaining_payload, structure_decoder_.offset());
HTTP2_VERIFY_EQ(remaining_payload_, 0);
// Should not have succeeded in decoding fast or slow.
HTTP2_VERIFY_EQ(0, fast_decode_count_);
HTTP2_VERIFY_EQ(0, slow_decode_count_);
// Should determined there was an error during StartDecoding
// at least once.
HTTP2_VERIFY_LT(0, error_start_count_);
// If the input is at least one byte long, then at least one segmentation
// will result in an empty buffer being passed to StartDecoding.
if (initial_remaining_payload >= 1) {
HTTP2_VERIFY_LT(0, incomplete_start_count_);
HTTP2_VERIFY_LT(0, error_resume_count_);
// And if the input is at least 2 bytes long, then at least one
// segmentation of the input will result in ResumeDecoding being called
// twice, so we should have counted at least one incomplete resume.
if (initial_remaining_payload >= 2) {
HTTP2_VERIFY_LT(0, incomplete_resume_count_);
}
}
}
return AssertionSuccess();
}
// Fully decodes the Structure at the start of data, and confirms it matches
// *expected (if provided).
AssertionResult DecodeLeadingStructure(const Structure* expected,
absl::string_view data) {
QUICHE_VLOG(2) << "DecodeLeadingStructure: "
<< quiche::QuicheTextUtils::HexDump(data);
// Decode with various sizes of remaining payload, from none,
// to insufficient, to enough, and finally more than enough.
for (size_t initial_remaining_payload = 0;
initial_remaining_payload <= Structure::EncodedSize() + 1;
++initial_remaining_payload) {
HTTP2_VERIFY_SUCCESS(
DecodeLeadingStructure(expected, data, initial_remaining_payload));
}
return AssertionSuccess();
}
// We use unsigned here because MSVC considers chars signed by default, so
// char literals that are 0x80 or greater cause compiler errors if we don't
// declare our literal arrays to be of type unsigned char[].
template <size_t N>
AssertionResult DecodeLeadingStructure(const unsigned char (&data)[N]) {
absl::string_view s(reinterpret_cast<const char*>(data), N);
QUICHE_VLOG(2) << "DecodeLeadingStructure: "
<< quiche::QuicheTextUtils::HexDump(s);
return DecodeLeadingStructure(nullptr, s);
}
// Encode the structure |in_s| into bytes, then decode the bytes
// and validate that the decoder produced the same field values.
AssertionResult EncodeThenDecode(const Structure& in_s) {
std::string bytes = SerializeStructure(in_s);
EXPECT_EQ(Structure::EncodedSize(), bytes.size());
return DecodeLeadingStructure(&in_s, bytes);
}
// Fill a Structure with random values, encode and decode. And repeat.
AssertionResult TestDecodingRandomizedStructures(size_t count) {
for (size_t i = 0; i < count; ++i) {
Structure input;
RandomizeStructure(&input);
HTTP2_VERIFY_SUCCESS(EncodeThenDecode(input));
}
return AssertionSuccess();
}
uint32_t decode_offset_ = 0;
std::unique_ptr<Structure> structure_;
Http2StructureDecoder structure_decoder_;
size_t fast_decode_count_ = 0;
size_t slow_decode_count_ = 0;
size_t incomplete_start_count_ = 0;
size_t incomplete_resume_count_ = 0;
size_t error_start_count_ = 0;
size_t error_resume_count_ = 0;
private:
uint32_t remaining_payload_;
uint32_t remaining_payload_at_start_;
};
//------------------------------------------------------------------------------
class Http2PriorityFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2PriorityFields> {};
TEST_F(Http2PriorityFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x80, 0x00, 0x00, 0x05, // Exclusive (yes) and Dependency (5)
0xff, // Weight: 256 (after adding 1)
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(5, structure_->stream_dependency);
EXPECT_EQ(256, structure_->weight);
EXPECT_EQ(true, structure_->is_exclusive);
}
{
const unsigned char kData[] = {
0x7f, 0xff, 0xff, 0xff, // Exclusive (no) and Dependency (0x7fffffff)
0x00, // Weight: 1 (after adding 1)
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(StreamIdMask(), structure_->stream_dependency);
EXPECT_EQ(1, structure_->weight);
EXPECT_EQ(false, structure_->is_exclusive);
}
}
TEST_F(Http2PriorityFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2RstStreamFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2RstStreamFields> {};
TEST_F(Http2RstStreamFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x00, 0x00, 0x00, 0x01, // Error: PROTOCOL_ERROR
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_TRUE(structure_->IsSupportedErrorCode());
EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, structure_->error_code);
}
{
const unsigned char kData[] = {
0xff, 0xff, 0xff, 0xff, // Error: max uint32 (Unknown error code)
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_FALSE(structure_->IsSupportedErrorCode());
EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_->error_code);
}
}
TEST_F(Http2RstStreamFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2SettingFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2SettingFields> {};
TEST_F(Http2SettingFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x00, 0x01, // Setting: HEADER_TABLE_SIZE
0x00, 0x00, 0x40, 0x00, // Value: 16K
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_TRUE(structure_->IsSupportedParameter());
EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE, structure_->parameter);
EXPECT_EQ(1 << 14, structure_->value);
}
{
const unsigned char kData[] = {
0x00, 0x00, // Setting: Unknown (0)
0xff, 0xff, 0xff, 0xff, // Value: max uint32
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_FALSE(structure_->IsSupportedParameter());
EXPECT_EQ(static_cast<Http2SettingsParameter>(0), structure_->parameter);
}
}
TEST_F(Http2SettingFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2PushPromiseFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2PushPromiseFields> {
};
TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x00, 0x01, 0x8a, 0x92, // Promised Stream ID: 101010
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(101010, structure_->promised_stream_id);
}
{
// Promised stream id has R-bit (reserved for future use) set, which
// should be cleared by the decoder.
const unsigned char kData[] = {
0xff, 0xff, 0xff, 0xff, // Promised Stream ID: max uint31 and R-bit
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(StreamIdMask(), structure_->promised_stream_id);
}
}
TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2PingFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2PingFields> {};
TEST_F(Http2PingFieldsDecoderTest, DecodesLiteral) {
{
// Each byte is different, so can detect if order changed.
const unsigned char kData[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(ToStringPiece(kData), ToStringPiece(structure_->opaque_bytes));
}
{
// All zeros, detect problems handling NULs.
const unsigned char kData[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(ToStringPiece(kData), ToStringPiece(structure_->opaque_bytes));
}
{
const unsigned char kData[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(ToStringPiece(kData), ToStringPiece(structure_->opaque_bytes));
}
}
TEST_F(Http2PingFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2GoAwayFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2GoAwayFields> {};
TEST_F(Http2GoAwayFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x00, 0x00, 0x00, 0x00, // Last Stream ID: 0
0x00, 0x00, 0x00, 0x00, // Error: INTERNAL_ERROR
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(0, structure_->last_stream_id);
EXPECT_TRUE(structure_->IsSupportedErrorCode());
EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, structure_->error_code);
}
{
const unsigned char kData[] = {
0x00, 0x00, 0x00, 0x01, // Last Stream ID: 1
0x00, 0x00, 0x00, 0x0d, // Error: HTTP_1_1_REQUIRED
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(1, structure_->last_stream_id);
EXPECT_TRUE(structure_->IsSupportedErrorCode());
EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED, structure_->error_code);
}
{
const unsigned char kData[] = {
0xff, 0xff, 0xff, 0xff, // Last Stream ID: max uint31 and R-bit
0xff, 0xff, 0xff, 0xff, // Error: max uint32 (Unknown error code)
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(StreamIdMask(), structure_->last_stream_id); // No high-bit.
EXPECT_FALSE(structure_->IsSupportedErrorCode());
EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_->error_code);
}
}
TEST_F(Http2GoAwayFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2WindowUpdateFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<
Http2WindowUpdateFields> {};
TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x00, 0x01, 0x00, 0x00, // Window Size Increment: 2 ^ 16
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(1 << 16, structure_->window_size_increment);
}
{
// Increment must be non-zero, but we need to be able to decode the invalid
// zero to detect it.
const unsigned char kData[] = {
0x00, 0x00, 0x00, 0x00, // Window Size Increment: 0
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(0, structure_->window_size_increment);
}
{
// Increment has R-bit (reserved for future use) set, which
// should be cleared by the decoder.
const unsigned char kData[] = {
0xff, 0xff, 0xff, 0xff, // Window Size Increment: max uint31 and R-bit
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(StreamIdMask(), structure_->window_size_increment);
}
}
TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
//------------------------------------------------------------------------------
class Http2AltSvcFieldsDecoderTest
: public Http2StructureDecoderRemainingPayloadTest<Http2AltSvcFields> {};
TEST_F(Http2AltSvcFieldsDecoderTest, DecodesLiteral) {
{
const unsigned char kData[] = {
0x00, 0x00, // Origin Length: 0
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(0, structure_->origin_length);
}
{
const unsigned char kData[] = {
0x00, 0x14, // Origin Length: 20
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(20, structure_->origin_length);
}
{
const unsigned char kData[] = {
0xff, 0xff, // Origin Length: 20
};
EXPECT_TRUE(DecodeLeadingStructure(kData));
EXPECT_EQ(65535, structure_->origin_length);
}
}
TEST_F(Http2AltSvcFieldsDecoderTest, DecodesRandomized) {
uint64_t count = absl::GetFlag(FLAGS_random_decode_count);
EXPECT_LT(count, 1000 * 1000) << "That should be plenty!";
EXPECT_TRUE(TestDecodingRandomizedStructures(count));
}
} // namespace
} // namespace test
} // namespace http2