| // 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 "net/third_party/quiche/src/quic/core/http/http_decoder.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" | |
| #include "net/third_party/quiche/src/quic/core/http/http_frames.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_versions.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" | |
| #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" | |
| using ::testing::_; | |
| 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_; | |
| } | |
| }; | |
| class MockVisitor : public HttpDecoder::Visitor { | |
| public: | |
| ~MockVisitor() override = default; | |
| // Called if an error is detected. | |
| MOCK_METHOD1(OnError, void(HttpDecoder* decoder)); | |
| MOCK_METHOD1(OnPriorityFrameStart, bool(QuicByteCount header_length)); | |
| MOCK_METHOD1(OnPriorityFrame, bool(const PriorityFrame& frame)); | |
| MOCK_METHOD1(OnCancelPushFrame, bool(const CancelPushFrame& frame)); | |
| MOCK_METHOD1(OnMaxPushIdFrame, bool(const MaxPushIdFrame& frame)); | |
| MOCK_METHOD1(OnGoAwayFrame, bool(const GoAwayFrame& frame)); | |
| MOCK_METHOD1(OnSettingsFrameStart, bool(QuicByteCount header_length)); | |
| MOCK_METHOD1(OnSettingsFrame, bool(const SettingsFrame& frame)); | |
| MOCK_METHOD1(OnDuplicatePushFrame, bool(const DuplicatePushFrame& frame)); | |
| MOCK_METHOD1(OnDataFrameStart, bool(QuicByteCount header_length)); | |
| MOCK_METHOD1(OnDataFramePayload, bool(QuicStringPiece payload)); | |
| MOCK_METHOD0(OnDataFrameEnd, bool()); | |
| MOCK_METHOD1(OnHeadersFrameStart, bool(QuicByteCount header_length)); | |
| MOCK_METHOD1(OnHeadersFramePayload, bool(QuicStringPiece payload)); | |
| MOCK_METHOD0(OnHeadersFrameEnd, bool()); | |
| MOCK_METHOD1(OnPushPromiseFrameStart, bool(QuicByteCount header_length)); | |
| MOCK_METHOD2(OnPushPromiseFramePushId, | |
| bool(PushId push_id, QuicByteCount push_id_length)); | |
| MOCK_METHOD1(OnPushPromiseFramePayload, bool(QuicStringPiece payload)); | |
| MOCK_METHOD0(OnPushPromiseFrameEnd, bool()); | |
| MOCK_METHOD2(OnUnknownFrameStart, bool(uint64_t, QuicByteCount)); | |
| MOCK_METHOD1(OnUnknownFramePayload, bool(QuicStringPiece)); | |
| MOCK_METHOD0(OnUnknownFrameEnd, bool()); | |
| }; | |
| class HttpDecoderTest : public QuicTest { | |
| public: | |
| HttpDecoderTest() : decoder_(&visitor_) { | |
| ON_CALL(visitor_, OnPriorityFrameStart(_)).WillByDefault(Return(true)); | |
| ON_CALL(visitor_, OnPriorityFrame(_)).WillByDefault(Return(true)); | |
| ON_CALL(visitor_, OnCancelPushFrame(_)).WillByDefault(Return(true)); | |
| 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_, OnDuplicatePushFrame(_)).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_, OnPushPromiseFrameStart(_)).WillByDefault(Return(true)); | |
| ON_CALL(visitor_, OnPushPromiseFramePushId(_, _)) | |
| .WillByDefault(Return(true)); | |
| ON_CALL(visitor_, OnPushPromiseFramePayload(_)).WillByDefault(Return(true)); | |
| ON_CALL(visitor_, OnPushPromiseFrameEnd()).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(QuicStringPiece 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(QuicStringPiece 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(QuicStringPiece input) { | |
| std::string input_with_garbage_appended = QuicStrCat(input, "blahblah"); | |
| QuicByteCount processed_bytes = ProcessInput(input_with_garbage_appended); | |
| // Guaranteed by HttpDecoder::ProcessInput() contract. | |
| 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<MockVisitor> 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 | |
| 0x06, 0x0f, 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)); | |
| 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 = QuicTextUtils::HexDecode( | |
| "03" // type (CANCEL_PUSH) | |
| "01" // length | |
| "01"); // Push Id | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({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_, OnCancelPushFrame(CancelPushFrame({1}))); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the frame incrementally. | |
| EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1}))); | |
| ProcessInputCharByChar(input); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, PushPromiseFrame) { | |
| InSequence s; | |
| std::string input = | |
| QuicStrCat(QuicTextUtils::HexDecode("05" // type (PUSH PROMISE) | |
| "0f" // length | |
| "C000000000000101"), // push id 257 | |
| "Headers"); // headers | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)).WillOnce(Return(false)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8)) | |
| .WillOnce(Return(false)); | |
| QuicStringPiece remaining_input(input); | |
| QuicByteCount processed_bytes = | |
| ProcessInputWithGarbageAppended(remaining_input); | |
| EXPECT_EQ(2u, processed_bytes); | |
| remaining_input = remaining_input.substr(processed_bytes); | |
| processed_bytes = ProcessInputWithGarbageAppended(remaining_input); | |
| EXPECT_EQ(8u, processed_bytes); | |
| remaining_input = remaining_input.substr(processed_bytes); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))) | |
| .WillOnce(Return(false)); | |
| processed_bytes = ProcessInputWithGarbageAppended(remaining_input); | |
| EXPECT_EQ(remaining_input.size(), processed_bytes); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()).WillOnce(Return(false)); | |
| EXPECT_EQ(0u, ProcessInputWithGarbageAppended("")); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the full frame. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the frame incrementally. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("H"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("a"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("d"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("r"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("s"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); | |
| ProcessInputCharByChar(input); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process push id incrementally and append headers with last byte of push id. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); | |
| ProcessInputCharByChar(input.substr(0, 9)); | |
| EXPECT_EQ(8u, ProcessInput(input.substr(9))); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, CorruptPushPromiseFrame) { | |
| InSequence s; | |
| std::string input = QuicTextUtils::HexDecode( | |
| "05" // type (PUSH_PROMISE) | |
| "01" // length | |
| "40"); // first byte of two-byte varint push id | |
| { | |
| HttpDecoder decoder(&visitor_); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnError(&decoder)); | |
| decoder.ProcessInput(input.data(), input.size()); | |
| EXPECT_THAT(decoder.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("PUSH_PROMISE frame malformed.", decoder.error_detail()); | |
| } | |
| { | |
| HttpDecoder decoder(&visitor_); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnError(&decoder)); | |
| for (auto c : input) { | |
| decoder.ProcessInput(&c, 1); | |
| } | |
| EXPECT_THAT(decoder.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("PUSH_PROMISE frame malformed.", decoder.error_detail()); | |
| } | |
| } | |
| TEST_F(HttpDecoderTest, MaxPushId) { | |
| InSequence s; | |
| std::string input = QuicTextUtils::HexDecode( | |
| "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, DuplicatePush) { | |
| InSequence s; | |
| std::string input = QuicTextUtils::HexDecode( | |
| "0E" // type (DUPLICATE_PUSH) | |
| "01" // length | |
| "01"); // Push Id | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({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_, OnDuplicatePushFrame(DuplicatePushFrame({1}))); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the frame incrementally. | |
| EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1}))); | |
| ProcessInputCharByChar(input); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, PriorityFrame) { | |
| InSequence s; | |
| std::string input = QuicTextUtils::HexDecode( | |
| "02" // type (PRIORITY) | |
| "04" // length | |
| "08" // request stream, request stream, exclusive | |
| "03" // prioritized_element_id | |
| "04" // element_dependency_id | |
| "FF"); // weight | |
| PriorityFrame frame; | |
| frame.prioritized_type = REQUEST_STREAM; | |
| frame.dependency_type = REQUEST_STREAM; | |
| frame.exclusive = true; | |
| frame.prioritized_element_id = 0x03; | |
| frame.element_dependency_id = 0x04; | |
| frame.weight = 0xFF; | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnPriorityFrameStart(2)).WillOnce(Return(false)); | |
| QuicStringPiece remaining_input(input); | |
| QuicByteCount processed_bytes = | |
| ProcessInputWithGarbageAppended(remaining_input); | |
| EXPECT_EQ(2u, processed_bytes); | |
| remaining_input = remaining_input.substr(processed_bytes); | |
| EXPECT_CALL(visitor_, OnPriorityFrame(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_, OnPriorityFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPriorityFrame(frame)); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the frame incrementally. | |
| EXPECT_CALL(visitor_, OnPriorityFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPriorityFrame(frame)); | |
| ProcessInputCharByChar(input); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| std::string input2 = QuicTextUtils::HexDecode( | |
| "02" // type (PRIORITY) | |
| "02" // length | |
| "f8" // root of tree, root of tree, exclusive | |
| "FF"); // weight | |
| PriorityFrame frame2; | |
| frame2.prioritized_type = ROOT_OF_TREE; | |
| frame2.dependency_type = ROOT_OF_TREE; | |
| frame2.exclusive = true; | |
| frame2.weight = 0xFF; | |
| EXPECT_CALL(visitor_, OnPriorityFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPriorityFrame(frame2)); | |
| EXPECT_EQ(input2.size(), ProcessInput(input2)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| } | |
| // Regression test for https://crbug.com/981291 and https://crbug.com/981646. | |
| TEST_F(HttpDecoderTest, CorruptPriorityFrame) { | |
| const char* const payload1 = | |
| "\x01" // request stream, request stream, exclusive | |
| "\x03" // prioritized_element_id | |
| "\x04" // element_dependency_id | |
| "\xFF" // weight | |
| "\xFF"; // superfluous data | |
| const char* const payload2 = | |
| "\xf1" // root of tree, root of tree, exclusive | |
| "\xFF" // weight | |
| "\xFF"; // superfluous data | |
| struct { | |
| const char* const payload; | |
| size_t payload_length; | |
| const char* const error_message; | |
| } kTestData[] = { | |
| {payload1, 0, "Unable to read PRIORITY frame flags."}, | |
| {payload1, 1, "Unable to read prioritized_element_id."}, | |
| {payload1, 2, "Unable to read element_dependency_id."}, | |
| {payload1, 3, "Unable to read PRIORITY frame weight."}, | |
| {payload1, 5, "Superfluous data in PRIORITY frame."}, | |
| {payload2, 0, "Unable to read PRIORITY frame flags."}, | |
| {payload2, 1, "Unable to read PRIORITY frame weight."}, | |
| {payload2, 3, "Superfluous data in PRIORITY frame."}, | |
| }; | |
| for (const auto& test_data : kTestData) { | |
| std::string input; | |
| input.push_back(2u); // type PRIORITY | |
| input.push_back(test_data.payload_length); | |
| size_t header_length = input.size(); | |
| input.append(test_data.payload, test_data.payload_length); | |
| HttpDecoder decoder(&visitor_); | |
| EXPECT_CALL(visitor_, OnPriorityFrameStart(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_INVALID_FRAME_DATA)); | |
| EXPECT_EQ(test_data.error_message, decoder.error_detail()); | |
| } | |
| } | |
| TEST_F(HttpDecoderTest, SettingsFrame) { | |
| InSequence s; | |
| std::string input = QuicTextUtils::HexDecode( | |
| "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. | |
| QuicStringPiece 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 settings frame identifier"}, | |
| {5, "Unable to read settings frame content"}, | |
| {7, "Unable to read settings frame identifier"}, | |
| {12, "Unable to read settings frame content"}, | |
| }; | |
| 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_INVALID_FRAME_DATA)); | |
| EXPECT_EQ(test_data.error_message, decoder.error_detail()); | |
| } | |
| } | |
| TEST_F(HttpDecoderTest, DuplicateSettingsIdentifier) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "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_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Duplicate SETTINGS identifier.", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, DataFrame) { | |
| InSequence s; | |
| std::string input = QuicStrCat(QuicTextUtils::HexDecode("00" // type (DATA) | |
| "05"), // length | |
| "Data!"); // data | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnDataFrameStart(2)).WillOnce(Return(false)); | |
| QuicStringPiece 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(QuicStringPiece("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)); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("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)); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("D"))); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a"))); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("t"))); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a"))); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("!"))); | |
| 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'); | |
| std::unique_ptr<char[]> buffer; | |
| QuicByteCount header_length = | |
| HttpEncoder::SerializeDataFrameHeader(input.length(), &buffer); | |
| std::string header = std::string(buffer.get(), header_length); | |
| // 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)); | |
| EXPECT_EQ(header_length - 1, | |
| decoder_.ProcessInput(header.data() + 1, header_length - 1)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Send data. | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece(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)); | |
| 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 = QuicTextUtils::HexDecode( | |
| "07" // type (GOAWAY) | |
| "01" // length | |
| "01"); // StreamId | |
| // 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 = | |
| QuicStrCat(QuicTextUtils::HexDecode("01" // type (HEADERS) | |
| "07"), // length | |
| "Headers"); // headers | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnHeadersFrameStart(2)).WillOnce(Return(false)); | |
| QuicStringPiece 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(QuicStringPiece("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)); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("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)); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("H"))); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e"))); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("a"))); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("d"))); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e"))); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("r"))); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("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 = QuicTextUtils::HexDecode( | |
| "00" // type (DATA) | |
| "00"); // length | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnDataFrameStart(2)).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)); | |
| 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)); | |
| 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 = QuicTextUtils::HexDecode( | |
| "01" // type (HEADERS) | |
| "00"); // length | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnHeadersFrameStart(2)).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)); | |
| 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)); | |
| EXPECT_CALL(visitor_, OnHeadersFrameEnd()); | |
| ProcessInputCharByChar(input); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) { | |
| InSequence s; | |
| std::string input = QuicTextUtils::HexDecode( | |
| "05" // type (PUSH_PROMISE) | |
| "01" // length | |
| "01"); // Push Id | |
| // Visitor pauses processing. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(1, 1)).WillOnce(Return(false)); | |
| EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()).WillOnce(Return(false)); | |
| EXPECT_EQ(0u, ProcessInputWithGarbageAppended("")); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the full frame. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(1, 1)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| // Process the frame incrementally. | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFramePushId(1, 1)); | |
| EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); | |
| ProcessInputCharByChar(input); | |
| EXPECT_THAT(decoder_.error(), IsQuicNoError()); | |
| EXPECT_EQ("", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "03" // type (CANCEL_PUSH) | |
| "10" // length | |
| "15"); // malformed payload | |
| // Process the full frame. | |
| EXPECT_CALL(visitor_, OnError(&decoder_)); | |
| EXPECT_EQ(2u, ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Frame is too large", decoder_.error_detail()); | |
| } | |
| 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, QUIC_ARRAYSIZE(input))); | |
| EXPECT_THAT(decoder_.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Frame is too large", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, HeadersPausedThenData) { | |
| InSequence s; | |
| std::string input = | |
| QuicStrCat(QuicTextUtils::HexDecode("01" // type (HEADERS) | |
| "07"), // length | |
| "Headers", // headers | |
| QuicTextUtils::HexDecode("00" // type (DATA) | |
| "05"), // length | |
| "Data!"); // data | |
| // Visitor pauses processing, maybe because header decompression is blocked. | |
| EXPECT_CALL(visitor_, OnHeadersFrameStart(2)); | |
| EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers"))); | |
| EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false)); | |
| QuicStringPiece 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)); | |
| EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("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[] = {{"\x03" // type (CANCEL_PUSH) | |
| "\x01" // length | |
| "\x40", // first byte of two-byte varint push id | |
| "Unable to read push_id"}, | |
| {"\x03" // type (CANCEL_PUSH) | |
| "\x04" // length | |
| "\x05" // valid push id | |
| "foo", // superfluous data | |
| "Superfluous data in CANCEL_PUSH frame."}, | |
| {"\x0D" // type (MAX_PUSH_ID) | |
| "\x01" // length | |
| "\x40", // first byte of two-byte varint push id | |
| "Unable to read push_id"}, | |
| {"\x0D" // type (MAX_PUSH_ID) | |
| "\x04" // length | |
| "\x05" // valid push id | |
| "foo", // superfluous data | |
| "Superfluous data in MAX_PUSH_ID frame."}, | |
| {"\x0E" // type (DUPLICATE_PUSH) | |
| "\x01" // length | |
| "\x40", // first byte of two-byte varint push id | |
| "Unable to read push_id"}, | |
| {"\x0E" // type (DUPLICATE_PUSH) | |
| "\x04" // length | |
| "\x05" // valid push id | |
| "foo", // superfluous data | |
| "Superfluous data in DUPLICATE_PUSH frame."}, | |
| {"\x07" // type (GOAWAY) | |
| "\x01" // length | |
| "\x40", // first byte of two-byte varint stream id | |
| "Unable to read GOAWAY stream_id"}, | |
| {"\x07" // type (GOAWAY) | |
| "\x04" // length | |
| "\x05" // valid stream id | |
| "foo", // superfluous data | |
| "Superfluous data in GOAWAY frame."}}; | |
| for (const auto& test_data : kTestData) { | |
| { | |
| HttpDecoder decoder(&visitor_); | |
| EXPECT_CALL(visitor_, OnError(&decoder)); | |
| QuicStringPiece input(test_data.input); | |
| decoder.ProcessInput(input.data(), input.size()); | |
| EXPECT_THAT(decoder.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ(test_data.error_message, decoder.error_detail()); | |
| } | |
| { | |
| HttpDecoder decoder(&visitor_); | |
| EXPECT_CALL(visitor_, OnError(&decoder)); | |
| QuicStringPiece input(test_data.input); | |
| for (auto c : input) { | |
| decoder.ProcessInput(&c, 1); | |
| } | |
| EXPECT_THAT(decoder.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ(test_data.error_message, decoder.error_detail()); | |
| } | |
| } | |
| } | |
| TEST_F(HttpDecoderTest, EmptyCancelPushFrame) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "03" // type (CANCEL_PUSH) | |
| "00"); // frame length | |
| EXPECT_CALL(visitor_, OnError(&decoder_)); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Unable to read push_id", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, EmptySettingsFrame) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "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()); | |
| } | |
| // Regression test for https://crbug.com/1001823. | |
| TEST_F(HttpDecoderTest, EmptyPushPromiseFrame) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "05" // type (PUSH_PROMISE) | |
| "00"); // frame length | |
| EXPECT_CALL(visitor_, OnError(&decoder_)); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Corrupt PUSH_PROMISE frame.", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, EmptyGoAwayFrame) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "07" // type (GOAWAY) | |
| "00"); // frame length | |
| EXPECT_CALL(visitor_, OnError(&decoder_)); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Unable to read GOAWAY stream_id", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, EmptyMaxPushIdFrame) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "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_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Unable to read push_id", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, EmptyDuplicatePushFrame) { | |
| std::string input = QuicTextUtils::HexDecode( | |
| "0e" // type (DUPLICATE_PUSH) | |
| "00"); // frame length | |
| EXPECT_CALL(visitor_, OnError(&decoder_)); | |
| EXPECT_EQ(input.size(), ProcessInput(input)); | |
| EXPECT_THAT(decoder_.error(), IsError(QUIC_INVALID_FRAME_DATA)); | |
| EXPECT_EQ("Unable to read push_id", decoder_.error_detail()); | |
| } | |
| TEST_F(HttpDecoderTest, LargeStreamIdInGoAway) { | |
| GoAwayFrame frame; | |
| frame.stream_id = 1 << 30; | |
| 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()); | |
| } | |
| } // namespace test | |
| } // namespace quic |