use bytes::Bytes;
use crate::errors::Error;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::{Buffer, Runtime, Slice};
use crate::models::{Body, Message, Method, Role, URL, Version};
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PortKind {
UDS = 0,
TCP = 1,
QUIC = 2,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Port {
pub kind: i32,
pub number: u16,
pub path: *const u8,
pub path_len: usize,
}
impl PortKind {
pub fn from_code(code: i32) -> Option<Self> {
Some(match code {
0 => Self::UDS,
1 => Self::TCP,
2 => Self::QUIC,
_ => return None,
})
}
}
impl Port {
pub unsafe fn parse(&self) -> Option<crate::models::Port> {
match PortKind::from_code(self.kind)? {
PortKind::TCP => Some(crate::models::Port::TCP(self.number)),
PortKind::QUIC => Some(crate::models::Port::QUIC(self.number)),
PortKind::UDS => Some(crate::models::Port::UDS(unsafe { Slice::borrow_text(self.path, self.path_len) }?.to_owned())),
}
}
pub unsafe fn parse_all(ports: *const Port, count: usize) -> Option<Vec<crate::models::Port>> {
if ports.is_null() {
return None;
}
let mut parsed = Vec::with_capacity(count);
for index in 0..count {
parsed.push(unsafe { (*ports.add(index)).parse() }?);
}
Some(parsed)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_parse(url: *const u8, url_len: usize, out: *mut *mut URL, error: *mut *mut ErrorHandle) -> Status {
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(text) = (unsafe { Slice::borrow_text(url, url_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match URL::parse(text) {
Ok(url) => {
unsafe { *out = Box::into_raw(Box::new(url)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_free(url: *mut URL) {
if !url.is_null() {
drop(unsafe { Box::from_raw(url) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_scheme(url: *const URL) -> Slice {
Slice::maybe(unsafe { url.as_ref() }.map(|url| url.scheme.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_host(url: *const URL) -> Slice {
Slice::maybe(unsafe { url.as_ref() }.map(|url| url.host.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_target(url: *const URL) -> Slice {
Slice::maybe(unsafe { url.as_ref() }.map(|url| url.target.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_port(url: *const URL) -> u16 {
unsafe { url.as_ref() }.map_or(0, |url| url.port)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_secure(url: *const URL) -> bool {
unsafe { url.as_ref() }.is_some_and(|url| url.secure())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_authority(url: *const URL) -> Buffer {
match unsafe { url.as_ref() } {
Some(url) => Buffer::new(url.authority().into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_message_new(version: i32) -> *mut Message {
Box::into_raw(Box::new(Message::new(Version::of(version))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_request(method: i32, target: *const u8, target_len: usize, version: i32) -> *mut Message {
match (Method::from_code(method), unsafe { Slice::borrow_text(target, target_len) }) {
(Some(method), Some(target)) => Box::into_raw(Box::new(Message::request(method, target, Version::of(version)))),
_ => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_message_response(status_code: u16, version: i32) -> *mut Message {
Box::into_raw(Box::new(Message::response(status_code, Version::of(version))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_free(message: *mut Message) {
if !message.is_null() {
drop(unsafe { Box::from_raw(message) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_version(message: *const Message) -> Version {
unsafe { message.as_ref() }.map_or(Version::V1_1, |message| message.version)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_method(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(|message| message.method) {
Some(method) => method as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_status_code(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(|message| message.status_code) {
Some(status_code) => status_code as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_target(message: *const Message) -> Slice {
Slice::maybe(unsafe { message.as_ref() }.and_then(|message| message.target.as_deref()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_is_request(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.is_request())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_is_response(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.is_response())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_is_informational(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.is_informational())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_secure(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.security.secure)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_header_count(message: *const Message) -> usize {
unsafe { message.as_ref() }.and_then(|message| message.headers.as_ref()).map_or(0, |headers| headers.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_header_name(message: *const Message, index: usize) -> Slice {
let field = unsafe { message.as_ref() }.and_then(|message| message.headers.as_ref()).and_then(|headers| headers.iter().nth(index)).map(|(name, _)| name);
Slice::maybe(field)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_header_value(message: *const Message, index: usize) -> Slice {
let field = unsafe { message.as_ref() }.and_then(|message| message.headers.as_ref()).and_then(|headers| headers.iter().nth(index)).map(|(_, value)| value);
Slice::maybe(field)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_header(message: *const Message, name: *const u8, name_len: usize) -> Slice {
let Some(name) = (unsafe { Slice::borrow_text(name, name_len) }) else {
return Slice::ABSENT;
};
Slice::maybe(unsafe { message.as_ref() }.and_then(|message| message.headers.as_ref()).and_then(|headers| headers.get(name)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_append_header(message: *mut Message, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(message), Some(name), Some(value)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) })
else {
return false;
};
message.headers.get_or_insert_default().append(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_insert_header(message: *mut Message, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(message), Some(name), Some(value)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) })
else {
return false;
};
message.headers.get_or_insert_default().insert(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_remove_header(message: *mut Message, name: *const u8, name_len: usize) -> bool {
let (Some(message), Some(name)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return false;
};
message.headers.as_mut().is_some_and(|headers| headers.remove(name))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_trailer_count(message: *const Message) -> usize {
unsafe { message.as_ref() }.and_then(|message| message.trailers.as_ref()).map_or(0, |trailers| trailers.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_trailer_name(message: *const Message, index: usize) -> Slice {
let field = unsafe { message.as_ref() }.and_then(|message| message.trailers.as_ref()).and_then(|trailers| trailers.iter().nth(index)).map(|(name, _)| name);
Slice::maybe(field)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_trailer_value(message: *const Message, index: usize) -> Slice {
let field = unsafe { message.as_ref() }.and_then(|message| message.trailers.as_ref()).and_then(|trailers| trailers.iter().nth(index)).map(|(_, value)| value);
Slice::maybe(field)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_trailer(message: *const Message, name: *const u8, name_len: usize) -> Slice {
let Some(name) = (unsafe { Slice::borrow_text(name, name_len) }) else {
return Slice::ABSENT;
};
Slice::maybe(unsafe { message.as_ref() }.and_then(|message| message.trailers.as_ref()).and_then(|trailers| trailers.get(name)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_append_trailer(message: *mut Message, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(message), Some(name), Some(value)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) })
else {
return false;
};
message.trailers.get_or_insert_default().append(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_insert_trailer(message: *mut Message, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(message), Some(name), Some(value)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) })
else {
return false;
};
message.trailers.get_or_insert_default().insert(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_remove_trailer(message: *mut Message, name: *const u8, name_len: usize) -> bool {
let (Some(message), Some(name)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return false;
};
message.trailers.as_mut().is_some_and(|trailers| trailers.remove(name))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_stream_id(message: *const Message) -> i64 {
match unsafe { message.as_ref() }.and_then(|message| message.stream_id) {
Some(stream_id) => stream_id.0 as i64,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_stream_id(message: *mut Message, stream_id: i64) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.stream_id = u64::try_from(stream_id).ok().map(crate::models::StreamID);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_connection_id(message: *const Message) -> Slice {
match unsafe { message.as_ref() }.and_then(|message| message.connection_id.as_ref()) {
Some(id) => Slice::new(&id.0),
None => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_client(message: *const Message) -> Buffer {
match unsafe { message.as_ref() }.and_then(|message| message.client) {
Some(client) => Buffer::new(client.to_string().into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_compression(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(|message| message.compression) {
Some(compression) => compression as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_compression(message: *mut Message, compression: i32) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
if compression < 0 {
message.compression = None;
return true;
}
let Some(compression) = crate::ffi::helpers::compression::coding(compression) else {
return false;
};
message.compression = Some(compression);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_compressed(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(Message::compressed)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_accepted(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(Message::accepted) {
Some(compression) => compression as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_materialize(message: *mut Message, runtime: *const crate::ffi::Runtime, error: *mut *mut ErrorHandle) -> Status {
let (Some(message), Some(runtime)) = (unsafe { message.as_mut() }, unsafe { runtime.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match runtime.0.block_on(message.materialize()) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_compress(message: *mut Message, accepted: i32, error: *mut *mut ErrorHandle) -> Status {
let Some(message) = (unsafe { message.as_mut() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let accepted = match accepted {
..0 => None,
code => match crate::ffi::helpers::compression::coding(code) {
Some(compression) => Some(compression),
None => return unsafe { ErrorHandle::raise(error, Status::Invalid) },
},
};
match message.compress(accepted) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_decompress(message: *mut Message, max: u64, error: *mut *mut ErrorHandle) -> Status {
let Some(message) = (unsafe { message.as_mut() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match message.decompress(max) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_secure(message: *mut Message, secure: bool) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.security.secure = secure;
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_early_data(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.security.early_data)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_tls(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.security.tls)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_tls_version(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(|message| message.security.tls_version) {
Some(version) => version.0 as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_tls_group(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(|message| message.security.tls_group) {
Some(group) => group.0 as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_tls_cipher(message: *const Message) -> i32 {
match unsafe { message.as_ref() }.and_then(|message| message.security.tls_cipher) {
Some(cipher) => cipher.0 as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_quic(message: *const Message) -> bool {
unsafe { message.as_ref() }.is_some_and(|message| message.security.quic)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_quic_version(message: *const Message) -> i64 {
match unsafe { message.as_ref() }.and_then(|message| message.security.quic_version) {
Some(version) => version as i64,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_body_data(message: *mut Message, data: *const u8, data_len: usize) -> bool {
let (Some(message), Some(data)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow(data, data_len) }) else {
return false;
};
message.body = Some(Body::Data(Bytes::copy_from_slice(data)));
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_body_text(message: *mut Message, text: *const u8, text_len: usize) -> bool {
let (Some(message), Some(text)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(text, text_len) }) else {
return false;
};
message.body = Some(Body::Text(text.to_owned()));
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_body_file(message: *mut Message, path: *const u8, path_len: usize) -> bool {
let (Some(message), Some(path)) = (unsafe { message.as_mut() }, unsafe { Slice::borrow_text(path, path_len) }) else {
return false;
};
message.body = Some(Body::File(path.to_owned()));
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_body_len(message: *const Message) -> i64 {
match unsafe { message.as_ref() }.and_then(|message| message.body.as_ref()).and_then(|body| body.len()) {
Some(len) => len as i64,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_body(runtime: *mut Runtime, message: *const Message, out: *mut Buffer, error: *mut *mut ErrorHandle) -> Status {
let (Some(runtime), Some(message)) = (unsafe { runtime.as_ref() }, unsafe { message.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(body) = message.body.as_ref() else {
unsafe { *out = Buffer::EMPTY };
return Status::Ok;
};
match runtime.0.block_on(body.bytes()) {
Ok(octets) => {
unsafe { *out = Buffer::new(octets.to_vec()) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &Error::IO(failure)) },
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Limits {
pub max_message_size: u64,
pub max_message_body_size: u64,
pub max_decompressed_body_size: u64,
pub max_startline_size: u32,
pub max_headers_size: u64,
pub max_header_count: u16,
pub max_chunk_header_size: u32,
pub read_chunk_size: u64,
pub idle_capacity: u64,
pub max_pending_handshakes: u32,
pub handshake_timeout: f64,
pub read_timeout: f64,
pub write_timeout: f64,
pub receive_timeout: f64,
pub send_timeout: f64,
pub inline_body_size: u64,
pub max_concurrent_streams: u32,
pub max_connection_buffer_size: u64,
pub max_premature_resets: u32,
pub max_encoder_table_size: u64,
pub max_idle_frames: u32,
pub output_high_water: u64,
pub max_requests_per_connection: 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 ws_linger_timeout: f64,
pub ws_max_fragments: u16,
pub max_cookies: u32,
pub max_cookies_per_domain: u16,
pub max_hsts_entries: u32,
}
impl Limits {
pub fn parse(&self) -> crate::models::Limits {
crate::models::Limits {
max_message_size: self.max_message_size,
max_message_body_size: self.max_message_body_size,
max_decompressed_body_size: self.max_decompressed_body_size,
max_startline_size: self.max_startline_size,
max_headers_size: self.max_headers_size,
max_header_count: self.max_header_count,
max_chunk_header_size: self.max_chunk_header_size,
read_chunk_size: self.read_chunk_size,
idle_capacity: self.idle_capacity,
max_pending_handshakes: self.max_pending_handshakes,
handshake_timeout: self.handshake_timeout,
read_timeout: self.read_timeout,
write_timeout: self.write_timeout,
receive_timeout: self.receive_timeout,
send_timeout: self.send_timeout,
inline_body_size: self.inline_body_size,
max_concurrent_streams: self.max_concurrent_streams,
max_connection_buffer_size: self.max_connection_buffer_size,
max_premature_resets: self.max_premature_resets,
max_encoder_table_size: self.max_encoder_table_size,
max_idle_frames: self.max_idle_frames,
output_high_water: self.output_high_water,
max_requests_per_connection: self.max_requests_per_connection,
qpack_block_timeout: self.qpack_block_timeout,
max_peer_uni_streams: self.max_peer_uni_streams,
max_outstanding_sections: self.max_outstanding_sections,
max_blocked_streams: self.max_blocked_streams,
tunnel_backlog: self.tunnel_backlog,
command_backlog: self.command_backlog,
ws_linger_timeout: self.ws_linger_timeout,
ws_max_fragments: self.ws_max_fragments,
max_cookies: self.max_cookies,
max_cookies_per_domain: self.max_cookies_per_domain,
max_hsts_entries: self.max_hsts_entries,
}
}
pub fn build(limits: &crate::models::Limits) -> Self {
Self {
max_message_size: limits.max_message_size,
max_message_body_size: limits.max_message_body_size,
max_decompressed_body_size: limits.max_decompressed_body_size,
max_startline_size: limits.max_startline_size,
max_headers_size: limits.max_headers_size,
max_header_count: limits.max_header_count,
max_chunk_header_size: limits.max_chunk_header_size,
read_chunk_size: limits.read_chunk_size,
idle_capacity: limits.idle_capacity,
max_pending_handshakes: limits.max_pending_handshakes,
handshake_timeout: limits.handshake_timeout,
read_timeout: limits.read_timeout,
write_timeout: limits.write_timeout,
receive_timeout: limits.receive_timeout,
send_timeout: limits.send_timeout,
inline_body_size: limits.inline_body_size,
max_concurrent_streams: limits.max_concurrent_streams,
max_connection_buffer_size: limits.max_connection_buffer_size,
max_premature_resets: limits.max_premature_resets,
max_encoder_table_size: limits.max_encoder_table_size,
max_idle_frames: limits.max_idle_frames,
output_high_water: limits.output_high_water,
max_requests_per_connection: limits.max_requests_per_connection,
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,
ws_linger_timeout: limits.ws_linger_timeout,
ws_max_fragments: limits.ws_max_fragments,
max_cookies: limits.max_cookies,
max_cookies_per_domain: limits.max_cookies_per_domain,
max_hsts_entries: limits.max_hsts_entries,
}
}
pub unsafe fn or_default(limits: *const Limits) -> crate::models::Limits {
match unsafe { limits.as_ref() } {
Some(limits) => limits.parse(),
None => crate::models::Limits::default(),
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_limits_default() -> Limits {
Limits::build(&crate::models::Limits::default())
}
impl Version {
pub unsafe fn parse_all(versions: *const i32, count: usize) -> Option<Vec<Version>> {
if versions.is_null() {
return Some(Vec::new());
}
let mut parsed = Vec::with_capacity(count);
for index in 0..count {
parsed.push(match unsafe { *versions.add(index) } {
0 => Version::V1_0,
1 => Version::V1_1,
2 => Version::V2_0,
3 => Version::V3_0,
_ => return None,
});
}
Some(parsed)
}
}
impl Role {
pub fn build(role: Role) -> u32 {
match role {
Role::UserAgent => 0,
Role::Origin => 1,
Role::Proxy => 2,
Role::Gateway => 3,
Role::Tunnel => 4,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum TransportKind {
Stream = 0,
QUIC = 1,
}
impl TransportKind {
pub fn build(transport: crate::models::TransportKind) -> Self {
match transport {
crate::models::TransportKind::Stream => Self::Stream,
crate::models::TransportKind::QUIC => Self::QUIC,
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_port_transport(port: *const Port) -> TransportKind {
match unsafe { port.as_ref() }.and_then(|port| unsafe { port.parse() }) {
Some(port) => TransportKind::build(port.transport()),
None => TransportKind::Stream,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_port_carries(port: *const Port, version: i32) -> bool {
let Some(version) = Version::from_code(version) else {
return false;
};
unsafe { port.as_ref() }.and_then(|port| unsafe { port.parse() }).is_some_and(|port| port.carries(version))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_port_offers(port: *const Port, versions: *const Version, count: usize, out: *mut Version) -> usize {
let (Some(port), Some(versions)) = (unsafe { port.as_ref() }.and_then(|port| unsafe { port.parse() }), unsafe { Version::borrow_all(versions, count) }) else {
return 0;
};
let offered = port.offers(versions);
if !out.is_null() {
for (index, version) in offered.iter().take(count).enumerate() {
unsafe { *out.add(index) = *version };
}
}
offered.len()
}
impl Version {
pub unsafe fn borrow_all<'a>(versions: *const Version, count: usize) -> Option<&'a [Version]> {
if versions.is_null() {
return (count == 0).then_some(&[][..]);
}
Some(unsafe { std::slice::from_raw_parts(versions, count) })
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_default_port(scheme: *const u8, scheme_len: usize) -> u16 {
match unsafe { Slice::borrow_text(scheme, scheme_len) } {
Some(scheme) => URL::default_port(scheme),
None => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_url_authority_of(scheme: *const u8, scheme_len: usize, host: *const u8, host_len: usize, port: u16) -> Buffer {
let (Some(scheme), Some(host)) = (unsafe { Slice::borrow_text(scheme, scheme_len) }, unsafe { Slice::borrow_text(host, host_len) }) else {
return Buffer::EMPTY;
};
Buffer::new(URL::authority_of(scheme, host, port).into_bytes())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_version_alpn(version: i32) -> Slice {
Slice::maybe(Version::from_code(version).map(|version| version.alpn()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_version_from_alpn(alpn: *const u8, alpn_len: usize) -> i32 {
match unsafe { Slice::borrow(alpn, alpn_len) }.and_then(Version::from_alpn) {
Some(version) => version as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_version_major(version: i32) -> u8 {
Version::from_code(version).map_or(0, |version| version.major())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_version_transport(version: i32) -> TransportKind {
TransportKind::build(Version::of(version).transport())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_version_name(version: i32) -> Slice {
Slice::maybe(Version::from_code(version).map(|version| version.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_version_parse(name: *const u8, name_len: usize) -> i32 {
match unsafe { Slice::borrow_text(name, name_len) }.and_then(|name| name.parse::<Version>().ok()) {
Some(version) => version as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_alpn_wire(versions: *const Version, count: usize) -> Buffer {
match unsafe { Version::borrow_all(versions, count) } {
Some(versions) => Buffer::new(crate::models::ALPN::wire(versions)),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_alpn_select(versions: *const Version, count: usize, client: *const u8, client_len: usize) -> Slice {
let (Some(versions), Some(client)) = (unsafe { Version::borrow_all(versions, count) }, unsafe { Slice::borrow(client, client_len) }) else {
return Slice::ABSENT;
};
match crate::models::ALPN::select(&crate::models::ALPN::list(versions), client) {
Some(chosen) => Slice::new(chosen),
None => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_alpn_negotiated(alpn: *const u8, alpn_len: usize, versions: *const Version, count: usize, out: *mut Version, error: *mut *mut ErrorHandle) -> Status {
let Some(versions) = (unsafe { Version::borrow_all(versions, count) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::models::ALPN::negotiated(unsafe { Slice::borrow(alpn, alpn_len) }, versions) {
Ok(version) => {
if !out.is_null() {
unsafe { *out = version };
}
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_method_name(method: i32) -> Slice {
Slice::maybe(Method::from_code(method).map(|method| method.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_method_parse(name: *const u8, name_len: usize) -> i32 {
match unsafe { Slice::borrow_text(name, name_len) }.and_then(|name| name.parse::<Method>().ok()) {
Some(method) => method as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_method_safe(method: i32) -> bool {
Method::from_code(method).is_some_and(|method| method.safe())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_method_idempotent(method: i32) -> bool {
Method::from_code(method).is_some_and(|method| method.idempotent())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_role_is_client(role: i32) -> bool {
Role::from_code(role).is_some_and(|role| role.is_client())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_role_is_server(role: i32) -> bool {
Role::from_code(role).is_some_and(|role| role.is_server())
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum HeaderCase {
Title = 0,
Lower = 1,
}
impl HeaderCase {
pub fn parse(self) -> crate::models::HeaderCase {
match self {
Self::Title => crate::models::HeaderCase::Title,
Self::Lower => crate::models::HeaderCase::Lower,
}
}
pub fn build(case: crate::models::HeaderCase) -> Self {
match case {
crate::models::HeaderCase::Title => Self::Title,
crate::models::HeaderCase::Lower => Self::Lower,
}
}
pub fn from_code(code: i32) -> Option<Self> {
Some(match code {
0 => Self::Title,
1 => Self::Lower,
_ => return None,
})
}
pub fn of(code: i32) -> Self {
Self::from_code(code).unwrap_or(Self::Lower)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_header_case_from_version(version: i32) -> HeaderCase {
HeaderCase::build(crate::models::HeaderCase::from_version(Version::of(version)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_header_case_apply(case: i32, name: *const u8, name_len: usize) -> Buffer {
match unsafe { Slice::borrow_text(name, name_len) } {
Some(name) => Buffer::new(HeaderCase::of(case).parse().apply(name).into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_header_case_apply_in_place(case: i32, name: *mut u8, name_len: usize) -> bool {
if name.is_null() {
return false;
}
HeaderCase::of(case).parse().apply_in_place(unsafe { std::slice::from_raw_parts_mut(name, name_len) });
true
}
pub use crate::models::Headers;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_well_known(name: *const u8, name_len: usize) -> u32 {
match unsafe { Slice::borrow_text(name, name_len) } {
Some(name) => Headers::well_known(name),
None => 0,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_headers_bit(matched: bool, index: u32) -> u32 {
Headers::bit(matched, index)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_named(stored: *const u8, stored_len: usize, name: *const u8, name_len: usize) -> bool {
let (Some(stored), Some(name)) = (unsafe { Slice::borrow_text(stored, stored_len) }, unsafe { Slice::borrow_text(name, name_len) }) else {
return false;
};
Headers::named(stored, name)
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_headers_new() -> *mut Headers {
Box::into_raw(Box::new(Headers::new()))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_headers_with_capacity(fields: usize) -> *mut Headers {
Box::into_raw(Box::new(Headers::with_capacity(fields)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_free(headers: *mut Headers) {
if !headers.is_null() {
drop(unsafe { Box::from_raw(headers) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_len(headers: *const Headers) -> usize {
unsafe { headers.as_ref() }.map_or(0, |headers| headers.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_is_empty(headers: *const Headers) -> bool {
unsafe { headers.as_ref() }.is_none_or(|headers| headers.is_empty())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_name(headers: *const Headers, index: usize) -> Slice {
Slice::maybe(unsafe { headers.as_ref() }.and_then(|headers| headers.fields().get(index)).map(|field| field.name.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_value(headers: *const Headers, index: usize) -> Slice {
Slice::maybe(unsafe { headers.as_ref() }.and_then(|headers| headers.fields().get(index)).map(|field| field.value.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_contains(headers: *const Headers, name: *const u8, name_len: usize) -> bool {
let (Some(headers), Some(name)) = (unsafe { headers.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return false;
};
headers.contains(name)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_absent(headers: *const Headers, name: *const u8, name_len: usize) -> bool {
let (Some(headers), Some(name)) = (unsafe { headers.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return true;
};
headers.absent(name)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_get(headers: *const Headers, name: *const u8, name_len: usize) -> Slice {
let (Some(headers), Some(name)) = (unsafe { headers.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return Slice::ABSENT;
};
Slice::maybe(headers.get(name))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_get_all_count(headers: *const Headers, name: *const u8, name_len: usize) -> usize {
let (Some(headers), Some(name)) = (unsafe { headers.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return 0;
};
headers.get_all(name).count()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_get_all(headers: *const Headers, name: *const u8, name_len: usize, index: usize) -> Slice {
let (Some(headers), Some(name)) = (unsafe { headers.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return Slice::ABSENT;
};
Slice::maybe(headers.get_all(name).nth(index))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_append(headers: *mut Headers, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(headers), Some(name), Some(value)) = (unsafe { headers.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) }) else {
return false;
};
headers.append(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_append_lowercase(headers: *mut Headers, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(headers), Some(name), Some(value)) = (unsafe { headers.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) }) else {
return false;
};
headers.append_lowercase(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_insert(headers: *mut Headers, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(headers), Some(name), Some(value)) = (unsafe { headers.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) }) else {
return false;
};
headers.insert(name, value);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_headers_remove(headers: *mut Headers, name: *const u8, name_len: usize) -> bool {
let (Some(headers), Some(name)) = (unsafe { headers.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }) else {
return false;
};
headers.remove(name)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_headers(message: *mut Message) -> *mut Headers {
match unsafe { message.as_mut() } {
Some(message) => message.headers.get_or_insert_with(Headers::new),
None => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_trailers(message: *mut Message) -> *mut Headers {
match unsafe { message.as_mut() } {
Some(message) => message.trailers.get_or_insert_with(Headers::new),
None => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_tunneling(message: *const Message, method: i32) -> bool {
let Some(message) = (unsafe { message.as_ref() }) else {
return false;
};
message.tunneling(Method::from_code(method))
}
impl Version {
pub fn from_code(code: i32) -> Option<Self> {
Some(match code {
0 => Self::V1_0,
1 => Self::V1_1,
2 => Self::V2_0,
3 => Self::V3_0,
_ => return None,
})
}
pub fn of(code: i32) -> Self {
Self::from_code(code).unwrap_or(Self::V1_1)
}
}
impl Role {
pub fn from_code(code: i32) -> Option<Self> {
Some(match code {
0 => Self::UserAgent,
1 => Self::Origin,
2 => Self::Proxy,
3 => Self::Gateway,
4 => Self::Tunnel,
_ => return None,
})
}
}
impl Method {
pub fn from_code(code: i32) -> Option<Self> {
Some(match code {
0 => Self::GET,
1 => Self::HEAD,
2 => Self::POST,
3 => Self::PUT,
4 => Self::DELETE,
5 => Self::CONNECT,
6 => Self::OPTIONS,
7 => Self::TRACE,
8 => Self::PATCH,
_ => return None,
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_version(message: *mut Message, version: i32) -> bool {
let (Some(message), Some(version)) = (unsafe { message.as_mut() }, Version::from_code(version)) else {
return false;
};
message.version = version;
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_method(message: *mut Message, method: i32) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.method = Method::from_code(method);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_target(message: *mut Message, target: *const u8, target_len: usize) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
if target.is_null() {
message.target = None;
return true;
}
let Some(target) = (unsafe { Slice::borrow_text(target, target_len) }) else {
return false;
};
message.target = Some(crate::helpers::text::Text::from_str(target));
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_status_code(message: *mut Message, status_code: i32) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.status_code = u16::try_from(status_code).ok();
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_set_connection_id(message: *mut Message, id: *const u8, id_len: usize) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.connection_id = unsafe { Slice::borrow(id, id_len) }.map(|id| crate::models::ConnectionID(Bytes::copy_from_slice(id)));
true
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BodyKind {
None = 0,
Data = 1,
Text = 2,
File = 3,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_body_kind(message: *const Message) -> BodyKind {
match unsafe { message.as_ref() }.and_then(|message| message.body.as_ref()) {
Some(Body::Data(_)) => BodyKind::Data,
Some(Body::Text(_)) => BodyKind::Text,
Some(Body::File(_)) => BodyKind::File,
None => BodyKind::None,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_body_is_empty(message: *const Message) -> bool {
unsafe { message.as_ref() }.and_then(|message| message.body.as_ref()).is_none_or(|body| body.is_empty())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_body_inline(message: *const Message) -> Slice {
match unsafe { message.as_ref() }.and_then(|message| message.body.as_ref()) {
Some(Body::Data(data)) => Slice::new(data),
Some(Body::Text(text)) => Slice::text(text),
_ => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_body_path(message: *const Message) -> Slice {
match unsafe { message.as_ref() }.and_then(|message| message.body.as_ref()) {
Some(Body::File(path)) => Slice::text(path),
_ => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_clear_body(message: *mut Message) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.body = None;
true
}