| /** @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 <cstdio> |
| #include "proxy/http3/Http3Frame.h" |
| #include "proxy/http3/Http3FrameDispatcher.h" |
| #include "proxy/http3/Http3SettingsHandler.h" |
| |
| TEST_CASE("Http3Frame Type", "[http3]") |
| { |
| CHECK(Http3Frame::type(reinterpret_cast<const uint8_t *>("\x00\x00"), 2) == Http3FrameType::DATA); |
| // Undefined range |
| CHECK(Http3Frame::type(reinterpret_cast<const uint8_t *>("\x0f\x00"), 2) == Http3FrameType::UNKNOWN); |
| CHECK(Http3Frame::type(reinterpret_cast<const uint8_t *>("\xff\xff\xff\xff\xff\xff\xff\x00"), 9) == Http3FrameType::UNKNOWN); |
| } |
| |
| TEST_CASE("Load DATA Frame", "[http3]") |
| { |
| SECTION("Normal") |
| { |
| uint8_t buf1[] = { |
| 0x00, // Type |
| 0x04, // Length |
| 0x11, 0x22, 0x33, 0x44, // Payload |
| }; |
| MIOBuffer *input = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input->write(buf1, sizeof(buf1)); |
| IOBufferReader *input_reader = input->alloc_reader(); |
| std::shared_ptr<Http3Frame> frame1 = Http3FrameFactory::create(*input_reader); |
| frame1->update(); |
| CHECK(frame1->type() == Http3FrameType::DATA); |
| CHECK(frame1->length() == 4); |
| |
| std::shared_ptr<Http3DataFrame> data_frame = std::dynamic_pointer_cast<Http3DataFrame>(frame1); |
| CHECK(data_frame); |
| CHECK(data_frame->payload_length() == 4); |
| IOBufferReader *data_reader = data_frame->data(); |
| CHECK(data_reader->read_avail() == 4); |
| CHECK(memcmp(data_reader->start(), "\x11\x22\x33\x44", 4) == 0); |
| |
| free_MIOBuffer(input); |
| } |
| } |
| |
| TEST_CASE("Store DATA Frame", "[http3]") |
| { |
| SECTION("Normal") |
| { |
| uint8_t buf[32] = {0}; |
| size_t len; |
| uint8_t expected1[] = { |
| 0x00, // Type |
| 0x04, // Length |
| 0x11, 0x22, 0x33, 0x44, // Payload |
| }; |
| |
| uint8_t raw1[] = "\x11\x22\x33\x44"; |
| MIOBuffer *payload1 = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); |
| payload1->set(raw1, 4); |
| IOBufferReader *payload1_reader = payload1->alloc_reader(); |
| Http3DataFrame data_frame(*payload1_reader, 4); |
| free_MIOBuffer(payload1); |
| CHECK(data_frame.length() == 4); |
| |
| auto ibb = data_frame.to_io_buffer_block(); |
| IOBufferReader reader; |
| reader.block = ibb.get(); |
| len = reader.read_avail(); |
| reader.read(buf, sizeof(buf)); |
| CHECK(len == 6); |
| CHECK(memcmp(buf, expected1, len) == 0); |
| } |
| } |
| |
| TEST_CASE("Store HEADERS Frame", "[http3]") |
| { |
| SECTION("Normal") |
| { |
| uint8_t buf[32] = {0}; |
| size_t len; |
| uint8_t expected1[] = { |
| 0x01, // Type |
| 0x04, // Length |
| 0x11, 0x22, 0x33, 0x44, // Payload |
| }; |
| |
| uint8_t raw1[] = "\x11\x22\x33\x44"; |
| ats_unique_buf header_block = ats_unique_malloc(4); |
| memcpy(header_block.get(), raw1, 4); |
| |
| Http3HeadersFrame hdrs_frame(std::move(header_block), 4); |
| CHECK(hdrs_frame.length() == 4); |
| |
| auto ibb = hdrs_frame.to_io_buffer_block(); |
| IOBufferReader reader; |
| reader.block = ibb.get(); |
| len = reader.read_avail(); |
| reader.read(buf, sizeof(buf)); |
| CHECK(len == 6); |
| CHECK(memcmp(buf, expected1, len) == 0); |
| } |
| } |
| |
| TEST_CASE("Load SETTINGS Frame", "[http3]") |
| { |
| SECTION("Normal") |
| { |
| uint8_t buf[] = { |
| 0x04, // Type |
| 0x08, // Length |
| 0x06, // Identifier |
| 0x44, 0x00, // Value |
| 0x09, // Identifier |
| 0x0f, // Value |
| 0x4a, 0xba, // Identifier |
| 0x00, // Value |
| }; |
| MIOBuffer *input = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input->write(buf, sizeof(buf)); |
| IOBufferReader *input_reader = input->alloc_reader(); |
| |
| std::shared_ptr<Http3Frame> frame = Http3FrameFactory::create(*input_reader); |
| frame->update(); |
| CHECK(frame->type() == Http3FrameType::SETTINGS); |
| CHECK(frame->length() == sizeof(buf) - 2); |
| |
| std::shared_ptr<Http3SettingsFrame> settings_frame = std::dynamic_pointer_cast<Http3SettingsFrame>(frame); |
| CHECK(settings_frame); |
| CHECK(settings_frame->is_valid()); |
| CHECK(settings_frame->get(Http3SettingsId::MAX_FIELD_SECTION_SIZE) == 0x0400); |
| CHECK(settings_frame->get(Http3SettingsId::NUM_PLACEHOLDERS) == 0x0f); |
| |
| free_MIOBuffer(input); |
| } |
| } |
| |
| TEST_CASE("Store SETTINGS Frame", "[http3]") |
| { |
| SECTION("Normal") |
| { |
| uint8_t expected[] = { |
| 0x04, // Type |
| 0x08, // Length |
| 0x06, // Identifier |
| 0x44, 0x00, // Value |
| 0x09, // Identifier |
| 0x0f, // Value |
| 0x4a, 0x0a, // Identifier |
| 0x00, // Value |
| }; |
| |
| Http3SettingsFrame settings_frame; |
| settings_frame.set(Http3SettingsId::MAX_FIELD_SECTION_SIZE, 0x0400); |
| settings_frame.set(Http3SettingsId::NUM_PLACEHOLDERS, 0x0f); |
| |
| uint8_t buf[32] = {0}; |
| size_t len; |
| auto ibb = settings_frame.to_io_buffer_block(); |
| IOBufferReader reader; |
| reader.block = ibb.get(); |
| len = reader.read_avail(); |
| reader.read(buf, sizeof(buf)); |
| CHECK(len == sizeof(expected)); |
| CHECK(memcmp(buf, expected, len) == 0); |
| } |
| |
| SECTION("Normal from Client") |
| { |
| uint8_t expected[] = { |
| 0x04, // Type |
| 0x06, // Length |
| 0x06, // Identifier |
| 0x44, 0x00, // Value |
| 0x4a, 0x0a, // Identifier |
| 0x00, // Value |
| }; |
| |
| Http3SettingsFrame settings_frame; |
| settings_frame.set(Http3SettingsId::MAX_FIELD_SECTION_SIZE, 0x0400); |
| |
| uint8_t buf[32] = {0}; |
| size_t len; |
| auto ibb = settings_frame.to_io_buffer_block(); |
| IOBufferReader reader; |
| reader.block = ibb.get(); |
| len = reader.read_avail(); |
| reader.read(buf, sizeof(buf)); |
| CHECK(len == sizeof(expected)); |
| CHECK(memcmp(buf, expected, len) == 0); |
| } |
| } |
| |
| TEST_CASE("Http3FrameFactory Create Unknown Frame", "[http3]") |
| { |
| uint8_t buf1[] = { |
| 0x0f, // Type |
| 0x00, // Length |
| }; |
| MIOBuffer *input = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input->write(buf1, sizeof(buf1)); |
| IOBufferReader *input_reader = input->alloc_reader(); |
| std::shared_ptr<const Http3Frame> frame1 = Http3FrameFactory::create(*input_reader); |
| CHECK(frame1); |
| CHECK(frame1->type() == Http3FrameType::UNKNOWN); |
| CHECK(frame1->length() == 0); |
| free_MIOBuffer(input); |
| } |
| |
| TEST_CASE("Http3FrameFactory Fast Create Frame", "[http3]") |
| { |
| Http3FrameFactory factory; |
| |
| uint8_t buf1[] = { |
| 0x00, // Type |
| 0x04, // Length |
| 0x11, 0x22, 0x33, 0x44, // Payload |
| }; |
| uint8_t buf2[] = { |
| 0x00, // Type |
| 0x04, // Length |
| 0xaa, 0xbb, 0xcc, 0xdd, // Payload |
| }; |
| MIOBuffer *input1 = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input1->write(buf1, sizeof(buf1)); |
| IOBufferReader *input_reader1 = input1->alloc_reader(); |
| MIOBuffer *input2 = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input2->write(buf2, sizeof(buf2)); |
| IOBufferReader *input_reader2 = input2->alloc_reader(); |
| |
| std::shared_ptr<const Http3Frame> frame1 = factory.fast_create(*input_reader1); |
| CHECK(frame1 != nullptr); |
| |
| std::shared_ptr<const Http3DataFrame> data_frame1 = std::dynamic_pointer_cast<const Http3DataFrame>(frame1); |
| CHECK(data_frame1 != nullptr); |
| CHECK(memcmp(data_frame1->data()->start(), buf1 + 2, 4) == 0); |
| |
| std::shared_ptr<const Http3Frame> frame2 = factory.fast_create(*input_reader2); |
| CHECK(frame2 != nullptr); |
| |
| std::shared_ptr<const Http3DataFrame> data_frame2 = std::dynamic_pointer_cast<const Http3DataFrame>(frame2); |
| CHECK(data_frame2 != nullptr); |
| CHECK(memcmp(data_frame2->data()->start(), buf2 + 2, 4) == 0); |
| |
| CHECK(frame1 == frame2); |
| |
| free_MIOBuffer(input1); |
| free_MIOBuffer(input2); |
| } |
| |
| TEST_CASE("Http3FrameFactory Fast Create Unknown Frame", "[http3]") |
| { |
| Http3FrameFactory factory; |
| |
| uint8_t buf1[] = { |
| 0x0f, // Type |
| }; |
| MIOBuffer *input = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input->write(buf1, sizeof(buf1)); |
| IOBufferReader *input_reader = input->alloc_reader(); |
| std::shared_ptr<const Http3Frame> frame1 = factory.fast_create(*input_reader); |
| CHECK(frame1); |
| CHECK(frame1->type() == Http3FrameType::UNKNOWN); |
| free_MIOBuffer(input); |
| } |
| |
| TEST_CASE("SETTINGS frame handler", "[http3]") |
| { |
| uint8_t buf1[] = { |
| 0x04, // Type |
| 0x08, // Length |
| 0x06, // Identifier |
| 0x44, 0x00, // Value |
| 0x09, // Identifier |
| 0x0f, // Value |
| 0x4a, 0x0a, // Identifier |
| 0x00, // Value |
| }; |
| MIOBuffer *input = new_MIOBuffer(BUFFER_SIZE_INDEX_128); |
| input->write(buf1, sizeof(buf1)); |
| IOBufferReader *input_reader = input->alloc_reader(); |
| |
| Http3SettingsHandler handler = Http3SettingsHandler(nullptr); |
| Http3SettingsFrame invalid_frame = Http3SettingsFrame(*input_reader, 1); |
| Http3ErrorUPtr error = Http3ErrorUPtr(nullptr); |
| |
| invalid_frame.update(); |
| CHECK(invalid_frame.is_valid() == false); |
| |
| std::shared_ptr<const Http3Frame> invalid_frame_ptr = std::make_shared<const Http3SettingsFrame>(invalid_frame); |
| error = handler.handle_frame(invalid_frame_ptr); |
| |
| REQUIRE(error); |
| CHECK(error->code == Http3ErrorCode::H3_EXCESSIVE_LOAD); |
| |
| input->reset(); |
| input->write(buf1, sizeof(buf1)); |
| Http3SettingsFrame valid_frame = Http3SettingsFrame(*input_reader, 3); |
| |
| CHECK(valid_frame.is_valid() == true); |
| |
| std::shared_ptr<const Http3Frame> valid_frame_ptr = std::make_shared<const Http3SettingsFrame>(valid_frame); |
| error = handler.handle_frame(valid_frame_ptr); |
| CHECK(error == nullptr); |
| |
| free_MIOBuffer(input); |
| } |