blob: db8c98b1adf99573187b6703393f980ba869927f [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 "tscore/Diags.h"
#include "quic/QUICIntUtil.h"
#include "Http3Frame.h"
#include "Http3Config.h"
ClassAllocator<Http3Frame> http3FrameAllocator("http3FrameAllocator");
ClassAllocator<Http3DataFrame> http3DataFrameAllocator("http3DataFrameAllocator");
ClassAllocator<Http3HeadersFrame> http3HeadersFrameAllocator("http3HeadersFrameAllocator");
ClassAllocator<Http3SettingsFrame> http3SettingsFrameAllocator("http3SettingsFrameAllocator");
//
// Static functions
//
int
Http3Frame::length(const uint8_t *buf, size_t buf_len, uint64_t &length)
{
size_t length_field_length = 0;
return QUICVariableInt::decode(length, length_field_length, buf, buf_len);
}
Http3FrameType
Http3Frame::type(const uint8_t *buf, size_t buf_len)
{
uint64_t type = 0;
size_t type_field_length = 0;
int ret = QUICVariableInt::decode(type, type_field_length, buf, buf_len);
ink_assert(ret != 1);
if (type <= static_cast<uint64_t>(Http3FrameType::X_MAX_DEFINED)) {
return static_cast<Http3FrameType>(type);
} else {
return Http3FrameType::UNKNOWN;
}
}
//
// Generic Frame
//
Http3Frame::Http3Frame(const uint8_t *buf, size_t buf_len)
{
// Type
size_t type_field_length = 0;
int ret = QUICVariableInt::decode(reinterpret_cast<uint64_t &>(this->_type), type_field_length, buf, buf_len);
ink_assert(ret != 1);
// 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);
// Payload offset
this->_payload_offset = type_field_length + length_field_length;
}
Http3Frame::Http3Frame(Http3FrameType type) : _type(type) {}
uint64_t
Http3Frame::total_length() const
{
return this->_payload_offset + this->length();
}
uint64_t
Http3Frame::length() const
{
return this->_length;
}
Http3FrameType
Http3Frame::type() const
{
if (static_cast<uint64_t>(this->_type) <= static_cast<uint64_t>(Http3FrameType::X_MAX_DEFINED)) {
return this->_type;
} else {
return Http3FrameType::UNKNOWN;
}
}
void
Http3Frame::store(uint8_t *buf, size_t *len) const
{
// If you really need this, you should keep the data passed to its constructor
ink_assert(!"Not supported");
}
void
Http3Frame::reset(const uint8_t *buf, size_t len)
{
this->~Http3Frame();
new (this) Http3Frame(buf, len);
}
//
// UNKNOWN Frame
//
Http3UnknownFrame::Http3UnknownFrame(const uint8_t *buf, size_t buf_len) : Http3Frame(buf, buf_len), _buf(buf), _buf_len(buf_len) {}
void
Http3UnknownFrame::store(uint8_t *buf, size_t *len) const
{
memcpy(buf, this->_buf, this->_buf_len);
*len = this->_buf_len;
}
//
// DATA Frame
//
Http3DataFrame::Http3DataFrame(const uint8_t *buf, size_t buf_len) : Http3Frame(buf, buf_len)
{
this->_payload = buf + this->_payload_offset;
this->_payload_len = buf_len - this->_payload_offset;
}
Http3DataFrame::Http3DataFrame(ats_unique_buf payload, size_t payload_len)
: Http3Frame(Http3FrameType::DATA), _payload_uptr(std::move(payload)), _payload_len(payload_len)
{
this->_length = this->_payload_len;
this->_payload = this->_payload_uptr.get();
}
void
Http3DataFrame::store(uint8_t *buf, size_t *len) const
{
size_t written = 0;
size_t n;
QUICVariableInt::encode(buf, UINT64_MAX, n, static_cast<uint64_t>(this->_type));
written += n;
QUICVariableInt::encode(buf + written, UINT64_MAX, n, this->_length);
written += n;
memcpy(buf + written, this->_payload, this->_payload_len);
written += this->_payload_len;
*len = written;
}
void
Http3DataFrame::reset(const uint8_t *buf, size_t len)
{
this->~Http3DataFrame();
new (this) Http3DataFrame(buf, len);
}
const uint8_t *
Http3DataFrame::payload() const
{
return this->_payload;
}
uint64_t
Http3DataFrame::payload_length() const
{
return this->_payload_len;
}
//
// 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(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)
{
this->_length = header_block_len;
this->_header_block = this->_header_block_uptr.get();
}
void
Http3HeadersFrame::store(uint8_t *buf, size_t *len) const
{
size_t written = 0;
size_t n;
QUICVariableInt::encode(buf, UINT64_MAX, n, static_cast<uint64_t>(this->_type));
written += n;
QUICVariableInt::encode(buf + written, UINT64_MAX, n, this->_length);
written += n;
memcpy(buf + written, this->_header_block, this->_header_block_len);
written += this->_header_block_len;
*len = written;
}
void
Http3HeadersFrame::reset(const uint8_t *buf, size_t len)
{
this->~Http3HeadersFrame();
new (this) Http3HeadersFrame(buf, len);
}
const uint8_t *
Http3HeadersFrame::header_block() const
{
return this->_header_block;
}
uint64_t
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)
{
size_t len = this->_payload_offset;
uint32_t nsettings = 0;
while (len < buf_len) {
if (nsettings >= max_settings) {
this->_error_code = Http3ErrorCode::EXCESSIVE_LOAD;
this->_error_reason = reinterpret_cast<const char *>("too many settings");
break;
}
size_t id_len = QUICVariableInt::size(buf + len);
uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - len);
len += id_len;
size_t value_len = QUICVariableInt::size(buf + len);
uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - 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 == buf_len) {
this->_valid = true;
}
}
void
Http3SettingsFrame::store(uint8_t *buf, size_t *len) const
{
uint8_t payload[Http3SettingsFrame::MAX_PAYLOAD_SIZE] = {0};
uint8_t *p = payload;
size_t l = 0;
for (auto &it : this->_settings) {
QUICIntUtil::write_QUICVariableInt(static_cast<uint64_t>(it.first), p, &l);
p += l;
QUICIntUtil::write_QUICVariableInt(it.second, p, &l);
p += l;
}
// Exercise the requirement that unknown identifiers be ignored. - 4.2.5.1.
QUICIntUtil::write_QUICVariableInt(static_cast<uint64_t>(Http3SettingsId::UNKNOWN), p, &l);
p += l;
QUICIntUtil::write_QUICVariableInt(0, p, &l);
p += l;
size_t written = 0;
size_t payload_len = p - payload;
size_t n;
QUICVariableInt::encode(buf, UINT64_MAX, n, static_cast<uint64_t>(this->_type));
written += n;
QUICVariableInt::encode(buf + written, UINT64_MAX, n, payload_len);
written += n;
// Payload
memcpy(buf + written, payload, payload_len);
written += payload_len;
*len = written;
}
void
Http3SettingsFrame::reset(const uint8_t *buf, size_t len)
{
this->~Http3SettingsFrame();
new (this) Http3SettingsFrame(buf, len);
}
bool
Http3SettingsFrame::is_valid() const
{
return this->_valid;
}
Http3ErrorUPtr
Http3SettingsFrame::get_error() const
{
return std::make_unique<Http3ConnectionError>(this->_error_code, this->_error_reason);
}
bool
Http3SettingsFrame::contains(Http3SettingsId id) const
{
auto p = this->_settings.find(id);
return (p != this->_settings.end());
}
uint64_t
Http3SettingsFrame::get(Http3SettingsId id) const
{
auto p = this->_settings.find(id);
if (p != this->_settings.end()) {
return p->second;
}
return 0;
}
void
Http3SettingsFrame::set(Http3SettingsId id, uint64_t value)
{
this->_settings[id] = value;
}
//
// Http3FrameFactory
//
Http3FrameUPtr
Http3FrameFactory::create_null_frame()
{
return {nullptr, &Http3FrameDeleter::delete_null_frame};
}
Http3FrameUPtr
Http3FrameFactory::create(const uint8_t *buf, size_t len)
{
Http3Config::scoped_config params;
Http3Frame *frame = nullptr;
Http3FrameType type = Http3Frame::type(buf, len);
switch (type) {
case Http3FrameType::HEADERS:
frame = http3HeadersFrameAllocator.alloc();
new (frame) Http3HeadersFrame(buf, len);
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_headers_frame);
case Http3FrameType::DATA:
frame = http3DataFrameAllocator.alloc();
new (frame) Http3DataFrame(buf, len);
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_data_frame);
case Http3FrameType::SETTINGS:
frame = http3SettingsFrameAllocator.alloc();
new (frame) Http3SettingsFrame(buf, len, params->max_settings());
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_settings_frame);
default:
// Unknown frame
Debug("http3_frame_factory", "Unknown frame type %hhx", static_cast<uint8_t>(type));
frame = http3FrameAllocator.alloc();
new (frame) Http3Frame(buf, len);
return Http3FrameUPtr(frame, &Http3FrameDeleter::delete_frame);
}
}
std::shared_ptr<const Http3Frame>
Http3FrameFactory::fast_create(const uint8_t *buf, size_t len)
{
Http3FrameType type = Http3Frame::type(buf, len);
if (type == Http3FrameType::UNKNOWN) {
if (!this->_unknown_frame) {
this->_unknown_frame = Http3FrameFactory::create(buf, len);
} else {
this->_unknown_frame->reset(buf, len);
}
return this->_unknown_frame;
}
std::shared_ptr<Http3Frame> frame = this->_reusable_frames[static_cast<uint8_t>(type)];
if (frame == nullptr) {
frame = Http3FrameFactory::create(buf, len);
if (frame != nullptr) {
this->_reusable_frames[static_cast<uint8_t>(type)] = frame;
}
} else {
frame->reset(buf, len);
}
return frame;
}
std::shared_ptr<const Http3Frame>
Http3FrameFactory::fast_create(QUICStreamIO &stream_io, size_t frame_len)
{
uint8_t buf[65536];
// FIXME DATA frames can be giga bytes
ink_assert(sizeof(buf) > frame_len);
if (stream_io.peek(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)
{
ats_unique_buf buf = ats_unique_malloc(header_block_len);
memcpy(buf.get(), header_block, header_block_len);
Http3HeadersFrame *frame = http3HeadersFrameAllocator.alloc();
new (frame) Http3HeadersFrame(std::move(buf), header_block_len);
return Http3HeadersFrameUPtr(frame, &Http3FrameDeleter::delete_headers_frame);
}
Http3HeadersFrameUPtr
Http3FrameFactory::create_headers_frame(IOBufferReader *header_block_reader, size_t header_block_len)
{
ats_unique_buf buf = ats_unique_malloc(header_block_len);
int64_t nread;
while ((nread = header_block_reader->read(buf.get(), header_block_len)) > 0) {
;
}
Http3HeadersFrame *frame = http3HeadersFrameAllocator.alloc();
new (frame) Http3HeadersFrame(std::move(buf), header_block_len);
return Http3HeadersFrameUPtr(frame, &Http3FrameDeleter::delete_headers_frame);
}
Http3DataFrameUPtr
Http3FrameFactory::create_data_frame(const uint8_t *payload, size_t payload_len)
{
ats_unique_buf buf = ats_unique_malloc(payload_len);
memcpy(buf.get(), payload, payload_len);
Http3DataFrame *frame = http3DataFrameAllocator.alloc();
new (frame) Http3DataFrame(std::move(buf), payload_len);
return Http3DataFrameUPtr(frame, &Http3FrameDeleter::delete_data_frame);
}
// TODO: This should clone IOBufferBlock chain to avoid memcpy
Http3DataFrameUPtr
Http3FrameFactory::create_data_frame(IOBufferReader *reader, size_t payload_len)
{
ats_unique_buf buf = ats_unique_malloc(payload_len);
size_t written = 0;
while (written < payload_len) {
int64_t len = reader->block_read_avail();
if (written + len > payload_len) {
len = payload_len - written;
}
memcpy(buf.get() + written, reinterpret_cast<uint8_t *>(reader->start()), len);
reader->consume(len);
written += len;
}
ink_assert(written == payload_len);
Http3DataFrame *frame = http3DataFrameAllocator.alloc();
new (frame) Http3DataFrame(std::move(buf), payload_len);
return Http3DataFrameUPtr(frame, &Http3FrameDeleter::delete_data_frame);
}