blob: 0c7a65b749c9386caebde377df0357072435288b [file] [log] [blame]
/** @file
*
* A brief file description
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <catch2/catch_test_macros.hpp>
#include "proxy/http3/Http3FrameDispatcher.h"
#include "proxy/http3/Http3ProtocolEnforcer.h"
#include "Mock.h"
TEST_CASE("Http3FrameHandler dispatch", "[http3]")
{
Http3FrameDispatcher http3FrameDispatcher;
Http3MockFrameHandler handler;
Http3ProtocolEnforcer enforcer;
http3FrameDispatcher.add_handler(&handler);
http3FrameDispatcher.add_handler(&enforcer);
MIOBuffer *buf = new_MIOBuffer(BUFFER_SIZE_INDEX_512);
IOBufferReader *reader = buf->alloc_reader();
uint64_t nread = 0;
Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
SECTION("Test good case")
{
uint8_t input[] = {// 1st frame (HEADERS)
0x01, 0x04, 0x11, 0x22, 0x33, 0x44,
// 2nd frame (DATA)
0x00, 0x04, 0xaa, 0xbb, 0xcc, 0xdd,
// 3rd frame (incomplete)
0xff};
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::UNKNOWN, *reader, nread);
CHECK(!error);
CHECK(handler.total_frame_received == 1);
CHECK(nread == 12);
}
SECTION("Test good case with a multibyte frame type encoding")
{
uint8_t input[] = {// 1st frame (HEADERS)
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x11, 0x22, 0x33, 0x44,
// 2nd frame (DATA)
0x00, 0x04, 0xaa, 0xbb, 0xcc, 0xdd,
// 3rd frame (incomplete)
0xff};
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
SECTION("Write everything at once")
{
buf->write(input, sizeof(input));
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::UNKNOWN, *reader, nread);
CHECK(!error);
CHECK(handler.total_frame_received == 1);
CHECK(nread == 19);
}
SECTION("Write one byte at a time")
{
int total_nread{};
for (uint8_t *it{input}; it < input + sizeof(input); ++it) {
buf->write(it, 1);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::UNKNOWN, *reader, nread);
total_nread += nread;
CHECK(!error);
}
CHECK(handler.total_frame_received == 5);
CHECK(total_nread == 19);
}
}
free_MIOBuffer(buf);
}
TEST_CASE("control stream tests", "[http3]")
{
Http3FrameDispatcher http3FrameDispatcher;
Http3ProtocolEnforcer enforcer;
Http3MockFrameHandler handler;
http3FrameDispatcher.add_handler(&enforcer);
http3FrameDispatcher.add_handler(&handler);
MIOBuffer *buf = new_MIOBuffer(BUFFER_SIZE_INDEX_512);
IOBufferReader *reader = buf->alloc_reader();
uint64_t nread = 0;
Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
SECTION("Only one SETTINGS frame is allowed per the control stream")
{
uint8_t input[] = {
0x04, // Type
0x08, // Length
0x06, // Identifier
0x44, 0x00, // Value
0x09, // Identifier
0x0f, // Value
0x4a, 0x0a, // Identifier
0x00, // Value
0x04, // Type
0x08, // Length
0x06, // Identifier
0x44, 0x00, // Value
0x09, // Identifier
0x0f, // Value
0x4a, 0x0a, // Identifier
0x00, // Value
};
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::CONTROL, *reader, nread);
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_FRAME_UNEXPECTED);
CHECK(handler.total_frame_received == 1);
CHECK(nread == sizeof(input));
}
SECTION("first frame of the control stream must be SETTINGS frame")
{
uint8_t input[] = {
0x0d, // Type
0x01, // Length
0x01, // Push ID
0x04, // Type
0x08, // Length
0x06, // Identifier
0x44, 0x00, // Value
0x09, // Identifier
0x0f, // Value
0x4a, 0x0a, // Identifier
0x00, // Value
};
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::CONTROL, *reader, nread);
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_MISSING_SETTINGS);
CHECK(handler.total_frame_received == 0);
CHECK(nread == 3);
}
SECTION("DATA frame is not allowed on control stream")
{
uint8_t input[] = {0x04, // Type
0x08, // Length
0x06, // Identifier
0x44, 0x00, // Value
0x09, // Identifier
0x0f, // Value
0x4a, 0x0a, // Identifier
0x00, // Value
0x00, // Type
0x04, // Length
0x11, 0x22, 0x33, 0x44};
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::CONTROL, *reader, nread);
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_FRAME_UNEXPECTED);
CHECK(handler.total_frame_received == 1);
CHECK(nread == sizeof(input));
}
SECTION("HEADERS frame is not allowed on control stream")
{
uint8_t input[] = {0x04, // Type
0x08, // Length
0x06, // Identifier
0x44, 0x00, // Value
0x09, // Identifier
0x0f, // Value
0x4a, 0x0a, // Identifier
0x00, // Value
0x01, // Type
0x04, // Length
0x11, 0x22, 0x33, 0x44};
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::CONTROL, *reader, nread);
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_FRAME_UNEXPECTED);
CHECK(handler.total_frame_received == 1);
CHECK(nread == sizeof(input));
}
SECTION("RESERVED frame is not allowed on control stream")
{
uint8_t input[] = {0x04, // Type
0x08, // Length
0x06, // Identifier
0x44, 0x00, // Value
0x09, // Identifier
0x0f, // Value
0x4a, 0x0a, // Identifier
0x00, // Value
0x06, // Type
0x04, // Length
0x11, 0x22, 0x33, 0x44};
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::CONTROL, *reader, nread);
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_FRAME_UNEXPECTED);
CHECK(handler.total_frame_received == 1);
CHECK(nread == sizeof(input));
}
SECTION("padding should not be interpreted as a DATA frame", "[http3]")
{
uint8_t input[] = {
0x40, 0x04, // Type
0x03, // Length
0x06, // Identifier
0x44, 0x00, // Value
};
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
int total_nread{};
for (uint8_t *it{input}; it < input + sizeof(input); ++it) {
buf->write(it, 1);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::CONTROL, *reader, nread);
total_nread += nread;
CHECK(!error);
}
CHECK(handler.total_frame_received == 1);
CHECK(total_nread == 6);
}
free_MIOBuffer(buf);
}
TEST_CASE("ignore unknown frames", "[http3]")
{
SECTION("ignore unkown frame")
{
uint8_t input[] = {
0x0f // Type
};
Http3FrameDispatcher http3FrameDispatcher;
MIOBuffer *buf = new_MIOBuffer(BUFFER_SIZE_INDEX_512);
IOBufferReader *reader = buf->alloc_reader();
uint64_t nread = 0;
Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
buf->write(input, sizeof(input));
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::UNKNOWN, *reader, nread);
CHECK(!error);
CHECK(nread == 0);
free_MIOBuffer(buf);
}
}
TEST_CASE("Reserved frame type not allowed", "[http3]")
{
SECTION("Reject reserved frame type in non control stream")
{
uint8_t input[] = {// 1st frame (HEADERS)
0x01, 0x04, 0x11, 0x22, 0x33, 0x44,
// 2nd frame (DATA)
0x06, 0x04, 0xaa, 0xbb, 0xcc, 0xdd,
// 3rd frame (incomplete)
0xff};
Http3FrameDispatcher http3FrameDispatcher;
Http3MockFrameHandler handler;
Http3ProtocolEnforcer enforcer;
http3FrameDispatcher.add_handler(&handler);
http3FrameDispatcher.add_handler(&enforcer);
MIOBuffer *buf = new_MIOBuffer(BUFFER_SIZE_INDEX_512);
IOBufferReader *reader = buf->alloc_reader();
uint64_t nread = 0;
Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
buf->write(input, sizeof(input));
// Initial state
CHECK(handler.total_frame_received == 0);
CHECK(nread == 0);
error = http3FrameDispatcher.on_read_ready(0, Http3StreamType::UNKNOWN, *reader, nread);
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_FRAME_UNEXPECTED);
CHECK(handler.total_frame_received == 0);
CHECK(nread == 12);
free_MIOBuffer(buf);
}
}