use bytes::Bytes;
use crate::errors::Error;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::{borrow, borrow_text, Buffer, Runtime, Slice};
use crate::models::{Body, Message, Method, 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: PortKind,
pub number: u16,
pub path: *const u8,
pub path_len: usize,
}
impl Port {
pub unsafe fn parse(&self) -> Option<crate::models::Port> {
match 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 { borrow_text(self.path, self.path_len) }?.to_owned())),
}
}
}
#[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 { 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: Version) -> *mut Message {
Box::into_raw(Box::new(Message::new(version)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_request(method: Method, target: *const u8, target_len: usize, version: Version) -> *mut Message {
match unsafe { borrow_text(target, target_len) } {
Some(target) => Box::into_raw(Box::new(Message::request(method, target, version))),
None => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_message_response(status_code: u16, version: Version) -> *mut Message {
Box::into_raw(Box::new(Message::response(status_code, 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.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 {
Slice::maybe(
unsafe { message.as_ref() }
.and_then(|message| message.headers.as_ref())
.and_then(|headers| headers.iter().nth(index))
.map(|(name, _)| name),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_header_value(message: *const Message, index: usize) -> Slice {
Slice::maybe(
unsafe { message.as_ref() }
.and_then(|message| message.headers.as_ref())
.and_then(|headers| headers.iter().nth(index))
.map(|(_, value)| value),
)
}
#[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 { 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 { borrow_text(name, name_len) }, unsafe { 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 { borrow_text(name, name_len) }, unsafe { 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 { 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 {
Slice::maybe(
unsafe { message.as_ref() }
.and_then(|message| message.trailers.as_ref())
.and_then(|trailers| trailers.iter().nth(index))
.map(|(name, _)| name),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_message_trailer_value(message: *const Message, index: usize) -> Slice {
Slice::maybe(
unsafe { message.as_ref() }
.and_then(|message| message.trailers.as_ref())
.and_then(|trailers| trailers.iter().nth(index))
.map(|(_, value)| value),
)
}
#[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 { 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 { borrow_text(name, name_len) }, unsafe { 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 { borrow_text(name, name_len) }, unsafe { 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 { 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_set_secure(message: *mut Message, secure: bool) -> bool {
let Some(message) = (unsafe { message.as_mut() }) else {
return false;
};
message.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.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.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.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.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.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.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.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 { 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 { 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 { 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)) },
}
}