| /** @file |
| * |
| * Unit tests for Http3StreamDataVIOAdaptor body buffering. |
| * |
| * @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 <memory> |
| #include <vector> |
| |
| #include "iocore/eventsystem/Lock.h" |
| #include "iocore/eventsystem/VIO.h" |
| #include "iocore/eventsystem/IOBuffer.h" |
| #include "proxy/http3/Http3Frame.h" |
| #include "proxy/http3/Http3StreamDataVIOAdaptor.h" |
| |
| namespace |
| { |
| // Build a single DATA frame (type 0x00 + 1-byte length + payload) in the |
| // caller-owned buffer and return a frame object that reads from it. The caller |
| // must keep @a buf alive for the returned frame's lifetime. |
| std::shared_ptr<Http3DataFrame> |
| make_data_frame(MIOBuffer *buf, uint8_t payload_len, uint8_t fill) |
| { |
| IOBufferReader *reader = buf->alloc_reader(); |
| uint8_t header[] = {0x00, payload_len}; // payload_len < 64 -> 1-byte varint |
| buf->write(header, sizeof(header)); |
| |
| std::vector<uint8_t> payload(payload_len, fill); |
| buf->write(payload.data(), payload.size()); |
| |
| return std::make_shared<Http3DataFrame>(*reader); |
| } |
| } // namespace |
| |
| TEST_CASE("Http3StreamDataVIOAdaptor delivers a multi-frame body intact", "[http3]") |
| { |
| // Each DATA frame appends a separate block to the adaptor's internal |
| // buffer, so several frames leave the buffer spanning multiple blocks. |
| // Allocating the drain reader at finalize time would reset it to the |
| // current write tail and deliver only the last frame's bytes; every frame |
| // must reach the sink. |
| constexpr int frame_count = 4; |
| constexpr uint8_t per_frame_bytes = 50; |
| constexpr int64_t expected = static_cast<int64_t>(frame_count) * per_frame_bytes; |
| |
| // Sink VIO with a writer buffer; allocate its reader while empty so it is |
| // anchored at the head and observes everything finalize() writes. |
| MIOBuffer *sink_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_4K); |
| IOBufferReader *sink_reader = sink_buffer->alloc_reader(); |
| VIO sink_vio; |
| sink_vio.mutex = new_ProxyMutex(); |
| sink_vio.set_writer(sink_buffer); |
| |
| std::vector<MIOBuffer *> frame_buffers; |
| { |
| Http3StreamDataVIOAdaptor adaptor(&sink_vio); |
| |
| for (int i = 0; i < frame_count; ++i) { |
| MIOBuffer *fb = new_MIOBuffer(BUFFER_SIZE_INDEX_4K); |
| frame_buffers.push_back(fb); |
| auto frame = make_data_frame(fb, per_frame_bytes, static_cast<uint8_t>('A' + i)); |
| adaptor.handle_frame(frame); |
| } |
| |
| REQUIRE(adaptor.has_data()); |
| |
| adaptor.finalize(); |
| |
| CHECK(sink_reader->read_avail() == expected); |
| CHECK(sink_vio.nbytes == expected); |
| |
| // Frame i filled its payload with 'A' + i, so the sink bytes pin down |
| // order and block identity, not just the total length. |
| std::vector<uint8_t> want; |
| for (int i = 0; i < frame_count; ++i) { |
| want.insert(want.end(), per_frame_bytes, static_cast<uint8_t>('A' + i)); |
| } |
| std::vector<uint8_t> got(expected); |
| REQUIRE(sink_reader->read(got.data(), expected) == expected); |
| CHECK(got == want); |
| } |
| |
| free_MIOBuffer(sink_buffer); |
| for (auto *fb : frame_buffers) { |
| free_MIOBuffer(fb); |
| } |
| } |