use core::ffi; use std::{ borrow::Cow, ffi::{CStr, CString}, ptr, str::FromStr, sync::Mutex, }; #[repr(C)] pub struct ModuleRequest<'req> { pub headers_len: ffi::c_ulong, pub headers: &'req [[*const ffi::c_char; 2]], pub body_len: ffi::c_ulong, 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) -> Self { let reqref = unsafe { request.as_ref() }.unwrap(); let mut headers = vec![]; for idx in 0..reqref.headers_len as usize { let kvarr = reqref.headers[idx as usize]; let k = unsafe { CStr::from_ptr(kvarr[0]) }.to_string_lossy(); let v = unsafe { CStr::from_ptr(kvarr[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 } } #[repr(C)] pub struct ModuleResponse { pub status: ffi::c_ushort, pub headers_len: ffi::c_ulong, pub headers: &'static [[*const ffi::c_char; 2]], pub body_len: ffi::c_ulong, pub body: *const u8, } pub static mut HEADERS: [[*const ffi::c_char; 2]; 64] = [[ptr::null(), ptr::null()]; 64]; static RESPONSE: Mutex> = Mutex::new(None); #[derive(Clone, Debug, PartialEq)] enum ResponseBody { Building(String), Built(CString), } impl ResponseBody { pub fn get_built(&mut self) -> &CString { match self { ResponseBody::Building(body) => { *self = ResponseBody::Built(CString::from_str(&body).unwrap()); if let ResponseBody::Built(bdy) = self { bdy } else { unreachable!() } } ResponseBody::Built(bdy) => bdy, } } } pub struct Response { headers: Vec<(CString, CString)>, body: ResponseBody, } impl Response { pub fn new() -> Self { Self { headers: vec![], body: ResponseBody::Building(String::new()), } } 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() { unsafe { HEADERS[idx][0] = key.as_ptr(); HEADERS[idx][1] = value.as_ptr(); } } let (body_len, body) = { let built = this.body.get_built(); (built.as_bytes().len() as u64, built.as_ptr() as *const u8) }; let headers_len = this.headers.len() as u64; let boxed = Box::new(ModuleResponse { status, headers_len, headers: unsafe { &HEADERS[..headers_len as usize] }, body_len, body, }); 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 push_str(&mut self, s: &str) { match self.body { ResponseBody::Building(ref mut body) => { body.push_str(s); } _ => (), } } 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); } }