use bytes::Bytes;
use crate::api::server::{Handler, Server, ServerHandle};
use crate::api::tls::Identity;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::models::Port;
use crate::ffi::{borrow, Runtime};
use crate::models::{Body, Message, Version};
use crate::protocol::common::{AnyConnection, Connection};
pub type OnRequest = extern "C" fn(context: *mut std::ffi::c_void, request: *mut Message) -> *mut Message;
pub struct CallbackHandler {
pub on_request: OnRequest,
pub context: *mut std::ffi::c_void,
}
unsafe impl Send for CallbackHandler {}
unsafe impl Sync for CallbackHandler {}
impl CallbackHandler {
pub fn answer(&self, request: Message, version: Version) -> Message {
let stream_id = request.stream_id;
let response = (self.on_request)(self.context, Box::into_raw(Box::new(request)));
let mut response = match std::ptr::NonNull::new(response) {
Some(response) => *unsafe { Box::from_raw(response.as_ptr()) },
None => Message::response(500, version),
};
response.stream_id = stream_id;
response
}
}
impl Handler for CallbackHandler {
async fn on_connection(&self, connection: AnyConnection) {
let mut connection = connection;
let version = connection.version();
loop {
let Ok(request) = connection.receive().await else {
break;
};
if connection.send(self.answer(request, version)).await.is_err() {
break;
}
if !connection.reusable() {
break;
}
}
connection.close().await;
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ServerConfig {
pub certificate: *const u8,
pub certificate_len: usize,
pub key: *const u8,
pub key_len: usize,
pub max_connections: u32,
pub max_connections_per_ip: u32,
pub reuseport: bool,
}
impl ServerConfig {
pub const DEFAULT: Self = Self {
certificate: std::ptr::null(),
certificate_len: 0,
key: std::ptr::null(),
key_len: 0,
max_connections: 0,
max_connections_per_ip: 0,
reuseport: true,
};
pub unsafe fn build(&self) -> Server {
let mut builder = Server::builder()
.max_connections(self.max_connections)
.max_connections_per_ip(self.max_connections_per_ip)
.reuseport(self.reuseport);
if let (Some(certificate), Some(key)) =
(unsafe { borrow(self.certificate, self.certificate_len) }, unsafe { borrow(self.key, self.key_len) })
{
builder = builder.with_identity(Identity::new(vec![certificate.to_vec()], key.to_vec()));
}
builder.build()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_new(config: *const ServerConfig) -> *mut Server {
let config = unsafe { config.as_ref() }.copied().unwrap_or(ServerConfig::DEFAULT);
Box::into_raw(Box::new(unsafe { config.build() }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_free(server: *mut Server) {
if !server.is_null() {
drop(unsafe { Box::from_raw(server) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_serve(
runtime: *mut Runtime,
server: *const Server,
on_request: OnRequest,
context: *mut std::ffi::c_void,
ports: *const Port,
port_count: usize,
out: *mut *mut ServerHandle,
error: *mut *mut ErrorHandle,
) -> Status {
let (Some(runtime), Some(server)) = (unsafe { runtime.as_ref() }, unsafe { server.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if ports.is_null() || out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let mut bound = Vec::with_capacity(port_count);
for index in 0..port_count {
let Some(port) = (unsafe { (*ports.add(index)).parse() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
bound.push(port);
}
let handler = CallbackHandler { on_request, context };
match runtime.0.block_on(server.serve(handler, &bound)) {
Ok(handle) => {
unsafe { *out = Box::into_raw(Box::new(handle)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_handle_port(handle: *const ServerHandle) -> u16 {
unsafe { handle.as_ref() }.and_then(|handle| handle.address()).map_or(0, |address| address.port())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_handle_close(runtime: *mut Runtime, handle: *mut ServerHandle, timeout: f64) {
if handle.is_null() {
return;
}
let handle = *unsafe { Box::from_raw(handle) };
if let Some(runtime) = unsafe { runtime.as_ref() } {
runtime.0.block_on(handle.close((timeout >= 0.0).then_some(timeout)));
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_response_with_body(
status_code: u16,
version: Version,
body: *const u8,
body_len: usize,
) -> *mut Message {
let mut response = Message::response(status_code, version);
if let Some(body) = unsafe { borrow(body, body_len) } {
response.body = Some(Body::Data(Bytes::copy_from_slice(body)));
}
Box::into_raw(Box::new(response))
}