use bytes::Bytes;
use crate::api::server::{Cluster, Handler, Server, ServerHandle};
use crate::ffi::api::client::parse_versions;
use crate::ffi::api::common::Limits;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::helpers::hsts::HstsPolicy;
use crate::ffi::models::Port;
use crate::ffi::websocket::WebSocket;
use crate::ffi::{borrow, Runtime, Slice};
use crate::models::{Body, Message, Version};
use crate::protocol::base::{AnyConnection, Connection, Transport};
use crate::tls::{EchKeys, Identity};
pub type OnRequest = extern "C" fn(context: *mut std::ffi::c_void, request: *mut Message) -> *mut Message;
pub type OnWebSocket = extern "C" fn(context: *mut std::ffi::c_void, socket: *mut WebSocket);
pub struct SendPtr<T: ?Sized>(pub *mut T);
unsafe impl<T: ?Sized> Send for SendPtr<T> {}
pub struct CallbackHandler {
pub on_request: OnRequest,
pub on_websocket: Option<OnWebSocket>,
pub context: *mut std::ffi::c_void,
}
unsafe impl Send for CallbackHandler {}
unsafe impl Sync for CallbackHandler {}
impl CallbackHandler {
pub async fn answer(&self, request: Message, version: Version) -> Message {
let stream_id = request.stream_id;
let callback = self.on_request;
let context = SendPtr(self.context);
let request = SendPtr(Box::into_raw(Box::new(request)));
let answered = tokio::task::spawn_blocking(move || {
let (context, request) = (context, request);
SendPtr(callback(context.0, request.0))
})
.await;
let mut response = match answered.ok().and_then(|response| std::ptr::NonNull::new(response.0)) {
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 self.on_websocket.is_some() && crate::websocket::upgrade_requested(&request) {
if crate::websocket::verify_upgrade(&request).is_err() {
if connection.send(crate::api::server::upgrade_required(&request, version)).await.is_err() {
break;
}
continue;
}
if let Ok(socket) = connection.accept_websocket(&request).await {
self.on_websocket(socket).await;
}
return;
}
if connection.send(self.answer(request, version).await).await.is_err() {
break;
}
if !connection.reusable() {
break;
}
}
connection.close().await;
}
async fn on_websocket(&self, socket: crate::websocket::WebSocketConnection<Box<dyn Transport>>) {
let Some(callback) = self.on_websocket else {
let mut socket = socket;
socket.close(crate::websocket::CloseCode::InternalError, "WebSocket is not configured").await;
return;
};
let handle = tokio::runtime::Handle::current();
let socket = SendPtr(Box::into_raw(Box::new(WebSocket { connection: socket, handle })));
let context = SendPtr(self.context);
let _ = tokio::task::spawn_blocking(move || {
let (context, socket) = (context, socket);
callback(context.0, socket.0)
})
.await;
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Rate {
pub period: f64,
pub count: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ServerLimits {
pub message: Limits,
pub max_connections: u32,
pub max_connections_per_ip: u32,
pub max_connection_rate: *const Rate,
pub rate_count: usize,
pub max_connection_history: usize,
}
impl ServerLimits {
pub unsafe fn parse(&self) -> crate::api::server::ServerLimits {
let mut rate = Vec::new();
if !self.max_connection_rate.is_null() {
for index in 0..self.rate_count {
let entry = unsafe { *self.max_connection_rate.add(index) };
rate.push((entry.period, entry.count));
}
}
crate::api::server::ServerLimits {
message: self.message.parse(),
max_connections: self.max_connections,
max_connections_per_ip: self.max_connections_per_ip,
max_connection_rate: rate,
max_connection_history: self.max_connection_history,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_server_limits_default() -> ServerLimits {
let limits = crate::api::server::ServerLimits::default();
ServerLimits {
message: Limits::build(&limits.message),
max_connections: limits.max_connections,
max_connections_per_ip: limits.max_connections_per_ip,
max_connection_rate: std::ptr::null(),
rate_count: 0,
max_connection_history: limits.max_connection_history,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_cores() -> u32 {
crate::api::server::cores() as u32
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ServerConfig {
pub versions: *const i32,
pub version_count: usize,
pub limits: *const ServerLimits,
pub identity: *const Identity,
pub certificate: Slice,
pub key: Slice,
pub ech: *const EchKeys,
pub hsts: *const HstsPolicy,
pub reuseport: bool,
}
impl ServerConfig {
pub const DEFAULT: Self = Self {
versions: std::ptr::null(),
version_count: 0,
limits: std::ptr::null(),
identity: std::ptr::null(),
certificate: Slice::ABSENT,
key: Slice::ABSENT,
ech: std::ptr::null(),
hsts: std::ptr::null(),
reuseport: true,
};
pub unsafe fn build(&self) -> Option<Server> {
let mut config = crate::api::server::ServerConfig { reuseport: self.reuseport, ..Default::default() };
let versions = unsafe { parse_versions(self.versions, self.version_count) }?;
if !versions.is_empty() {
config.versions = versions;
}
if let Some(limits) = unsafe { self.limits.as_ref() } {
config.limits = unsafe { limits.parse() };
}
if let Some(identity) = unsafe { self.identity.as_ref() } {
config.identity = Some(identity.clone());
} else if let (Some(certificate), Some(key)) = (
unsafe { borrow(self.certificate.data, self.certificate.len) },
unsafe { borrow(self.key.data, self.key.len) },
) {
config.identity = Some(Identity::new(vec![certificate.to_vec()], key.to_vec()));
}
if let Some(ech) = unsafe { self.ech.as_ref() } {
config.ech = Some(ech.clone());
}
if let Some(hsts) = unsafe { self.hsts.as_ref() } {
config.hsts = Some(hsts.parse());
}
Some(Server::new(config))
}
}
#[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);
match unsafe { config.build() } {
Some(server) => Box::into_raw(Box::new(server)),
None => std::ptr::null_mut(),
}
}
#[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 fn parse_ports(ports: *const Port, port_count: usize) -> Option<Vec<crate::models::Port>> {
if ports.is_null() {
return None;
}
let mut bound = Vec::with_capacity(port_count);
for index in 0..port_count {
bound.push(unsafe { (*ports.add(index)).parse() }?);
}
Some(bound)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_serve(runtime: *mut Runtime, server: *const Server, on_request: OnRequest, on_websocket: Option<OnWebSocket>, 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 out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(bound) = (unsafe { parse_ports(ports, port_count) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let handler = CallbackHandler { on_request, on_websocket, 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_address_count(handle: *const ServerHandle) -> usize {
unsafe { handle.as_ref() }.map_or(0, |handle| handle.addresses.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_handle_port_at(handle: *const ServerHandle, index: usize) -> u16 {
unsafe { handle.as_ref() }
.and_then(|handle| handle.addresses.get(index))
.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_server_run(server: *const Server, on_request: OnRequest, on_websocket: Option<OnWebSocket>, context: *mut std::ffi::c_void, ports: *const Port, port_count: usize, workers: u32, out: *mut *mut Cluster, error: *mut *mut ErrorHandle,) -> Status {
let Some(server) = (unsafe { server.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(bound) = (unsafe { parse_ports(ports, port_count) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let workers = match workers {
0 => crate::api::server::cores(),
count => count as usize,
};
let handler = CallbackHandler { on_request, on_websocket, context };
match server.run(handler, &bound, workers) {
Ok(cluster) => {
unsafe { *out = Box::into_raw(Box::new(cluster)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cluster_port(cluster: *const Cluster) -> u16 {
unsafe { cluster.as_ref() }.and_then(|cluster| cluster.address()).map_or(0, |address| address.port())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cluster_address_count(cluster: *const Cluster) -> usize {
unsafe { cluster.as_ref() }.map_or(0, |cluster| cluster.addresses().len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cluster_port_at(cluster: *const Cluster, index: usize) -> u16 {
unsafe { cluster.as_ref() }
.and_then(|cluster| cluster.addresses().get(index))
.map_or(0, |address| address.port())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cluster_workers(cluster: *const Cluster) -> u32 {
unsafe { cluster.as_ref() }.map_or(0, |cluster| cluster.workers() as u32)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cluster_close(cluster: *mut Cluster, timeout: f64) {
if cluster.is_null() {
return;
}
let cluster = *unsafe { Box::from_raw(cluster) };
cluster.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))
}