#[cfg(not(feature = "compio"))]
use std::future::Future;
#[cfg(not(feature = "compio"))]
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use http::StatusCode;
#[cfg(not(feature = "compio"))]
use tako_rs_core::body::TakoBody;
#[cfg(not(feature = "compio"))]
use tako_rs_core::middleware::IntoMiddleware;
#[cfg(not(feature = "compio"))]
use tako_rs_core::middleware::Next;
use tako_rs_core::types::Request;
#[cfg(not(feature = "compio"))]
use tako_rs_core::types::Response;
pub type TimeoutDynamicFn = Arc<dyn Fn(&Request) -> Option<Duration> + Send + Sync + 'static>;
#[cfg_attr(feature = "compio", allow(dead_code))]
pub struct Timeout {
duration: Duration,
status: StatusCode,
dynamic: Option<TimeoutDynamicFn>,
}
impl Timeout {
pub fn new(duration: Duration) -> Self {
Self {
duration,
status: StatusCode::SERVICE_UNAVAILABLE,
dynamic: None,
}
}
pub fn status(mut self, status: StatusCode) -> Self {
self.status = status;
self
}
pub fn dynamic<F>(mut self, f: F) -> Self
where
F: Fn(&Request) -> Option<Duration> + Send + Sync + 'static,
{
self.dynamic = Some(Arc::new(f));
self
}
}
#[cfg(not(feature = "compio"))]
impl IntoMiddleware for Timeout {
fn into_middleware(
self,
) -> impl Fn(Request, Next) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
+ Clone
+ Send
+ Sync
+ 'static {
let default_duration = self.duration;
let status = self.status;
let dynamic = self.dynamic;
move |req: Request, next: Next| {
let dynamic = dynamic.clone();
Box::pin(async move {
let deadline = match dynamic.as_ref() {
Some(f) => f(&req),
None => Some(default_duration),
};
let fut = next.run(req);
match deadline {
Some(d) => match tokio::time::timeout(d, fut).await {
Ok(resp) => resp,
Err(_) => http::Response::builder()
.status(status)
.body(TakoBody::empty())
.expect("valid timeout response"),
},
None => fut.await,
}
})
}
}
}