use crate::api::cluster::Cluster;
use crate::api::server::{Handler, Server, ServerHandle};
use crate::ffi::SendPtr;
use crate::ffi::models::Limits;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::hsts::HSTSPolicy;
use crate::ffi::models::Port;
use crate::ffi::tls::TLSConfig;
use crate::ffi::websocket::WebSocket;
use crate::ffi::{Runtime, Slice};
use crate::models::{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 CallbackHandler {
pub on_request: OnRequest,
pub on_websocket: Option<OnWebSocket>,
pub context: *mut std::ffi::c_void,
pub limits: crate::websocket::WebSocketLimits,
}
unsafe impl Send for CallbackHandler {}
unsafe impl Sync for CallbackHandler {}
pub struct Answered(pub *mut Message);
unsafe impl Send for Answered {}
impl Answered {
pub fn take(mut self) -> Option<Message> {
let handle = std::ptr::NonNull::new(self.0)?;
self.0 = std::ptr::null_mut();
Some(*unsafe { Box::from_raw(handle.as_ptr()) })
}
}
impl Drop for Answered {
fn drop(&mut self) {
if !self.0.is_null() {
drop(unsafe { Box::from_raw(self.0) });
}
}
}
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);
Answered(callback(context.0, request.0))
})
.await;
let mut response = match answered.ok().and_then(Answered::take) {
Some(response) => response,
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::Handshake::requested(&request) {
match crate::websocket::Handshake::answer(connection, &request, self.limits).await {
crate::websocket::Answer::Accepted(socket) => {
self.on_websocket(socket).await;
return;
}
crate::websocket::Answer::Refused(kept) => {
connection = kept;
continue;
}
crate::websocket::Answer::Failed => 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 backlog: u32,
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,
pub worker_stack_size: 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(),
backlog: self.backlog,
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,
worker_stack_size: self.worker_stack_size,
}
}
pub fn build(limits: &crate::api::server::ServerLimits) -> Self {
Self {
message: Limits::build(&limits.message),
backlog: limits.backlog,
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,
worker_stack_size: limits.worker_stack_size,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_server_limits_default() -> ServerLimits {
ServerLimits::build(&crate::api::server::ServerLimits::default())
}
#[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 tls: *const TLSConfig,
pub ech: *const ECHKeys,
pub hsts: *const HSTSPolicy,
pub reuseport: bool,
pub uds_mode: u32,
}
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,
tls: std::ptr::null(),
ech: std::ptr::null(),
hsts: std::ptr::null(),
reuseport: true,
uds_mode: 0o666,
};
pub unsafe fn build(&self) -> Option<Server> {
let mut config = crate::api::server::ServerConfig { reuseport: self.reuseport, uds_mode: self.uds_mode, ..Default::default() };
let versions = unsafe { Version::parse_all(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 { Slice::borrow(self.certificate.data, self.certificate.len) },
unsafe { Slice::borrow(self.key.data, self.key.len) },
) {
config.identity = Some(Identity::new(vec![certificate.to_vec()], key.to_vec()));
}
if let Some(tls) = unsafe { self.tls.as_ref() } {
config.tls = unsafe { tls.parse() }?;
}
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(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 { Port::parse_all(ports, port_count) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let handler = CallbackHandler { on_request, on_websocket, context, limits: server.config.limits.message.into() };
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 { Port::parse_all(ports, port_count) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let workers = match workers {
0 => Cluster::cores(),
count => count as usize,
};
let handler = CallbackHandler { on_request, on_websocket, context, limits: server.config.limits.message.into() };
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) },
}
}
pub use crate::api::server::RawSocket;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_open(server: *const Server, target: *const Port, out: *mut *mut RawSocket, error: *mut *mut ErrorHandle) -> Status {
let (Some(server), Some(target)) = (unsafe { server.as_ref() }, unsafe { target.as_ref() }.and_then(|target| unsafe { target.parse() })) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match server.open(&target) {
Ok(socket) => {
if !out.is_null() {
unsafe { *out = Box::into_raw(Box::new(socket)) };
}
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_raw_socket_free(socket: *mut RawSocket) {
if !socket.is_null() {
drop(unsafe { Box::from_raw(socket) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_raw_socket_address(socket: *const RawSocket) -> crate::ffi::Buffer {
match unsafe { socket.as_ref() }.and_then(|socket| socket.address().ok()) {
Some(address) => crate::ffi::Buffer::new(address.to_string().into_bytes()),
None => crate::ffi::Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_raw_socket_port(socket: *const RawSocket) -> u16 {
unsafe { socket.as_ref() }.and_then(|socket| socket.address().ok()).map_or(0, |address| address.port())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_raw_socket_share(socket: *const RawSocket, out: *mut *mut RawSocket, error: *mut *mut ErrorHandle) -> Status {
let Some(socket) = (unsafe { socket.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match socket.share() {
Ok(shared) => {
if !out.is_null() {
unsafe { *out = Box::into_raw(Box::new(shared)) };
}
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_raw_socket_descriptor(socket: *const RawSocket) -> i32 {
use std::os::fd::AsRawFd;
match unsafe { socket.as_ref() } {
Some(RawSocket::UDS(listener)) => listener.as_raw_fd(),
Some(RawSocket::TCP(listener)) => listener.as_raw_fd(),
Some(RawSocket::QUIC(socket)) => socket.as_raw_fd(),
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_version_count(server: *const Server) -> usize {
unsafe { server.as_ref() }.map_or(0, |server| server.config.versions.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_version_at(server: *const Server, index: usize) -> i32 {
match unsafe { server.as_ref() }.and_then(|server| server.config.versions.get(index)) {
Some(&version) => version as i32,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_reuseport(server: *const Server) -> bool {
unsafe { server.as_ref() }.is_some_and(|server| server.config.reuseport)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_uds_mode(server: *const Server) -> u32 {
unsafe { server.as_ref() }.map_or(0, |server| server.config.uds_mode)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_limits_gate(limits: *const ServerLimits) -> *mut crate::ffi::api::gate::GateHandle {
let limits = match unsafe { limits.as_ref() } {
Some(limits) => unsafe { limits.parse() },
None => crate::api::server::ServerLimits::default(),
};
Box::into_raw(Box::new(crate::ffi::api::gate::GateHandle(limits.gate())))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_server_handle_address_at(handle: *const ServerHandle, index: usize) -> crate::ffi::Buffer {
match unsafe { handle.as_ref() }.and_then(|handle| handle.addresses().get(index)) {
Some(address) => crate::ffi::Buffer::new(address.to_string().into_bytes()),
None => crate::ffi::Buffer::EMPTY,
}
}