use std::{borrow::Cow, ffi::CStr, ptr, sync::Mutex}; use ffi::{ModuleRequest, ModuleResponse}; pub mod ffi { use core::ffi; use std::marker::PhantomData; #[repr(C)] pub struct ModuleRequest<'req> { pub headers_len: u64, pub headers: *const [*const ffi::c_char; 2], pub body_len: u64, pub body: *const u8, pub _phantom: PhantomData<&'req u8>, } #[repr(C)] pub struct ModuleResponse { pub status: u16, pub headers_len: u64, pub headers: *const [*const ffi::c_char; 2], pub body_len: u64, pub body: *const u8, } } pub struct Request<'req> { headers: Vec<(Cow<'req, str>, Cow<'req, str>)>, body: Option<&'req [u8]>, } impl<'req> Request<'req> { pub fn from_mod_request(request: *const ModuleRequest<'req>) -> Self { // SAFTEY: corgi will never give us a null pointer let reqref = unsafe { request.as_ref() }.unwrap(); let headers_ffi = unsafe { std::slice::from_raw_parts(reqref.headers, reqref.headers_len as usize) }; let mut headers = vec![]; for pair in headers_ffi { let k = unsafe { CStr::from_ptr(pair[0]) }.to_string_lossy(); let v = unsafe { CStr::from_ptr(pair[1]) }.to_string_lossy(); headers.push((k, v)); } let body = if reqref.body.is_null() { None } else { Some(unsafe { std::slice::from_raw_parts(reqref.body, reqref.body_len as usize) }) }; Self { headers, body } } pub fn header(&self, key: &str) -> Option<&str> { for (hkey, hval) in &self.headers { if hkey == key { return Some(hval); } } None } pub fn headers(&self) -> &[(Cow, Cow)] { &self.headers } pub fn body(&self) -> Option<&[u8]> { self.body } } const HEADERS_LEN: usize = 64; static mut HEADERS: [[*const core::ffi::c_char; 2]; HEADERS_LEN] = [[ptr::null(), ptr::null()]; 64]; static RESPONSE: Mutex> = Mutex::new(None); pub struct Response { headers: Vec<(Cow<'static, CStr>, Cow<'static, CStr>)>, body: Vec, } impl Response { pub fn new() -> Self { Self { headers: vec![], body: vec![], } } pub fn into_mod_response(self, status: u16) -> *const ModuleResponse { let mut lock = RESPONSE.lock().unwrap(); *lock = Some(self); let this = lock.as_mut().unwrap(); for (idx, (key, value)) in this.headers.iter().enumerate().take(HEADERS_LEN) { unsafe { HEADERS[idx][0] = key.as_ptr(); HEADERS[idx][1] = value.as_ptr(); } } let headers_len = this.headers.len().min(HEADERS_LEN) as u64; let boxed = Box::new(ModuleResponse { status, headers_len, headers: unsafe { HEADERS[..headers_len as usize].as_ptr() }, body_len: this.body.len() as u64, body: this.body.as_ptr(), }); Box::::into_raw(boxed) } pub fn header>, V: Into>>( &mut self, key: K, value: V, ) -> &mut Self { self.headers.push((key.into(), value.into())); self } pub fn body(&mut self, vec: Vec) -> &mut Self { self.body = vec; self } pub fn cleanup(response: *const ModuleResponse) { let mut lock = RESPONSE.lock().unwrap(); match lock.take() { Some(response) => drop(response), None => (), } let boxed = unsafe { Box::from_raw(response as *mut ModuleResponse) }; drop(boxed); } }