This is an automated email from the ASF dual-hosted git repository.
maskit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new bd5f3eded5 Enable receiving large H3 frames (#11497)
bd5f3eded5 is described below
commit bd5f3eded5a9d534fca0b3324f9ff011a9f62494
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Fri Jul 5 09:18:17 2024 -0600
Enable receiving large H3 frames (#11497)
* http3: Support reading large H3 frames
* Update tests
* Fix buffer-overflow
---
include/proxy/http3/Http3Frame.h | 60 ++++--
include/proxy/http3/Http3FrameDispatcher.h | 2 +
src/proxy/http3/Http3Frame.cc | 292 +++++++++++++++++----------
src/proxy/http3/Http3FrameDispatcher.cc | 68 ++++---
src/proxy/http3/Http3StreamDataVIOAdaptor.cc | 4 +-
src/proxy/http3/test/test_Http3Frame.cc | 85 +++++---
6 files changed, 323 insertions(+), 188 deletions(-)
diff --git a/include/proxy/http3/Http3Frame.h b/include/proxy/http3/Http3Frame.h
index 2b13be1c53..08ac54115a 100644
--- a/include/proxy/http3/Http3Frame.h
+++ b/include/proxy/http3/Http3Frame.h
@@ -35,29 +35,39 @@ public:
constexpr static size_t MAX_FRAM_HEADER_OVERHEAD = 128; ///< Type (i) +
Length (i)
Http3Frame() {}
- Http3Frame(const uint8_t *buf, size_t len);
+ Http3Frame(IOBufferReader &reader);
Http3Frame(Http3FrameType type);
- virtual ~Http3Frame() {}
+ virtual ~Http3Frame();
+ bool is_valid() const;
uint64_t total_length() const;
uint64_t length() const;
Http3FrameType type() const;
+ bool update();
virtual Ptr<IOBufferBlock> to_io_buffer_block() const;
- virtual void reset(const uint8_t *buf, size_t len);
+ virtual void reset(IOBufferReader &reader);
static int length(const uint8_t *buf, size_t buf_len,
uint64_t &length);
static Http3FrameType type(const uint8_t *buf, size_t buf_len);
protected:
- uint64_t _length = 0;
- Http3FrameType _type = Http3FrameType::UNKNOWN;
- size_t _payload_offset = 0;
+ IOBufferReader *_reader = nullptr;
+ bool _finished_reading = false;
+ uint64_t _length = 0;
+ Http3FrameType _type = Http3FrameType::UNKNOWN;
+ size_t _payload_offset = 0;
+ bool _is_valid = true;
+
+ virtual bool _parse();
+
+private:
+ bool _is_ready = false;
};
class Http3UnknownFrame : public Http3Frame
{
public:
Http3UnknownFrame() : Http3Frame() {}
- Http3UnknownFrame(const uint8_t *buf, size_t len);
+ Http3UnknownFrame(IOBufferReader &reader);
Ptr<IOBufferBlock> to_io_buffer_block() const override;
@@ -74,15 +84,17 @@ class Http3DataFrame : public Http3Frame
{
public:
Http3DataFrame() : Http3Frame() {}
- Http3DataFrame(const uint8_t *buf, size_t len);
+ Http3DataFrame(IOBufferReader &reader);
Http3DataFrame(ats_unique_buf payload, size_t payload_len);
Ptr<IOBufferBlock> to_io_buffer_block() const override;
- void reset(const uint8_t *buf, size_t len) override;
+ void reset(IOBufferReader &reader) override;
const uint8_t *payload() const;
uint64_t payload_length() const;
+ IOBufferReader *data() const;
+
private:
const uint8_t *_payload = nullptr;
ats_unique_buf _payload_uptr = {nullptr};
@@ -97,17 +109,21 @@ class Http3HeadersFrame : public Http3Frame
{
public:
Http3HeadersFrame() : Http3Frame() {}
- Http3HeadersFrame(const uint8_t *buf, size_t len);
+ Http3HeadersFrame(IOBufferReader &reader);
Http3HeadersFrame(ats_unique_buf header_block, size_t header_block_len);
+ ~Http3HeadersFrame();
Ptr<IOBufferBlock> to_io_buffer_block() const override;
- void reset(const uint8_t *buf, size_t len) override;
+ void reset(IOBufferReader &reader) override;
const uint8_t *header_block() const;
uint64_t header_block_length() const;
+protected:
+ bool _parse() override;
+
private:
- const uint8_t *_header_block = nullptr;
+ uint8_t *_header_block = nullptr;
ats_unique_buf _header_block_uptr = {nullptr};
size_t _header_block_len = 0;
};
@@ -120,7 +136,7 @@ class Http3SettingsFrame : public Http3Frame
{
public:
Http3SettingsFrame() : Http3Frame(Http3FrameType::SETTINGS) {}
- Http3SettingsFrame(const uint8_t *buf, size_t len, uint32_t max_settings =
0);
+ Http3SettingsFrame(IOBufferReader &reader, uint32_t max_settings = 0);
static constexpr size_t MAX_PAYLOAD_SIZE = 60;
static constexpr std::array<Http3SettingsId, 4> VALID_SETTINGS_IDS{
@@ -131,21 +147,22 @@ public:
};
Ptr<IOBufferBlock> to_io_buffer_block() const override;
- void reset(const uint8_t *buf, size_t len) override;
+ void reset(IOBufferReader &reader) override;
- bool is_valid() const;
Http3ErrorUPtr get_error() const;
bool contains(Http3SettingsId id) const;
uint64_t get(Http3SettingsId id) const;
void set(Http3SettingsId id, uint64_t value);
+protected:
+ bool _parse() override;
+
private:
+ uint32_t _max_settings = 0;
std::map<Http3SettingsId, uint64_t> _settings;
- // TODO: make connection error with HTTP_MALFORMED_FRAME
- bool _valid = false;
- Http3ErrorCode _error_code;
- const char *_error_reason = nullptr;
+ Http3ErrorCode _error_code;
+ const char *_error_reason = nullptr;
};
using Http3FrameDeleterFunc = void (*)(Http3Frame *p);
@@ -217,14 +234,13 @@ public:
/*
* This is used for creating a Http3Frame object based on received data.
*/
- static Http3FrameUPtr create(const uint8_t *buf, size_t len);
+ static Http3FrameUPtr create(IOBufferReader &reader);
/*
* This works almost the same as create() but it reuses created objects for
performance.
* If you create a frame object which has the same frame type that you
created before, the object will be reset by new data.
*/
- std::shared_ptr<const Http3Frame> fast_create(IOBufferReader &reader, size_t
frame_len);
- std::shared_ptr<const Http3Frame> fast_create(const uint8_t *buf, size_t
len);
+ std::shared_ptr<Http3Frame> fast_create(IOBufferReader &reader);
/*
* Creates a HEADERS frame.
diff --git a/include/proxy/http3/Http3FrameDispatcher.h
b/include/proxy/http3/Http3FrameDispatcher.h
index f0b2360a0e..d904956c1d 100644
--- a/include/proxy/http3/Http3FrameDispatcher.h
+++ b/include/proxy/http3/Http3FrameDispatcher.h
@@ -47,6 +47,8 @@ private:
int64_t _reading_frame_type_len;
int64_t _reading_frame_length_len;
uint64_t _reading_frame_payload_len;
+ uint64_t _bytes_to_skip;
Http3FrameFactory _frame_factory;
+ std::shared_ptr<Http3Frame> _current_frame = nullptr;
std::vector<Http3FrameHandler *> _handlers[256];
};
diff --git a/src/proxy/http3/Http3Frame.cc b/src/proxy/http3/Http3Frame.cc
index 96c46f794d..f2e3b91ca2 100644
--- a/src/proxy/http3/Http3Frame.cc
+++ b/src/proxy/http3/Http3Frame.cc
@@ -68,28 +68,61 @@ Http3Frame::type(const uint8_t *buf, size_t buf_len)
// Generic Frame
//
-Http3Frame::Http3Frame(const uint8_t *buf, size_t buf_len)
+Http3Frame::Http3Frame(IOBufferReader &reader) : _reader(&reader)
{
+ // Type field and Length field are variable-length integers, which can be 64
bits for each.
+ // 16 bytes (128 bits) is large enough to contain the two fields.
+ constexpr int MAX_HEADER_SIZE = 16;
+ uint8_t header[MAX_HEADER_SIZE];
+ uint8_t *p = reinterpret_cast<uint8_t *>(reader.memcpy(header,
MAX_HEADER_SIZE));
+ uint64_t header_len = p - header;
+
// Type
size_t type_field_length = 0;
uint64_t type = 0;
// Ideally we'd simply pass this->_type to decode, but arm compilers
complain with:
// error: dereferencing type-punned pointer will break strict-aliasing rules
- int ret = QUICVariableInt::decode(type, type_field_length, buf, buf_len);
+ int ret = QUICVariableInt::decode(type, type_field_length, header,
header_len);
+ if (ret != 0) {
+ this->_is_valid = false;
+ return;
+ }
this->_type = static_cast<Http3FrameType>(type);
- ink_assert(ret != 1);
+
+ // Check if we can proceed to read Length field
+ if (header_len <= type_field_length) {
+ this->_is_valid = false;
+ return;
+ }
// Length
size_t length_field_length = 0;
- ret = QUICVariableInt::decode(this->_length, length_field_length, buf +
type_field_length, buf_len - type_field_length);
- ink_assert(ret != 1);
+ ret = QUICVariableInt::decode(this->_length, length_field_length, header +
type_field_length, header_len - type_field_length);
+ if (ret != 0) {
+ this->_is_valid = false;
+ return;
+ }
- // Payload offset
+ // Rest of the data is Frame Payload
+ this->_reader->consume(type_field_length + length_field_length);
this->_payload_offset = type_field_length + length_field_length;
}
Http3Frame::Http3Frame(Http3FrameType type) : _type(type) {}
+Http3Frame::~Http3Frame()
+{
+ if (this->_reader) {
+ this->_reader->dealloc();
+ }
+}
+
+bool
+Http3Frame::is_valid() const
+{
+ return this->_is_valid;
+}
+
uint64_t
Http3Frame::total_length() const
{
@@ -112,6 +145,22 @@ Http3Frame::type() const
}
}
+bool
+Http3Frame::update()
+{
+ if (!this->_is_ready) {
+ this->_is_ready = this->_parse();
+ }
+
+ return this->_is_ready;
+}
+
+bool
+Http3Frame::_parse()
+{
+ return true;
+}
+
Ptr<IOBufferBlock>
Http3Frame::to_io_buffer_block() const
{
@@ -120,16 +169,16 @@ Http3Frame::to_io_buffer_block() const
}
void
-Http3Frame::reset(const uint8_t *buf, size_t len)
+Http3Frame::reset(IOBufferReader &reader)
{
this->~Http3Frame();
- new (this) Http3Frame(buf, len);
+ new (this) Http3Frame(reader);
}
//
// UNKNOWN Frame
//
-Http3UnknownFrame::Http3UnknownFrame(const uint8_t *buf, size_t buf_len) :
Http3Frame(buf, buf_len), _buf(buf), _buf_len(buf_len) {}
+Http3UnknownFrame::Http3UnknownFrame(IOBufferReader &reader) :
Http3Frame(reader) {}
Ptr<IOBufferBlock>
Http3UnknownFrame::to_io_buffer_block() const
@@ -150,10 +199,9 @@ Http3UnknownFrame::to_io_buffer_block() const
//
// DATA Frame
//
-Http3DataFrame::Http3DataFrame(const uint8_t *buf, size_t buf_len) :
Http3Frame(buf, buf_len)
+Http3DataFrame::Http3DataFrame(IOBufferReader &reader) : Http3Frame(reader)
{
- this->_payload = buf + this->_payload_offset;
- this->_payload_len = buf_len - this->_payload_offset;
+ this->_payload_len = this->_length;
}
Http3DataFrame::Http3DataFrame(ats_unique_buf payload, size_t payload_len)
@@ -186,10 +234,10 @@ Http3DataFrame::to_io_buffer_block() const
}
void
-Http3DataFrame::reset(const uint8_t *buf, size_t len)
+Http3DataFrame::reset(IOBufferReader &reader)
{
this->~Http3DataFrame();
- new (this) Http3DataFrame(buf, len);
+ new (this) Http3DataFrame(reader);
}
const uint8_t *
@@ -204,14 +252,16 @@ Http3DataFrame::payload_length() const
return this->_payload_len;
}
+IOBufferReader *
+Http3DataFrame::data() const
+{
+ return _reader;
+}
+
//
// HEADERS Frame
//
-Http3HeadersFrame::Http3HeadersFrame(const uint8_t *buf, size_t buf_len) :
Http3Frame(buf, buf_len)
-{
- this->_header_block = buf + this->_payload_offset;
- this->_header_block_len = buf_len - this->_payload_offset;
-}
+Http3HeadersFrame::Http3HeadersFrame(IOBufferReader &reader) :
Http3Frame(reader) {}
Http3HeadersFrame::Http3HeadersFrame(ats_unique_buf header_block, size_t
header_block_len)
: Http3Frame(Http3FrameType::HEADERS),
_header_block_uptr(std::move(header_block)), _header_block_len(header_block_len)
@@ -220,6 +270,13 @@ Http3HeadersFrame::Http3HeadersFrame(ats_unique_buf
header_block, size_t header_
this->_header_block = this->_header_block_uptr.get();
}
+Http3HeadersFrame::~Http3HeadersFrame()
+{
+ if (this->_header_block_uptr == nullptr) {
+ ats_free(this->_header_block);
+ }
+}
+
Ptr<IOBufferBlock>
Http3HeadersFrame::to_io_buffer_block() const
{
@@ -243,10 +300,10 @@ Http3HeadersFrame::to_io_buffer_block() const
}
void
-Http3HeadersFrame::reset(const uint8_t *buf, size_t len)
+Http3HeadersFrame::reset(IOBufferReader &reader)
{
this->~Http3HeadersFrame();
- new (this) Http3HeadersFrame(buf, len);
+ new (this) Http3HeadersFrame(reader);
}
const uint8_t *
@@ -261,61 +318,28 @@ Http3HeadersFrame::header_block_length() const
return this->_header_block_len;
}
-//
-// SETTINGS Frame
-//
-
-Http3SettingsFrame::Http3SettingsFrame(const uint8_t *buf, size_t buf_len,
uint32_t max_settings) : Http3Frame(buf, buf_len)
+bool
+Http3HeadersFrame::_parse()
{
- size_t len = this->_payload_offset;
- uint32_t nsettings = 0;
-
- while (len < buf_len) {
- if (nsettings >= max_settings) {
- this->_error_code = Http3ErrorCode::H3_EXCESSIVE_LOAD;
- this->_error_reason = reinterpret_cast<const char *>("too many
settings");
- break;
- }
-
- size_t id_len = QUICVariableInt::size(buf + len);
- if ((len + id_len) >=
- buf_len) { // if the id is larger than the buffer or at the boundary
of the buffer (i.e. no value), it is invalid
- this->_error_code = Http3ErrorCode::H3_SETTINGS_ERROR;
- this->_error_reason = reinterpret_cast<const char *>("invalid SETTINGS
frame");
- break;
- }
- uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - len);
- len += id_len;
-
- size_t value_len = QUICVariableInt::size(buf + len);
- if ((len + value_len) > buf_len) {
- this->_error_code = Http3ErrorCode::H3_SETTINGS_ERROR;
- this->_error_reason = reinterpret_cast<const char *>("invalid SETTINGS
frame");
- break;
- }
- uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len -
len);
- len += value_len;
+ if (this->_reader->read_avail() != static_cast<int64_t>(this->_length)) {
+ // Whole payload is not received yet
+ return false;
+ }
- // Ignore any SETTINGS identifier it does not understand.
- bool ignore = true;
- for (const auto &known_id : Http3SettingsFrame::VALID_SETTINGS_IDS) {
- if (id == static_cast<uint64_t>(known_id)) {
- ignore = false;
- break;
- }
- }
+ this->_header_block_len = this->_length;
+ this->_header_block = static_cast<uint8_t
*>(ats_malloc(this->_header_block_len));
+ this->_reader->memcpy(this->_header_block, this->_header_block_len);
- if (ignore) {
- continue;
- }
+ return true;
+}
- this->_settings.insert(std::make_pair(static_cast<Http3SettingsId>(id),
value));
- ++nsettings;
- }
+//
+// SETTINGS Frame
+//
- if (len == buf_len) {
- this->_valid = true;
- }
+Http3SettingsFrame::Http3SettingsFrame(IOBufferReader &reader, uint32_t
max_settings)
+ : Http3Frame(reader), _max_settings(max_settings)
+{
}
Ptr<IOBufferBlock>
@@ -363,16 +387,10 @@ Http3SettingsFrame::to_io_buffer_block() const
}
void
-Http3SettingsFrame::reset(const uint8_t *buf, size_t len)
+Http3SettingsFrame::reset(IOBufferReader &reader)
{
this->~Http3SettingsFrame();
- new (this) Http3SettingsFrame(buf, len);
-}
-
-bool
-Http3SettingsFrame::is_valid() const
-{
- return this->_valid;
+ new (this) Http3SettingsFrame(reader);
}
Http3ErrorUPtr
@@ -405,6 +423,74 @@ Http3SettingsFrame::set(Http3SettingsId id, uint64_t value)
this->_settings[id] = value;
}
+bool
+Http3SettingsFrame::_parse()
+{
+ if (this->_reader->read_avail() != static_cast<int64_t>(this->_length)) {
+ // Whole payload is not received yet
+ return false;
+ }
+
+ uint8_t *buf = static_cast<uint8_t *>(ats_malloc(this->_length));
+ this->_reader->memcpy(buf, this->_length);
+
+ size_t len = 0;
+ uint32_t nsettings = 0;
+
+ while (len < this->_length) {
+ if (nsettings >= this->_max_settings) {
+ this->_error_code = Http3ErrorCode::H3_EXCESSIVE_LOAD;
+ this->_error_reason = reinterpret_cast<const char *>("too many
settings");
+ this->_is_valid = false;
+ break;
+ }
+
+ size_t id_len = QUICVariableInt::size(buf + len);
+ if ((len + id_len) >=
+ this->_length) { // if the id is larger than the buffer or at the
boundary of the buffer (i.e. no value), it is invalid
+ this->_error_code = Http3ErrorCode::H3_SETTINGS_ERROR;
+ this->_error_reason = reinterpret_cast<const char *>("invalid SETTINGS
frame");
+ break;
+ }
+ uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len, this->_length
- len);
+ len += id_len;
+
+ size_t value_len = QUICVariableInt::size(buf + len);
+ if ((len + value_len) > this->_length) {
+ this->_error_code = Http3ErrorCode::H3_SETTINGS_ERROR;
+ this->_error_reason = reinterpret_cast<const char *>("invalid SETTINGS
frame");
+ break;
+ }
+ uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len,
this->_length - len);
+ len += value_len;
+
+ // Ignore any SETTINGS identifier it does not understand.
+ bool ignore = true;
+ for (const auto &known_id : Http3SettingsFrame::VALID_SETTINGS_IDS) {
+ if (id == static_cast<uint64_t>(known_id)) {
+ ignore = false;
+ break;
+ }
+ }
+
+ if (ignore) {
+ continue;
+ }
+
+ this->_settings.insert(std::make_pair(static_cast<Http3SettingsId>(id),
value));
+ ++nsettings;
+ }
+
+ if (len != this->_length) {
+ // TODO: make connection error with HTTP_MALFORMED_FRAME
+ this->_is_valid = false;
+ }
+
+ ats_free(buf);
+
+ return true;
+}
+
//
// Http3FrameFactory
//
@@ -415,43 +501,50 @@ Http3FrameFactory::create_null_frame()
}
Http3FrameUPtr
-Http3FrameFactory::create(const uint8_t *buf, size_t len)
+Http3FrameFactory::create(IOBufferReader &reader)
{
ts::Http3Config::scoped_config params;
Http3Frame *frame = nullptr;
- Http3FrameType type = Http3Frame::type(buf, len);
+
+ // FIXME Frame type can be longer than 1 byte
+ uint8_t type_buf[1];
+ reader.memcpy(type_buf, sizeof(type_buf));
+ Http3FrameType type = Http3Frame::type(type_buf, sizeof(type_buf));
switch (type) {
case Http3FrameType::HEADERS:
frame = http3HeadersFrameAllocator.alloc();
- new (frame) Http3HeadersFrame(buf, len);
+ new (frame) Http3HeadersFrame(reader);
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_headers_frame);
case Http3FrameType::DATA:
frame = http3DataFrameAllocator.alloc();
- new (frame) Http3DataFrame(buf, len);
+ new (frame) Http3DataFrame(reader);
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_data_frame);
case Http3FrameType::SETTINGS:
frame = http3SettingsFrameAllocator.alloc();
- new (frame) Http3SettingsFrame(buf, len, params->max_settings());
+ new (frame) Http3SettingsFrame(reader, params->max_settings());
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_settings_frame);
default:
// Unknown frame
Dbg(dbg_ctl_http3_frame_factory, "Unknown frame type %hhx",
static_cast<uint8_t>(type));
frame = http3FrameAllocator.alloc();
- new (frame) Http3Frame(buf, len);
+ new (frame) Http3Frame(reader);
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_frame);
}
}
-std::shared_ptr<const Http3Frame>
-Http3FrameFactory::fast_create(const uint8_t *buf, size_t len)
+std::shared_ptr<Http3Frame>
+Http3FrameFactory::fast_create(IOBufferReader &reader)
{
- Http3FrameType type = Http3Frame::type(buf, len);
+ // FIXME Frame type can be longer than 1 byte
+ uint8_t type_buf[1];
+ reader.memcpy(type_buf, sizeof(type_buf));
+ Http3FrameType type = Http3Frame::type(type_buf, sizeof(type_buf));
if (type == Http3FrameType::UNKNOWN) {
if (!this->_unknown_frame) {
- this->_unknown_frame = Http3FrameFactory::create(buf, len);
+ this->_unknown_frame = Http3FrameFactory::create(reader);
} else {
- this->_unknown_frame->reset(buf, len);
+ this->_unknown_frame->reset(reader);
}
return this->_unknown_frame;
}
@@ -459,32 +552,17 @@ Http3FrameFactory::fast_create(const uint8_t *buf, size_t
len)
std::shared_ptr<Http3Frame> frame =
this->_reusable_frames[static_cast<uint8_t>(type)];
if (frame == nullptr) {
- frame = Http3FrameFactory::create(buf, len);
+ frame = Http3FrameFactory::create(reader);
if (frame != nullptr) {
this->_reusable_frames[static_cast<uint8_t>(type)] = frame;
}
} else {
- frame->reset(buf, len);
+ frame->reset(reader);
}
return frame;
}
-std::shared_ptr<const Http3Frame>
-Http3FrameFactory::fast_create(IOBufferReader &reader, size_t frame_len)
-{
- uint8_t buf[65536];
-
- // FIXME DATA frames can be giga bytes
- ink_assert(sizeof(buf) > frame_len);
-
- if (reader.read(buf, frame_len) < static_cast<int64_t>(frame_len)) {
- // Return if whole frame data is not available
- return nullptr;
- }
- return this->fast_create(buf, frame_len);
-}
-
Http3HeadersFrameUPtr
Http3FrameFactory::create_headers_frame(const uint8_t *header_block, size_t
header_block_len)
{
diff --git a/src/proxy/http3/Http3FrameDispatcher.cc
b/src/proxy/http3/Http3FrameDispatcher.cc
index ae1a6e4771..1b6b3f32bb 100644
--- a/src/proxy/http3/Http3FrameDispatcher.cc
+++ b/src/proxy/http3/Http3FrameDispatcher.cc
@@ -50,10 +50,9 @@ Http3FrameDispatcher::add_handler(Http3FrameHandler *handler)
Http3ErrorUPtr
Http3FrameDispatcher::on_read_ready(QUICStreamId stream_id, Http3StreamType
stream_type, IOBufferReader &reader, uint64_t &nread)
{
- std::shared_ptr<const Http3Frame> frame(nullptr);
- Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
- nread = 0;
- uint32_t frame_count = 0;
+ Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
+ nread = 0;
+ uint32_t frame_count = 0;
while (true) {
// Read a length of Type field and hopefully a length of Length field too
@@ -94,32 +93,49 @@ Http3FrameDispatcher::on_read_ready(QUICStreamId stream_id,
Http3StreamType stre
}
if (this->_reading_state == READING_PAYLOAD) {
- // Create a frame
- // Type field length + Length field length + Payload length
- size_t frame_len = this->_reading_frame_type_len +
this->_reading_frame_length_len + this->_reading_frame_payload_len;
- auto cloned_reader = reader.clone();
- frame = this->_frame_factory.fast_create(*cloned_reader,
frame_len);
- cloned_reader->dealloc();
- if (frame == nullptr) {
+ if (read_len == 0) {
break;
}
- ++frame_count;
-
- // Consume buffer if frame is created
- nread += frame_len;
- reader.consume(frame_len);
-
- // Dispatch
- Http3FrameType type = frame->type();
- Dbg(dbg_ctl_http3, "[RX] [%" PRIu64 "] | %s size=%zu", stream_id,
Http3DebugNames::frame_type(type), frame_len);
- std::vector<Http3FrameHandler *> handlers =
this->_handlers[static_cast<uint8_t>(type)];
- for (auto h : handlers) {
- error = h->handle_frame(frame, frame_count - 1, stream_type);
- if (error && error->cls != Http3ErrorClass::UNDEFINED) {
- return error;
+ if (this->_current_frame == nullptr) {
+ // Create a frame
+ // Type field length + Length field length + Payload length
+ size_t frame_len = this->_reading_frame_type_len +
this->_reading_frame_length_len + this->_reading_frame_payload_len;
+
+ // Create a reader to read one frame. The reader will be deallocated
by the frame object created
+ auto cloned_reader = reader.clone();
+ cloned_reader->size_limit = frame_len;
+ this->_current_frame =
this->_frame_factory.fast_create(*cloned_reader);
+ if (this->_current_frame == nullptr) {
+ cloned_reader->dealloc();
+ break;
}
+ this->_bytes_to_skip = this->_current_frame->total_length();
+ ++frame_count;
+ }
+
+ auto skip = std::min(static_cast<uint64_t>(reader.read_avail()),
this->_bytes_to_skip);
+ reader.consume(skip);
+ this->_bytes_to_skip -= skip;
+ nread += skip;
+
+ if (this->_current_frame->update()) {
+ // Dispatch
+ Http3FrameType type = this->_current_frame->type();
+ Debug("http3", "[RX] [%" PRIu64 "] | %s size=%" PRIu64 "/%" PRIu64,
stream_id, Http3DebugNames::frame_type(type),
+ this->_current_frame->total_length() - _bytes_to_skip,
this->_current_frame->total_length());
+ std::vector<Http3FrameHandler *> handlers =
this->_handlers[static_cast<uint8_t>(type)];
+ for (auto h : handlers) {
+ error = h->handle_frame(this->_current_frame, frame_count - 1,
stream_type);
+ if (error && error->cls != Http3ErrorClass::UNDEFINED) {
+ return error;
+ }
+ }
+ }
+
+ if (this->_bytes_to_skip == 0) {
+ this->_current_frame = nullptr;
+ this->_reading_state = READING_TYPE_LEN;
}
- this->_reading_state = READING_TYPE_LEN;
}
}
diff --git a/src/proxy/http3/Http3StreamDataVIOAdaptor.cc
b/src/proxy/http3/Http3StreamDataVIOAdaptor.cc
index 0a9749bf6b..d4ffcb3a21 100644
--- a/src/proxy/http3/Http3StreamDataVIOAdaptor.cc
+++ b/src/proxy/http3/Http3StreamDataVIOAdaptor.cc
@@ -45,8 +45,8 @@ Http3StreamDataVIOAdaptor::handle_frame(std::shared_ptr<const
Http3Frame> frame,
const Http3DataFrame *dframe = dynamic_cast<const Http3DataFrame
*>(frame.get());
// Need to wait for headers to be written
- this->_buffer->write(dframe->payload(), dframe->payload_length());
- this->_total_data_length += dframe->payload_length();
+ int64_t written = this->_buffer->write(dframe->data());
+ this->_total_data_length += written;
return Http3ErrorUPtr(nullptr);
}
diff --git a/src/proxy/http3/test/test_Http3Frame.cc
b/src/proxy/http3/test/test_Http3Frame.cc
index 80010a2976..c28267f27d 100644
--- a/src/proxy/http3/test/test_Http3Frame.cc
+++ b/src/proxy/http3/test/test_Http3Frame.cc
@@ -37,38 +37,29 @@ TEST_CASE("Http3Frame Type", "[http3]")
TEST_CASE("Load DATA Frame", "[http3]")
{
- SECTION("No flags")
+ SECTION("Normal")
{
uint8_t buf1[] = {
0x00, // Type
0x04, // Length
0x11, 0x22, 0x33, 0x44, // Payload
};
- std::shared_ptr<const Http3Frame> frame1 = Http3FrameFactory::create(buf1,
sizeof(buf1));
+ 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<const Http3DataFrame> data_frame =
std::dynamic_pointer_cast<const Http3DataFrame>(frame1);
+ std::shared_ptr<Http3DataFrame> data_frame =
std::dynamic_pointer_cast<Http3DataFrame>(frame1);
CHECK(data_frame);
CHECK(data_frame->payload_length() == 4);
- CHECK(memcmp(data_frame->payload(), "\x11\x22\x33\x44", 4) == 0);
- }
+ IOBufferReader *data_reader = data_frame->data();
+ CHECK(data_reader->read_avail() == 4);
+ CHECK(memcmp(data_reader->start(), "\x11\x22\x33\x44", 4) == 0);
- SECTION("Have flags (invalid)")
- {
- uint8_t buf1[] = {
- 0x00, // Type
- 0x04, // Length
- 0x11, 0x22, 0x33, 0x44, // Payload
- };
- std::shared_ptr<const Http3Frame> frame1 = Http3FrameFactory::create(buf1,
sizeof(buf1));
- CHECK(frame1->type() == Http3FrameType::DATA);
- CHECK(frame1->length() == 4);
-
- std::shared_ptr<const Http3DataFrame> data_frame =
std::dynamic_pointer_cast<const Http3DataFrame>(frame1);
- CHECK(data_frame);
- CHECK(data_frame->payload_length() == 4);
- CHECK(memcmp(data_frame->payload(), "\x11\x22\x33\x44", 4) == 0);
+ free_MIOBuffer(input);
}
}
@@ -144,16 +135,22 @@ TEST_CASE("Load SETTINGS Frame", "[http3]")
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<const Http3Frame> frame = Http3FrameFactory::create(buf,
sizeof(buf));
+ 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<const Http3SettingsFrame> settings_frame =
std::dynamic_pointer_cast<const Http3SettingsFrame>(frame);
+ 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);
}
}
@@ -219,10 +216,14 @@ TEST_CASE("Http3FrameFactory Create Unknown Frame",
"[http3]")
0x0f, // Type
0x00, // Length
};
- std::shared_ptr<const Http3Frame> frame1 = Http3FrameFactory::create(buf1,
sizeof(buf1));
+ 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]")
@@ -239,21 +240,31 @@ TEST_CASE("Http3FrameFactory Fast Create Frame",
"[http3]")
0x04, // Length
0xaa, 0xbb, 0xcc, 0xdd, // Payload
};
- std::shared_ptr<const Http3Frame> frame1 = factory.fast_create(buf1,
sizeof(buf1));
+ 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->payload(), buf1 + 2, 4) == 0);
+ CHECK(memcmp(data_frame1->data()->start(), buf1 + 2, 4) == 0);
- std::shared_ptr<const Http3Frame> frame2 = factory.fast_create(buf2,
sizeof(buf2));
+ 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->payload(), buf2 + 2, 4) == 0);
+ 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]")
@@ -263,14 +274,18 @@ TEST_CASE("Http3FrameFactory Fast Create Unknown Frame",
"[http3]")
uint8_t buf1[] = {
0x0f, // Type
};
- std::shared_ptr<const Http3Frame> frame1 = factory.fast_create(buf1,
sizeof(buf1));
+ 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 input[] = {
+ uint8_t buf1[] = {
0x04, // Type
0x08, // Length
0x06, // Identifier
@@ -280,11 +295,15 @@ TEST_CASE("SETTINGS frame handler", "[http3]")
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,
sizeof(input), 1);
+ 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);
@@ -293,11 +312,15 @@ TEST_CASE("SETTINGS frame handler", "[http3]")
REQUIRE(error);
CHECK(error->code == Http3ErrorCode::H3_EXCESSIVE_LOAD);
- Http3SettingsFrame valid_frame = Http3SettingsFrame(input, sizeof(input), 3);
+ 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);
}