use std::{ borrow::Cow, ffi::{CStr, c_void}, }; 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, /// The etc field is not read by corgi and exists so you may /// associate data with a request pub etc: *const ffi::c_void, } } 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 } } pub struct Response { headers: Vec<(Cow<'static, CStr>, Cow<'static, CStr>)>, ffi_headers: Vec<[*const i8; 2]>, body: Vec, } impl Response { pub fn new() -> Self { Self { headers: vec![], ffi_headers: vec![], body: vec![], } } pub fn into_mod_response(self, status: u16) -> *const ModuleResponse { let mut boxed_self = Box::new(self); for (key, value) in boxed_self.headers.iter() { let ffi_pair = [key.as_ptr(), value.as_ptr()]; boxed_self.ffi_headers.push(ffi_pair); } let boxed = Box::new(ModuleResponse { status, headers_len: boxed_self.ffi_headers.len() as u64, headers: boxed_self.ffi_headers.as_ptr(), body_len: boxed_self.body.len() as u64, body: boxed_self.body.as_ptr(), etc: Box::::into_raw(boxed_self) as *const c_void, }); 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 boxed = unsafe { Box::from_raw(response as *mut ModuleResponse) }; let etc = unsafe { Box::from_raw(boxed.etc as *mut Response) }; drop(boxed); drop(etc); } }