use bytes::Bytes;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::{Buffer, Slice};
use crate::protocol::h3::frames::{Code, Frame, FrameType, Settings, StreamKind};
#[repr(C)]
#[derive(Clone, Copy)]
pub struct H3Limits {
pub max_message_size: u64,
pub max_message_body_size: u64,
pub max_headers_size: u64,
pub max_header_count: u16,
pub max_concurrent_streams: u32,
pub max_connection_buffer_size: u64,
pub max_premature_resets: u32,
pub max_requests_per_connection: u64,
pub max_encoder_table_size: u64,
pub qpack_block_timeout: f64,
pub max_peer_uni_streams: u32,
pub max_outstanding_sections: u32,
pub max_blocked_streams: u32,
pub tunnel_backlog: u32,
pub command_backlog: u32,
pub idle_capacity: u64,
pub receive_timeout: f64,
pub send_timeout: f64,
}
impl H3Limits {
pub fn build(limits: &crate::protocol::h3::H3Limits) -> Self {
Self {
max_message_size: limits.max_message_size,
max_message_body_size: limits.max_message_body_size,
max_headers_size: limits.max_headers_size,
max_header_count: limits.max_header_count,
max_concurrent_streams: limits.max_concurrent_streams,
max_connection_buffer_size: limits.max_connection_buffer_size,
max_premature_resets: limits.max_premature_resets,
max_requests_per_connection: limits.max_requests_per_connection,
max_encoder_table_size: limits.max_encoder_table_size,
qpack_block_timeout: limits.qpack_block_timeout,
max_peer_uni_streams: limits.max_peer_uni_streams,
max_outstanding_sections: limits.max_outstanding_sections,
max_blocked_streams: limits.max_blocked_streams,
tunnel_backlog: limits.tunnel_backlog,
command_backlog: limits.command_backlog,
idle_capacity: limits.idle_capacity,
receive_timeout: limits.receive_timeout,
send_timeout: limits.send_timeout,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_limits_default() -> H3Limits {
H3Limits::build(&crate::protocol::h3::H3Limits::default())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_limits_of(limits: *const crate::ffi::models::Limits) -> H3Limits {
H3Limits::build(&crate::protocol::h3::H3Limits::from(unsafe { crate::ffi::models::Limits::or_default(limits) }))
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Kind {
Control = 0x00,
Push = 0x01,
QPACKEncoder = 0x02,
QPACKDecoder = 0x03,
Request = 0x04,
}
impl Kind {
pub fn build(kind: StreamKind) -> Self {
match kind {
StreamKind::Control => Self::Control,
StreamKind::Push => Self::Push,
StreamKind::QPACKEncoder => Self::QPACKEncoder,
StreamKind::QPACKDecoder => Self::QPACKDecoder,
StreamKind::Request => Self::Request,
}
}
pub fn parse(self) -> StreamKind {
match self {
Self::Control => StreamKind::Control,
Self::Push => StreamKind::Push,
Self::QPACKEncoder => StreamKind::QPACKEncoder,
Self::QPACKDecoder => StreamKind::QPACKDecoder,
Self::Request => StreamKind::Request,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_stream_kind_code(kind: Kind) -> i64 {
match kind.parse().code() {
Some(code) => code as i64,
None => -1,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_stream_kind_from_code(code: u64) -> i32 {
match StreamKind::from_code(code) {
Some(kind) => Kind::build(kind) as i32,
None => -1,
}
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FrameKind {
Data = 0x00,
Headers = 0x01,
CancelPush = 0x03,
Settings = 0x04,
PushPromise = 0x05,
GoAway = 0x07,
MaxPushID = 0x0d,
}
impl FrameKind {
pub fn build(kind: FrameType) -> Self {
match kind {
FrameType::Data => Self::Data,
FrameType::Headers => Self::Headers,
FrameType::CancelPush => Self::CancelPush,
FrameType::Settings => Self::Settings,
FrameType::PushPromise => Self::PushPromise,
FrameType::GoAway => Self::GoAway,
FrameType::MaxPushID => Self::MaxPushID,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_frame_type_known(code: u64) -> bool {
FrameType::from_code(code).is_some()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_reserved_frame_count() -> usize {
FrameType::RESERVED.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_reserved_frame(index: usize) -> i64 {
match FrameType::RESERVED.get(index) {
Some(&code) => code as i64,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_data(data: *const u8, data_len: usize) -> *mut Frame {
let data = Bytes::copy_from_slice(unsafe { Slice::borrow(data, data_len) }.unwrap_or_default());
Box::into_raw(Box::new(Frame::Data(data)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_headers(block: *const u8, block_len: usize) -> *mut Frame {
let block = Bytes::copy_from_slice(unsafe { Slice::borrow(block, block_len) }.unwrap_or_default());
Box::into_raw(Box::new(Frame::Headers(block)))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_frame_cancel_push(push_id: u64) -> *mut Frame {
Box::into_raw(Box::new(Frame::CancelPush { push_id }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_settings(params: *const Parameter, count: usize) -> *mut Frame {
let mut settings = Vec::with_capacity(count);
if !params.is_null() {
for index in 0..count {
let parameter = unsafe { *params.add(index) };
settings.push((parameter.id, parameter.value));
}
}
Box::into_raw(Box::new(Frame::Settings(settings)))
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Parameter {
pub id: u64,
pub value: u64,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_push_promise(push_id: u64, block: *const u8, block_len: usize) -> *mut Frame {
let block = Bytes::copy_from_slice(unsafe { Slice::borrow(block, block_len) }.unwrap_or_default());
Box::into_raw(Box::new(Frame::PushPromise { push_id, block }))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_frame_goaway(id: u64) -> *mut Frame {
Box::into_raw(Box::new(Frame::GoAway { id }))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_frame_max_push_id(push_id: u64) -> *mut Frame {
Box::into_raw(Box::new(Frame::MaxPushID { push_id }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_free(frame: *mut Frame) {
if !frame.is_null() {
drop(unsafe { Box::from_raw(frame) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_kind(frame: *const Frame) -> FrameKind {
match unsafe { frame.as_ref() } {
Some(frame) => FrameKind::build(frame.kind()),
None => FrameKind::Data,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_bytes(frame: *const Frame) -> Slice {
match unsafe { frame.as_ref() } {
Some(Frame::Data(data)) => Slice::new(data),
Some(Frame::Headers(block)) => Slice::new(block),
Some(Frame::PushPromise { block, .. }) => Slice::new(block),
_ => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_id(frame: *const Frame) -> i64 {
match unsafe { frame.as_ref() } {
Some(Frame::CancelPush { push_id }) => *push_id as i64,
Some(Frame::PushPromise { push_id, .. }) => *push_id as i64,
Some(Frame::GoAway { id }) => *id as i64,
Some(Frame::MaxPushID { push_id }) => *push_id as i64,
_ => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_parameter_count(frame: *const Frame) -> usize {
match unsafe { frame.as_ref() } {
Some(Frame::Settings(params)) => params.len(),
_ => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_parameter(frame: *const Frame, index: usize) -> Parameter {
match unsafe { frame.as_ref() } {
Some(Frame::Settings(params)) => match params.get(index) {
Some(&(id, value)) => Parameter { id, value },
None => Parameter { id: 0, value: 0 },
},
_ => Parameter { id: 0, value: 0 },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_payload_len(frame: *const Frame) -> usize {
unsafe { frame.as_ref() }.map_or(0, |frame| frame.payload_len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_encode(frame: *const Frame) -> Buffer {
match unsafe { frame.as_ref() } {
Some(frame) => Buffer::new(frame.encode()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_payload(frame: *const Frame) -> Buffer {
match unsafe { frame.as_ref() } {
Some(frame) => Buffer::new(frame.payload()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_frame_decode(data: *const u8, data_len: usize, out: *mut *mut Frame, read: *mut usize, error: *mut *mut ErrorHandle) -> Status {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let mut buffer = bytes::BytesMut::from(data);
let before = buffer.len();
let outcome = Frame::parse(&mut buffer);
if !read.is_null() {
unsafe { *read = before - buffer.len() };
}
match outcome {
Ok(Some(frame)) => {
if !out.is_null() {
unsafe { *out = Box::into_raw(Box::new(frame)) };
}
Status::Ok
}
Ok(None) => Status::Closed,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct H3Settings {
pub qpack_max_table_capacity: u64,
pub qpack_blocked_streams: u64,
pub max_field_section_size: i64,
pub enable_connect_protocol: bool,
}
impl H3Settings {
pub fn build(settings: &Settings) -> Self {
Self {
qpack_max_table_capacity: settings.qpack_max_table_capacity,
qpack_blocked_streams: settings.qpack_blocked_streams,
max_field_section_size: settings.max_field_section_size.map_or(-1, |size| size as i64),
enable_connect_protocol: settings.enable_connect_protocol,
}
}
pub fn parse(&self) -> Settings {
Settings {
qpack_max_table_capacity: self.qpack_max_table_capacity,
qpack_blocked_streams: self.qpack_blocked_streams,
max_field_section_size: u64::try_from(self.max_field_section_size).ok(),
enable_connect_protocol: self.enable_connect_protocol,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_settings_default() -> H3Settings {
H3Settings::build(&Settings::default())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_settings_peer() -> H3Settings {
H3Settings::build(&Settings::peer())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_settings_parameter_count(settings: *const H3Settings) -> usize {
match unsafe { settings.as_ref() } {
Some(settings) => settings.parse().parameters().len(),
None => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_settings_parameter(settings: *const H3Settings, index: usize) -> Parameter {
let Some(settings) = (unsafe { settings.as_ref() }) else {
return Parameter { id: 0, value: 0 };
};
match settings.parse().parameters().get(index) {
Some(&(id, value)) => Parameter { id, value },
None => Parameter { id: 0, value: 0 },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_h3_settings_apply(settings: *mut H3Settings, id: u64, value: u64, error: *mut *mut ErrorHandle) -> Status {
let Some(settings) = (unsafe { settings.as_mut() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let mut parsed = settings.parse();
match parsed.apply(id, value) {
Ok(()) => {
*settings = H3Settings::build(&parsed);
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_setting_qpack_max_table_capacity() -> u64 {
Settings::QPACK_MAX_TABLE_CAPACITY
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_setting_max_field_section_size() -> u64 {
Settings::MAX_FIELD_SECTION_SIZE
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_setting_qpack_blocked_streams() -> u64 {
Settings::QPACK_BLOCKED_STREAMS
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_setting_enable_connect_protocol() -> u64 {
Settings::ENABLE_CONNECT_PROTOCOL
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_reserved_setting_count() -> usize {
Settings::RESERVED.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_reserved_setting(index: usize) -> i64 {
match Settings::RESERVED.get(index) {
Some(&id) => id as i64,
None => -1,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_h3_error_code_name(code: u64) -> Slice {
Slice::text(match code {
Code::NO_ERROR => "H3_NO_ERROR",
Code::GENERAL_PROTOCOL_ERROR => "H3_GENERAL_PROTOCOL_ERROR",
Code::INTERNAL_ERROR => "H3_INTERNAL_ERROR",
Code::STREAM_CREATION_ERROR => "H3_STREAM_CREATION_ERROR",
Code::CLOSED_CRITICAL_STREAM => "H3_CLOSED_CRITICAL_STREAM",
Code::FRAME_UNEXPECTED => "H3_FRAME_UNEXPECTED",
Code::FRAME_ERROR => "H3_FRAME_ERROR",
Code::EXCESSIVE_LOAD => "H3_EXCESSIVE_LOAD",
Code::ID_ERROR => "H3_ID_ERROR",
Code::SETTINGS_ERROR => "H3_SETTINGS_ERROR",
Code::MISSING_SETTINGS => "H3_MISSING_SETTINGS",
Code::REQUEST_REJECTED => "H3_REQUEST_REJECTED",
Code::REQUEST_CANCELLED => "H3_REQUEST_CANCELLED",
Code::REQUEST_INCOMPLETE => "H3_REQUEST_INCOMPLETE",
Code::MESSAGE_ERROR => "H3_MESSAGE_ERROR",
Code::CONNECT_ERROR => "H3_CONNECT_ERROR",
Code::VERSION_FALLBACK => "H3_VERSION_FALLBACK",
Code::QPACK_DECOMPRESSION_FAILED => "QPACK_DECOMPRESSION_FAILED",
Code::QPACK_ENCODER_STREAM_ERROR => "QPACK_ENCODER_STREAM_ERROR",
Code::QPACK_DECODER_STREAM_ERROR => "QPACK_DECODER_STREAM_ERROR",
_ => "UNKNOWN",
})
}