use std::any::Any;
use std::panic::AssertUnwindSafe;
use std::ptr::NonNull;
use std::sync::{Arc, Mutex, MutexGuard, Once, Weak};
use libc::c_void;
use crate::error::{UnitError, UnitInitError, UnitResult};
use crate::nxt_unit::{
self, nxt_unit_ctx_t, nxt_unit_done, nxt_unit_init, nxt_unit_init_t, nxt_unit_request_done,
nxt_unit_request_info_t, nxt_unit_response_init, nxt_unit_run,
};
use crate::request::Request;
unsafe extern "C" fn request_handler(req: *mut nxt_unit_request_info_t) {
let context_data = (*(*req).ctx).data as *mut ContextData;
let context_data = &mut *context_data;
let rc = nxt_unit_response_init(req, 200, 1, 0 as u32);
if rc != nxt_unit::NXT_UNIT_OK as i32 {
nxt_unit_request_done(req, rc);
return;
}
let rc = if let Some(service) = &mut context_data.request_handler {
let unit_request = Request {
nxt_request: &mut *req,
_lifetime: Default::default(),
};
let handler = AssertUnwindSafe(|| service.handle_request(unit_request));
match std::panic::catch_unwind(handler) {
Ok(Ok(())) => nxt_unit::NXT_UNIT_OK as i32,
Ok(Err(UnitError(rc))) => rc,
Err(panic_payload) => {
nxt_unit_request_done(req, nxt_unit::NXT_UNIT_ERROR as i32);
std::panic::resume_unwind(panic_payload)
}
}
} else {
nxt_unit::NXT_UNIT_OK as i32
};
nxt_unit_request_done(req, rc);
}
struct ContextData {
request_handler: Option<Box<dyn UnitService>>,
unit_is_ready: bool,
panic_payload: Option<Box<dyn Any + Send>>,
}
unsafe extern "C" fn ready_handler(ctx: *mut nxt_unit_ctx_t) -> i32 {
let context_data = (*ctx).data as *mut ContextData;
let context_data = &mut *context_data;
context_data.unit_is_ready = true;
nxt_unit::NXT_UNIT_OK as i32
}
static mut MAIN_CONTEXT: Option<Mutex<MainContext>> = None;
static MAIN_CONTEXT_INIT: Once = Once::new();
enum MainContext {
Uninitialized,
InitFailed(UnitInitError),
Initialized(Weak<UnitContextWrapper>),
}
fn main_context() -> MutexGuard<'static, MainContext> {
unsafe {
MAIN_CONTEXT_INIT.call_once(|| {
MAIN_CONTEXT = Some(Mutex::new(MainContext::Uninitialized));
});
MAIN_CONTEXT
.as_ref()
.expect("Initialized above")
.lock()
.expect("Main context should not be poisoned")
}
}
pub struct Unit {
context_wrapper: Option<Arc<UnitContextWrapper>>,
context_data: *mut ContextData,
}
impl Unit {
pub fn new() -> Result<Self, UnitInitError> {
let mut main_context = main_context();
let main_unit_context = match &*main_context {
MainContext::InitFailed(UnitInitError) => {
return Err(UnitInitError);
}
MainContext::Uninitialized => None,
MainContext::Initialized(main_unit_context) => {
match main_unit_context.upgrade() {
Some(context) => Some(context),
None => {
return Ok(Self {
context_wrapper: None,
context_data: std::ptr::null_mut(),
});
}
}
}
};
if let Some(main_unit_context) = main_unit_context {
let context_data = Box::new(ContextData {
request_handler: None,
unit_is_ready: false,
panic_payload: None,
});
let context_user_data = Box::into_raw(context_data);
let ctx = unsafe {
nxt_unit::nxt_unit_ctx_alloc(
main_unit_context.context.as_ptr(),
context_user_data as *mut c_void,
)
};
let ctx = match NonNull::new(ctx) {
Some(ctx) => ctx,
None => {
return Err(UnitInitError);
}
};
let context_wrapper = UnitContextWrapper {
parent_context: Some(main_unit_context.clone()),
context: ctx,
};
Ok(Self {
context_wrapper: Some(Arc::new(context_wrapper)),
context_data: context_user_data,
})
} else {
let context_data = Box::new(ContextData {
request_handler: None,
unit_is_ready: false,
panic_payload: None,
});
let context_user_data = Box::into_raw(context_data);
let ctx = unsafe {
let mut init: nxt_unit_init_t = std::mem::zeroed();
init.callbacks.request_handler = Some(request_handler);
init.callbacks.ready_handler = Some(ready_handler);
init.ctx_data = context_user_data as *mut c_void;
nxt_unit_init(&mut init)
};
let ctx = match NonNull::new(ctx) {
Some(ctx) => ctx,
None => {
*main_context = MainContext::InitFailed(UnitInitError);
return Err(UnitInitError);
}
};
loop {
let rc = unsafe { nxt_unit::nxt_unit_run_once(ctx.as_ptr()) };
if rc != nxt_unit::NXT_UNIT_OK as i32 {
*main_context = MainContext::InitFailed(UnitInitError);
return Err(UnitInitError);
}
unsafe {
let context_data = (*ctx.as_ptr()).data as *mut ContextData;
let context_data = &mut *context_data;
if context_data.unit_is_ready {
break;
}
}
}
let context_wrapper = Arc::new(UnitContextWrapper {
parent_context: None,
context: ctx,
});
*main_context = MainContext::Initialized(Arc::downgrade(&context_wrapper));
Ok(Self {
context_wrapper: Some(context_wrapper),
context_data: context_user_data,
})
}
}
fn context_data_mut(&mut self) -> &mut ContextData {
unsafe { &mut *self.context_data }
}
pub fn set_request_handler(&mut self, f: impl UnitService + 'static) {
if self.context_wrapper.is_none() {
return;
}
self.context_data_mut().request_handler = Some(Box::new(f))
}
pub fn run(&mut self) {
if let Some(context_wrapper) = &self.context_wrapper {
unsafe {
nxt_unit_run(context_wrapper.context.as_ptr());
}
if let Some(panic_payload) = self.context_data_mut().panic_payload.take() {
std::panic::resume_unwind(panic_payload);
}
}
}
}
struct UnitContextWrapper {
parent_context: Option<Arc<UnitContextWrapper>>,
context: NonNull<nxt_unit_ctx_t>,
}
impl Drop for UnitContextWrapper {
fn drop(&mut self) {
unsafe {
nxt_unit_done(self.context.as_ptr());
}
drop(self.parent_context.take());
}
}
impl Drop for Unit {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.context_data));
}
drop(self.context_wrapper.take());
}
}
pub trait UnitService {
fn handle_request(&mut self, req: Request) -> UnitResult<()>;
}
impl<F> UnitService for F
where
F: FnMut(Request) -> UnitResult<()> + 'static,
{
fn handle_request(&mut self, req: Request) -> UnitResult<()> {
self(req)
}
}