blob: 95d5913896b50e3e6b10a3a73253d646988a8a4d [file] [log] [blame]
// Copyright (c) 2018 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 "quic/core/http/http_decoder.h"
#include <memory>
#include <utility>
#include "absl/base/macros.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "quic/core/http/http_encoder.h"
#include "quic/core/http/http_frames.h"
#include "quic/core/quic_data_writer.h"
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/quic_test_utils.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
namespace quic {
namespace test {
class HttpDecoderPeer {
public:
static uint64_t current_frame_type(HttpDecoder* decoder) {
return decoder->current_frame_type_;
}
};
namespace {
class MockHttpDecoderVisitor : public HttpDecoder::Visitor {
public:
~MockHttpDecoderVisitor() override = default;
// Called if an error is detected.
MOCK_METHOD(void, OnError, (HttpDecoder*), (override));
MOCK_METHOD(bool,
OnMaxPushIdFrame,
(const MaxPushIdFrame& frame),
(override));
MOCK_METHOD(bool, OnGoAwayFrame, (const GoAwayFrame& frame), (override));
MOCK_METHOD(bool,
OnSettingsFrameStart,
(QuicByteCount header_length),
(override));
MOCK_METHOD(bool, OnSettingsFrame, (const SettingsFrame& frame), (override));
MOCK_METHOD(bool,
OnDataFrameStart,
(QuicByteCount header_length, QuicByteCount payload_length),
(override));
MOCK_METHOD(bool,
OnDataFramePayload,
(absl::string_view payload),
(override));
MOCK_METHOD(bool, OnDataFrameEnd, (), (override));
MOCK_METHOD(bool,
OnHeadersFrameStart,
(QuicByteCount header_length, QuicByteCount payload_length),
(override));
MOCK_METHOD(bool,
OnHeadersFramePayload,
(absl::string_view payload),
(override));
MOCK_METHOD(bool, OnHeadersFrameEnd, (), (override));
MOCK_METHOD(bool,
OnPriorityUpdateFrameStart,
(QuicByteCount header_length),
(override));
MOCK_METHOD(bool,
OnPriorityUpdateFrame,
(const PriorityUpdateFrame& frame),
(override));
MOCK_METHOD(bool,
OnAcceptChFrameStart,
(QuicByteCount header_length),
(override));
MOCK_METHOD(bool, OnAcceptChFrame, (const AcceptChFrame& frame), (override));
MOCK_METHOD(void,
OnWebTransportStreamFrameType,
(QuicByteCount header_length, WebTransportSessionId session_id),
(override));
MOCK_METHOD(bool,
OnUnknownFrameStart,
(uint64_t frame_type,
QuicByteCount header_length,
QuicByteCount payload_length),
(override));
MOCK_METHOD(bool,
OnUnknownFramePayload,
(absl::string_view payload),
(override));
MOCK_METHOD(bool, OnUnknownFrameEnd, (), (override));
};
class HttpDecoderTest : public QuicTest {
public:
HttpDecoderTest() : decoder_(&visitor_) {
ON_CALL(visitor_, OnMaxPushIdFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnGoAwayFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnSettingsFrameStart(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnSettingsFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnDataFrameStart(_, _)).WillByDefault(Return(true));
ON_CALL(visitor_, OnDataFramePayload(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnDataFrameEnd()).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFrameStart(_, _)).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFramePayload(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFrameEnd()).WillByDefault(Return(true));
ON_CALL(visitor_, OnPriorityUpdateFrameStart(_))
.WillByDefault(Return(true));
ON_CALL(visitor_, OnPriorityUpdateFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnAcceptChFrameStart(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnAcceptChFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnUnknownFrameStart(_, _, _)).WillByDefault(Return(true));
ON_CALL(visitor_, OnUnknownFramePayload(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnUnknownFrameEnd()).WillByDefault(Return(true));
}
~HttpDecoderTest() override = default;
uint64_t current_frame_type() {
return HttpDecoderPeer::current_frame_type(&decoder_);
}
// Process |input| in a single call to HttpDecoder::ProcessInput().
QuicByteCount ProcessInput(absl::string_view input) {
return decoder_.ProcessInput(input.data(), input.size());
}
// Feed |input| to |decoder_| one character at a time,
// verifying that each character gets processed.
void ProcessInputCharByChar(absl::string_view input) {
for (char c : input) {
EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
}
}
// Append garbage to |input|, then process it in a single call to
// HttpDecoder::ProcessInput(). Verify that garbage is not read.
QuicByteCount ProcessInputWithGarbageAppended(absl::string_view input) {
std::string input_with_garbage_appended = absl::StrCat(input, "blahblah");
QuicByteCount processed_bytes = ProcessInput(input_with_garbage_appended);
// Guaranteed by HttpDecoder::ProcessInput() contract.
QUICHE_DCHECK_LE(processed_bytes, input_with_garbage_appended.size());
// Caller should set up visitor to pause decoding
// before HttpDecoder would read garbage.
EXPECT_LE(processed_bytes, input.size());
return processed_bytes;
}
testing::StrictMock<MockHttpDecoderVisitor> visitor_;
HttpDecoder decoder_;
};
TEST_F(HttpDecoderTest, InitialState) {
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, UnknownFrame) {
std::unique_ptr<char[]> input;
const QuicByteCount payload_lengths[] = {0, 14, 100};
const uint64_t frame_types[] = {
0x21, 0x40, 0x5f, 0x7e, 0x9d, // some reserved frame types
0x6f, 0x14 // some unknown, not reserved frame types
};
for (auto payload_length : payload_lengths) {
std::string data(payload_length, 'a');
for (auto frame_type : frame_types) {
const QuicByteCount total_length =
QuicDataWriter::GetVarInt62Len(frame_type) +
QuicDataWriter::GetVarInt62Len(payload_length) + payload_length;
input = std::make_unique<char[]>(total_length);
QuicDataWriter writer(total_length, input.get());
writer.WriteVarInt62(frame_type);
writer.WriteVarInt62(payload_length);
const QuicByteCount header_length = writer.length();
if (payload_length > 0) {
writer.WriteStringPiece(data);
}
EXPECT_CALL(visitor_, OnUnknownFrameStart(frame_type, header_length,
payload_length));
if (payload_length > 0) {
EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq(data)));
}
EXPECT_CALL(visitor_, OnUnknownFrameEnd());
EXPECT_EQ(total_length, decoder_.ProcessInput(input.get(), total_length));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
ASSERT_EQ("", decoder_.error_detail());
EXPECT_EQ(frame_type, current_frame_type());
}
}
}
TEST_F(HttpDecoderTest, CancelPush) {
InSequence s;
std::string input = absl::HexStringToBytes(
"03" // type (CANCEL_PUSH)
"01" // length
"01"); // Push Id
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(1u, ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ("CANCEL_PUSH frame received.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, PushPromiseFrame) {
InSequence s;
std::string input =
absl::StrCat(absl::HexStringToBytes("05" // type (PUSH PROMISE)
"08" // length
"1f"), // push id 31
"Headers"); // headers
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(1u, ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ("PUSH_PROMISE frame received.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, MaxPushId) {
InSequence s;
std::string input = absl::HexStringToBytes(
"0D" // type (MAX_PUSH_ID)
"01" // length
"01"); // Push Id
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})))
.WillOnce(Return(false));
EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})));
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, SettingsFrame) {
InSequence s;
std::string input = absl::HexStringToBytes(
"04" // type (SETTINGS)
"07" // length
"01" // identifier (SETTINGS_QPACK_MAX_TABLE_CAPACITY)
"02" // content
"06" // identifier (SETTINGS_MAX_HEADER_LIST_SIZE)
"05" // content
"4100" // identifier, encoded on 2 bytes (0x40), value is 256 (0x100)
"04"); // content
SettingsFrame frame;
frame.values[1] = 2;
frame.values[6] = 5;
frame.values[256] = 4;
// Visitor pauses processing.
absl::string_view remaining_input(input);
EXPECT_CALL(visitor_, OnSettingsFrameStart(2)).WillOnce(Return(false));
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(2u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnSettingsFrame(frame)).WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnSettingsFrameStart(2));
EXPECT_CALL(visitor_, OnSettingsFrame(frame));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnSettingsFrameStart(2));
EXPECT_CALL(visitor_, OnSettingsFrame(frame));
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, CorruptSettingsFrame) {
const char* const kPayload =
"\x42\x11" // two-byte id
"\x80\x22\x33\x44" // four-byte value
"\x58\x39" // two-byte id
"\xf0\x22\x33\x44\x55\x66\x77\x88"; // eight-byte value
struct {
size_t payload_length;
const char* const error_message;
} kTestData[] = {
{1, "Unable to read setting identifier."},
{5, "Unable to read setting value."},
{7, "Unable to read setting identifier."},
{12, "Unable to read setting value."},
};
for (const auto& test_data : kTestData) {
std::string input;
input.push_back(4u); // type SETTINGS
input.push_back(test_data.payload_length);
const size_t header_length = input.size();
input.append(kPayload, test_data.payload_length);
HttpDecoder decoder(&visitor_);
EXPECT_CALL(visitor_, OnSettingsFrameStart(header_length));
EXPECT_CALL(visitor_, OnError(&decoder));
QuicByteCount processed_bytes =
decoder.ProcessInput(input.data(), input.size());
EXPECT_EQ(input.size(), processed_bytes);
EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ(test_data.error_message, decoder.error_detail());
}
}
TEST_F(HttpDecoderTest, DuplicateSettingsIdentifier) {
std::string input = absl::HexStringToBytes(
"04" // type (SETTINGS)
"04" // length
"01" // identifier
"01" // content
"01" // identifier
"02"); // content
EXPECT_CALL(visitor_, OnSettingsFrameStart(2));
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(),
IsError(QUIC_HTTP_DUPLICATE_SETTING_IDENTIFIER));
EXPECT_EQ("Duplicate setting identifier.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, DataFrame) {
InSequence s;
std::string input = absl::StrCat(absl::HexStringToBytes("00" // type (DATA)
"05"), // length
"Data!"); // data
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 5)).WillOnce(Return(false));
absl::string_view remaining_input(input);
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(2u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("Data!")))
.WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_CALL(visitor_, OnDataFrameEnd()).WillOnce(Return(false));
EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 5));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("Data!")));
EXPECT_CALL(visitor_, OnDataFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 5));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("D")));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("a")));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("t")));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("a")));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("!")));
EXPECT_CALL(visitor_, OnDataFrameEnd());
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) {
InSequence s;
// A large input that will occupy more than 1 byte in the length field.
std::string input(2048, 'x');
QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
input.length(), SimpleBufferAllocator::Get());
// Partially send only 1 byte of the header to process.
EXPECT_EQ(1u, decoder_.ProcessInput(header.data(), 1));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Send the rest of the header.
EXPECT_CALL(visitor_, OnDataFrameStart(3, input.length()));
EXPECT_EQ(header.size() - 1,
decoder_.ProcessInput(header.data() + 1, header.size() - 1));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Send data.
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view(input)));
EXPECT_CALL(visitor_, OnDataFrameEnd());
EXPECT_EQ(2048u, decoder_.ProcessInput(input.data(), 2048));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, PartialDeliveryOfLargeFrameType) {
// Use a reserved type that takes four bytes as a varint.
const uint64_t frame_type = 0x1f * 0x222 + 0x21;
const QuicByteCount payload_length = 0;
const QuicByteCount header_length =
QuicDataWriter::GetVarInt62Len(frame_type) +
QuicDataWriter::GetVarInt62Len(payload_length);
auto input = std::make_unique<char[]>(header_length);
QuicDataWriter writer(header_length, input.get());
writer.WriteVarInt62(frame_type);
writer.WriteVarInt62(payload_length);
EXPECT_CALL(visitor_,
OnUnknownFrameStart(frame_type, header_length, payload_length));
EXPECT_CALL(visitor_, OnUnknownFrameEnd());
auto raw_input = input.get();
for (uint64_t i = 0; i < header_length; ++i) {
char c = raw_input[i];
EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
}
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
EXPECT_EQ(frame_type, current_frame_type());
}
TEST_F(HttpDecoderTest, GoAway) {
InSequence s;
std::string input = absl::HexStringToBytes(
"07" // type (GOAWAY)
"01" // length
"01"); // ID
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})))
.WillOnce(Return(false));
EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})));
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, HeadersFrame) {
InSequence s;
std::string input =
absl::StrCat(absl::HexStringToBytes("01" // type (HEADERS)
"07"), // length
"Headers"); // headers
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 7)).WillOnce(Return(false));
absl::string_view remaining_input(input);
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(2u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("Headers")))
.WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false));
EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 7));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("Headers")));
EXPECT_CALL(visitor_, OnHeadersFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 7));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("H")));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("e")));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("a")));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("d")));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("e")));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("r")));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("s")));
EXPECT_CALL(visitor_, OnHeadersFrameEnd());
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, EmptyDataFrame) {
InSequence s;
std::string input = absl::HexStringToBytes(
"00" // type (DATA)
"00"); // length
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 0)).WillOnce(Return(false));
EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
EXPECT_CALL(visitor_, OnDataFrameEnd()).WillOnce(Return(false));
EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 0));
EXPECT_CALL(visitor_, OnDataFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 0));
EXPECT_CALL(visitor_, OnDataFrameEnd());
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, EmptyHeadersFrame) {
InSequence s;
std::string input = absl::HexStringToBytes(
"01" // type (HEADERS)
"00"); // length
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 0)).WillOnce(Return(false));
EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false));
EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 0));
EXPECT_CALL(visitor_, OnHeadersFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 0));
EXPECT_CALL(visitor_, OnHeadersFrameEnd());
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, GoawayWithOverlyLargePayload) {
std::string input = absl::HexStringToBytes(
"07" // type (GOAWAY)
"10"); // length exceeding the maximum possible length for GOAWAY frame
// Process all data at once.
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(2u, ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_TOO_LARGE));
EXPECT_EQ("Frame is too large.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, MaxPushIdWithOverlyLargePayload) {
std::string input = absl::HexStringToBytes(
"0d" // type (MAX_PUSH_ID)
"10"); // length exceeding the maximum possible length for MAX_PUSH_ID
// frame
// Process all data at once.
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(2u, ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_TOO_LARGE));
EXPECT_EQ("Frame is too large.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, FrameWithOverlyLargePayload) {
// Regression test for b/193919867: Ensure that reading frames with incredibly
// large payload lengths does not lead to allocating unbounded memory.
constexpr size_t max_input_length =
/*max frame type varint length*/ sizeof(uint64_t) +
/*max frame length varint length*/ sizeof(uint64_t) +
/*one byte of payload*/ sizeof(uint8_t);
char input[max_input_length];
for (uint64_t frame_type = 0; frame_type < 1025; frame_type++) {
::testing::NiceMock<MockHttpDecoderVisitor> visitor;
HttpDecoder decoder(&visitor);
QuicDataWriter writer(max_input_length, input);
ASSERT_TRUE(writer.WriteVarInt62(frame_type)); // frame type.
ASSERT_TRUE(writer.WriteVarInt62(kVarInt62MaxValue)); // frame length.
ASSERT_TRUE(writer.WriteUInt8(0x00)); // one byte of payload.
EXPECT_NE(decoder.ProcessInput(input, writer.length()), 0u) << frame_type;
}
}
TEST_F(HttpDecoderTest, MalformedSettingsFrame) {
char input[30];
QuicDataWriter writer(30, input);
// Write type SETTINGS.
writer.WriteUInt8(0x04);
// Write length.
writer.WriteVarInt62(2048 * 1024);
writer.WriteStringPiece("Malformed payload");
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(5u, decoder_.ProcessInput(input, ABSL_ARRAYSIZE(input)));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_TOO_LARGE));
EXPECT_EQ("Frame is too large.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, Http2Frame) {
std::string input = absl::HexStringToBytes(
"06" // PING in HTTP/2 but not supported in HTTP/3.
"05" // length
"15"); // random payload
// Process the full frame.
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(1u, ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_RECEIVE_SPDY_FRAME));
EXPECT_EQ("HTTP/2 frame received in a HTTP/3 connection: 6",
decoder_.error_detail());
}
TEST_F(HttpDecoderTest, HeadersPausedThenData) {
InSequence s;
std::string input =
absl::StrCat(absl::HexStringToBytes("01" // type (HEADERS)
"07"), // length
"Headers", // headers
absl::HexStringToBytes("00" // type (DATA)
"05"), // length
"Data!"); // data
// Visitor pauses processing, maybe because header decompression is blocked.
EXPECT_CALL(visitor_, OnHeadersFrameStart(2, 7));
EXPECT_CALL(visitor_, OnHeadersFramePayload(absl::string_view("Headers")));
EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false));
absl::string_view remaining_input(input);
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(9u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
// Process DATA frame.
EXPECT_CALL(visitor_, OnDataFrameStart(2, 5));
EXPECT_CALL(visitor_, OnDataFramePayload(absl::string_view("Data!")));
EXPECT_CALL(visitor_, OnDataFrameEnd());
processed_bytes = ProcessInput(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, CorruptFrame) {
InSequence s;
struct {
const char* const input;
const char* const error_message;
} kTestData[] = {{"\x0D" // type (MAX_PUSH_ID)
"\x01" // length
"\x40", // first byte of two-byte varint push id
"Unable to read MAX_PUSH_ID push_id."},
{"\x0D" // type (MAX_PUSH_ID)
"\x04" // length
"\x05" // valid push id
"foo", // superfluous data
"Superfluous data in MAX_PUSH_ID frame."},
{"\x07" // type (GOAWAY)
"\x01" // length
"\x40", // first byte of two-byte varint stream id
"Unable to read GOAWAY ID."},
{"\x07" // type (GOAWAY)
"\x04" // length
"\x05" // valid stream id
"foo", // superfluous data
"Superfluous data in GOAWAY frame."},
{"\x40\x89" // type (ACCEPT_CH)
"\x01" // length
"\x40", // first byte of two-byte varint origin length
"Unable to read ACCEPT_CH origin."},
{"\x40\x89" // type (ACCEPT_CH)
"\x01" // length
"\x05", // valid origin length but no origin string
"Unable to read ACCEPT_CH origin."},
{"\x40\x89" // type (ACCEPT_CH)
"\x04" // length
"\x05" // valid origin length
"foo", // payload ends before origin ends
"Unable to read ACCEPT_CH origin."},
{"\x40\x89" // type (ACCEPT_CH)
"\x04" // length
"\x03" // valid origin length
"foo", // payload ends at end of origin: no value
"Unable to read ACCEPT_CH value."},
{"\x40\x89" // type (ACCEPT_CH)
"\x05" // length
"\x03" // valid origin length
"foo" // payload ends at end of origin: no value
"\x40", // first byte of two-byte varint value length
"Unable to read ACCEPT_CH value."},
{"\x40\x89" // type (ACCEPT_CH)
"\x08" // length
"\x03" // valid origin length
"foo" // origin
"\x05" // valid value length
"bar", // payload ends before value ends
"Unable to read ACCEPT_CH value."}};
for (const auto& test_data : kTestData) {
{
HttpDecoder decoder(&visitor_);
EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
EXPECT_CALL(visitor_, OnError(&decoder));
absl::string_view input(test_data.input);
decoder.ProcessInput(input.data(), input.size());
EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ(test_data.error_message, decoder.error_detail());
}
{
HttpDecoder decoder(&visitor_);
EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
EXPECT_CALL(visitor_, OnError(&decoder));
absl::string_view input(test_data.input);
for (auto c : input) {
decoder.ProcessInput(&c, 1);
}
EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ(test_data.error_message, decoder.error_detail());
}
}
}
TEST_F(HttpDecoderTest, EmptySettingsFrame) {
std::string input = absl::HexStringToBytes(
"04" // type (SETTINGS)
"00"); // frame length
EXPECT_CALL(visitor_, OnSettingsFrameStart(2));
SettingsFrame empty_frame;
EXPECT_CALL(visitor_, OnSettingsFrame(empty_frame));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, EmptyGoAwayFrame) {
std::string input = absl::HexStringToBytes(
"07" // type (GOAWAY)
"00"); // frame length
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ("Unable to read GOAWAY ID.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, EmptyMaxPushIdFrame) {
std::string input = absl::HexStringToBytes(
"0d" // type (MAX_PUSH_ID)
"00"); // frame length
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ("Unable to read MAX_PUSH_ID push_id.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, LargeStreamIdInGoAway) {
GoAwayFrame frame;
frame.id = 1ull << 60;
std::unique_ptr<char[]> buffer;
uint64_t length = HttpEncoder::SerializeGoAwayFrame(frame, &buffer);
EXPECT_CALL(visitor_, OnGoAwayFrame(frame));
EXPECT_GT(length, 0u);
EXPECT_EQ(length, decoder_.ProcessInput(buffer.get(), length));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
// Old PRIORITY_UPDATE frame is parsed as unknown frame.
TEST_F(HttpDecoderTest, ObsoletePriorityUpdateFrame) {
const QuicByteCount header_length = 2;
const QuicByteCount payload_length = 3;
InSequence s;
std::string input = absl::HexStringToBytes(
"0f" // type (obsolete PRIORITY_UPDATE)
"03" // length
"666f6f"); // payload "foo"
// Process frame as a whole.
EXPECT_CALL(visitor_,
OnUnknownFrameStart(0x0f, header_length, payload_length));
EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("foo")));
EXPECT_CALL(visitor_, OnUnknownFrameEnd()).WillOnce(Return(false));
EXPECT_EQ(header_length + payload_length,
ProcessInputWithGarbageAppended(input));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process frame byte by byte.
EXPECT_CALL(visitor_,
OnUnknownFrameStart(0x0f, header_length, payload_length));
EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("f")));
EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("o")));
EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("o")));
EXPECT_CALL(visitor_, OnUnknownFrameEnd());
ProcessInputCharByChar(input);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, PriorityUpdateFrame) {
InSequence s;
std::string input1 = absl::HexStringToBytes(
"800f0700" // type (PRIORITY_UPDATE)
"01" // length
"03"); // prioritized element id
PriorityUpdateFrame priority_update1;
priority_update1.prioritized_element_type = REQUEST_STREAM;
priority_update1.prioritized_element_id = 0x03;
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(5)).WillOnce(Return(false));
absl::string_view remaining_input(input1);
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(5u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update1))
.WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(5));
EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update1));
EXPECT_EQ(input1.size(), ProcessInput(input1));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(5));
EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update1));
ProcessInputCharByChar(input1);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
std::string input2 = absl::HexStringToBytes(
"800f0700" // type (PRIORITY_UPDATE)
"04" // length
"05" // prioritized element id
"666f6f"); // priority field value: "foo"
PriorityUpdateFrame priority_update2;
priority_update2.prioritized_element_type = REQUEST_STREAM;
priority_update2.prioritized_element_id = 0x05;
priority_update2.priority_field_value = "foo";
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(5)).WillOnce(Return(false));
remaining_input = input2;
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(5u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update2))
.WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(5));
EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update2));
EXPECT_EQ(input2.size(), ProcessInput(input2));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(5));
EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update2));
ProcessInputCharByChar(input2);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, CorruptPriorityUpdateFrame) {
std::string payload =
absl::HexStringToBytes("4005"); // prioritized element id
struct {
size_t payload_length;
const char* const error_message;
} kTestData[] = {
{0, "Unable to read prioritized element id."},
{1, "Unable to read prioritized element id."},
};
for (const auto& test_data : kTestData) {
std::string input =
absl::HexStringToBytes("800f0700"); // type PRIORITY_UPDATE
input.push_back(test_data.payload_length);
size_t header_length = input.size();
input.append(payload.data(), test_data.payload_length);
HttpDecoder decoder(&visitor_);
EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(header_length));
EXPECT_CALL(visitor_, OnError(&decoder));
QuicByteCount processed_bytes =
decoder.ProcessInput(input.data(), input.size());
EXPECT_EQ(input.size(), processed_bytes);
EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
EXPECT_EQ(test_data.error_message, decoder.error_detail());
}
}
TEST_F(HttpDecoderTest, AcceptChFrame) {
InSequence s;
std::string input1 = absl::HexStringToBytes(
"4089" // type (ACCEPT_CH)
"00"); // length
AcceptChFrame accept_ch1;
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)).WillOnce(Return(false));
absl::string_view remaining_input(input1);
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(3u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1)).WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1));
EXPECT_EQ(input1.size(), ProcessInput(input1));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1));
ProcessInputCharByChar(input1);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
std::string input2 = absl::HexStringToBytes(
"4089" // type (ACCEPT_CH)
"08" // length
"03" // length of origin
"666f6f" // origin "foo"
"03" // length of value
"626172"); // value "bar"
AcceptChFrame accept_ch2;
accept_ch2.entries.push_back({"foo", "bar"});
// Visitor pauses processing.
EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)).WillOnce(Return(false));
remaining_input = input2;
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(3u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2)).WillOnce(Return(false));
processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
EXPECT_EQ(remaining_input.size(), processed_bytes);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2));
EXPECT_EQ(input2.size(), ProcessInput(input2));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2));
ProcessInputCharByChar(input2);
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, WebTransportStreamDisabled) {
InSequence s;
// Unknown frame of type 0x41 and length 0x104.
std::string input = absl::HexStringToBytes("40414104");
EXPECT_CALL(visitor_, OnUnknownFrameStart(0x41, input.size(), 0x104));
EXPECT_EQ(ProcessInput(input), input.size());
}
TEST(HttpDecoderTestNoFixture, WebTransportStream) {
HttpDecoder::Options options;
options.allow_web_transport_stream = true;
testing::StrictMock<MockHttpDecoderVisitor> visitor;
HttpDecoder decoder(&visitor, options);
// WebTransport stream for session ID 0x104, with four bytes of extra data.
std::string input = absl::HexStringToBytes("40414104ffffffff");
EXPECT_CALL(visitor, OnWebTransportStreamFrameType(4, 0x104));
QuicByteCount bytes = decoder.ProcessInput(input.data(), input.size());
EXPECT_EQ(bytes, 4u);
}
TEST(HttpDecoderTestNoFixture, WebTransportStreamError) {
HttpDecoder::Options options;
options.allow_web_transport_stream = true;
testing::StrictMock<MockHttpDecoderVisitor> visitor;
HttpDecoder decoder(&visitor, options);
std::string input = absl::HexStringToBytes("404100");
EXPECT_CALL(visitor, OnWebTransportStreamFrameType(_, _));
decoder.ProcessInput(input.data(), input.size());
EXPECT_CALL(visitor, OnError(_));
EXPECT_QUIC_BUG(decoder.ProcessInput(input.data(), input.size()),
"HttpDecoder called after an indefinite-length frame");
}
TEST_F(HttpDecoderTest, DecodeSettings) {
std::string input = absl::HexStringToBytes(
"04" // type (SETTINGS)
"07" // length
"01" // identifier (SETTINGS_QPACK_MAX_TABLE_CAPACITY)
"02" // content
"06" // identifier (SETTINGS_MAX_HEADER_LIST_SIZE)
"05" // content
"4100" // identifier, encoded on 2 bytes (0x40), value is 256 (0x100)
"04"); // content
SettingsFrame frame;
frame.values[1] = 2;
frame.values[6] = 5;
frame.values[256] = 4;
SettingsFrame out;
EXPECT_TRUE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out));
EXPECT_EQ(frame, out);
// non-settings frame.
input = absl::HexStringToBytes(
"0D" // type (MAX_PUSH_ID)
"01" // length
"01"); // Push Id
EXPECT_FALSE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out));
// Corrupt SETTINGS.
input = absl::HexStringToBytes(
"04" // type (SETTINGS)
"01" // length
"42"); // First byte of setting identifier, indicating a 2-byte varint62.
EXPECT_FALSE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out));
}
} // namespace
} // namespace test
} // namespace quic