use crate::api::client::Client;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::models::Port;
use crate::ffi::{borrow_text, Runtime};
use crate::models::{Message, Method, Url, Version};
use crate::protocol::common::{AnyConnection, Connection};
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ClientConfig {
pub version: i32,
pub secure: bool,
pub cookies: bool,
pub hsts: bool,
}
impl ClientConfig {
pub const DEFAULT: Self = Self { version: -1, secure: true, cookies: true, hsts: true };
pub fn version(&self) -> Option<Version> {
match self.version {
0 => Some(Version::V1_0),
1 => Some(Version::V1_1),
2 => Some(Version::V2_0),
3 => Some(Version::V3_0),
_ => None,
}
}
pub fn build(&self) -> Client {
let mut builder = Client::builder().secure(self.secure).cookies(self.cookies).hsts(self.hsts);
if let Some(version) = self.version() {
builder = builder.version(version);
}
builder.build()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_new(config: *const ClientConfig) -> *mut Client {
let config = unsafe { config.as_ref() }.copied().unwrap_or(ClientConfig::DEFAULT);
Box::into_raw(Box::new(config.build()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_free(client: *mut Client) {
if !client.is_null() {
drop(unsafe { Box::from_raw(client) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_fetch(
runtime: *mut Runtime,
client: *const Client,
method: Method,
url: *const u8,
url_len: usize,
request: *mut Message,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
let request = (!request.is_null()).then(|| *unsafe { Box::from_raw(request) });
let (Some(runtime), Some(client), Some(url)) =
(unsafe { runtime.as_ref() }, unsafe { client.as_ref() }, unsafe { borrow_text(url, url_len) })
else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let (headers, body) = match request {
Some(request) => (request.headers, request.body),
None => (None, None),
};
match runtime.0.block_on(client.fetch(method, url, headers, body)) {
Ok(response) => {
unsafe { *out = Box::into_raw(Box::new(response)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_get(
runtime: *mut Runtime,
client: *const Client,
url: *const u8,
url_len: usize,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
unsafe { soyokaze_client_fetch(runtime, client, Method::GET, url, url_len, std::ptr::null_mut(), out, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_head(
runtime: *mut Runtime,
client: *const Client,
url: *const u8,
url_len: usize,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
unsafe { soyokaze_client_fetch(runtime, client, Method::HEAD, url, url_len, std::ptr::null_mut(), out, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_post(
runtime: *mut Runtime,
client: *const Client,
url: *const u8,
url_len: usize,
request: *mut Message,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
unsafe { soyokaze_client_fetch(runtime, client, Method::POST, url, url_len, request, out, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_put(
runtime: *mut Runtime,
client: *const Client,
url: *const u8,
url_len: usize,
request: *mut Message,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
unsafe { soyokaze_client_fetch(runtime, client, Method::PUT, url, url_len, request, out, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_delete(
runtime: *mut Runtime,
client: *const Client,
url: *const u8,
url_len: usize,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
unsafe { soyokaze_client_fetch(runtime, client, Method::DELETE, url, url_len, std::ptr::null_mut(), out, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_open(
runtime: *mut Runtime,
client: *const Client,
url: *const Url,
out: *mut *mut AnyConnection,
error: *mut *mut ErrorHandle,
) -> Status {
let (Some(runtime), Some(client), Some(url)) =
(unsafe { runtime.as_ref() }, unsafe { client.as_ref() }, unsafe { url.as_ref() })
else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match runtime.0.block_on(client.open(url)) {
Ok(connection) => {
unsafe { *out = Box::into_raw(Box::new(connection)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_connect(
runtime: *mut Runtime,
client: *const Client,
host: *const u8,
host_len: usize,
port: *const Port,
out: *mut *mut AnyConnection,
error: *mut *mut ErrorHandle,
) -> Status {
let (Some(runtime), Some(client), Some(host), Some(port)) = (
unsafe { runtime.as_ref() },
unsafe { client.as_ref() },
unsafe { borrow_text(host, host_len) },
unsafe { port.as_ref() },
) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let Some(port) = (unsafe { port.parse() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match runtime.0.block_on(client.connect(host, port)) {
Ok(connection) => {
unsafe { *out = Box::into_raw(Box::new(connection)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_client_request(
runtime: *mut Runtime,
client: *const Client,
connection: *mut AnyConnection,
request: *mut Message,
out: *mut *mut Message,
error: *mut *mut ErrorHandle,
) -> Status {
if request.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let request = *unsafe { Box::from_raw(request) };
let (Some(runtime), Some(client), Some(connection)) =
(unsafe { runtime.as_ref() }, unsafe { client.as_ref() }, unsafe { connection.as_mut() })
else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match runtime.0.block_on(client.request(connection, request)) {
Ok(response) => {
unsafe { *out = Box::into_raw(Box::new(response)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_connection_version(connection: *const AnyConnection) -> Version {
unsafe { connection.as_ref() }.map_or(Version::V1_1, |connection| connection.version())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_connection_reusable(connection: *const AnyConnection) -> bool {
unsafe { connection.as_ref() }.is_some_and(|connection| connection.reusable())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_connection_close(runtime: *mut Runtime, connection: *mut AnyConnection) {
if let (Some(runtime), Some(connection)) = (unsafe { runtime.as_ref() }, unsafe { connection.as_mut() }) {
runtime.0.block_on(connection.close());
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_connection_free(connection: *mut AnyConnection) {
if !connection.is_null() {
drop(unsafe { Box::from_raw(connection) });
}
}