use crate::logging::trace;
use crate::{Error, HttpRequest, HttpResponse};
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
pub trait Handler: Clone + Send + Sync + 'static {
type Future: Future<Output = Result<HttpResponse, Error>> + Send + 'static;
fn call(&self, req: HttpRequest) -> Self::Future;
}
pub trait IntoHandler<Args>: Clone + Send + Sync + 'static {
type Handler: Handler;
fn into_handler(self) -> Self::Handler;
}
#[derive(Clone)]
pub struct FnHandler<F> {
f: F,
}
impl<F> FnHandler<F> {
#[inline(always)]
pub fn new(f: F) -> Self {
Self { f }
}
}
impl<F, Fut> Handler for FnHandler<F>
where
F: Fn(HttpRequest) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
{
type Future = Fut;
#[inline(always)]
fn call(&self, req: HttpRequest) -> Self::Future {
(self.f)(req)
}
}
impl<F, Fut> IntoHandler<(HttpRequest,)> for F
where
F: Fn(HttpRequest) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
{
type Handler = FnHandler<F>;
#[inline(always)]
fn into_handler(self) -> Self::Handler {
FnHandler::new(self)
}
}
pub struct BoxedHandler {
inner: Arc<dyn ErasedHandler>,
}
impl BoxedHandler {
#[inline]
pub fn new<H: Handler>(handler: H) -> Self {
Self {
inner: Arc::new(HandlerWrapper {
handler,
_marker: PhantomData,
}),
}
}
#[inline(always)]
pub fn call(
&self,
req: HttpRequest,
) -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>> {
trace!(path = %req.path, method = %req.method, "Handler dispatch");
self.inner.call(req)
}
}
impl Clone for BoxedHandler {
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
trait ErasedHandler: Send + Sync {
fn call(
&self,
req: HttpRequest,
) -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
}
struct HandlerWrapper<H: Handler> {
handler: H,
_marker: PhantomData<fn() -> H::Future>,
}
unsafe impl<H: Handler> Send for HandlerWrapper<H> {}
unsafe impl<H: Handler> Sync for HandlerWrapper<H> {}
impl<H: Handler> ErasedHandler for HandlerWrapper<H> {
#[inline(always)]
fn call(
&self,
req: HttpRequest,
) -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>> {
Box::pin(self.handler.call(req))
}
}
pub type OptimizedHandlerFn = BoxedHandler;
#[inline]
pub fn handler<H, Args>(h: H) -> BoxedHandler
where
H: IntoHandler<Args>,
{
BoxedHandler::new(h.into_handler())
}
#[allow(clippy::type_complexity)]
pub type LegacyHandlerFn = Arc<
dyn Fn(HttpRequest) -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>
+ Send
+ Sync,
>;
#[inline]
pub fn from_legacy_handler(f: LegacyHandlerFn) -> BoxedHandler {
BoxedHandler::new(LegacyHandler { f })
}
#[derive(Clone)]
struct LegacyHandler {
f: LegacyHandlerFn,
}
impl Handler for LegacyHandler {
type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
#[inline(always)]
fn call(&self, req: HttpRequest) -> Self::Future {
(self.f)(req)
}
}
#[cfg(test)]
mod tests {
use super::*;
async fn test_handler(_req: HttpRequest) -> Result<HttpResponse, Error> {
Ok(HttpResponse::ok())
}
#[tokio::test]
async fn test_fn_handler() {
let handler = FnHandler::new(test_handler);
let req = HttpRequest::new("GET", "/test".to_string());
let response = handler.call(req).await.unwrap();
assert_eq!(response.status, 200);
}
#[tokio::test]
async fn test_into_handler() {
let handler = test_handler.into_handler();
let req = HttpRequest::new("GET", "/test".to_string());
let response = handler.call(req).await.unwrap();
assert_eq!(response.status, 200);
}
#[tokio::test]
async fn test_boxed_handler() {
let boxed = BoxedHandler::new(test_handler.into_handler());
let req = HttpRequest::new("GET", "/test".to_string());
let response = boxed.call(req).await.unwrap();
assert_eq!(response.status, 200);
}
#[tokio::test]
async fn test_handler_fn() {
let h = handler(test_handler);
let req = HttpRequest::new("GET", "/test".to_string());
let response = h.call(req).await.unwrap();
assert_eq!(response.status, 200);
}
#[tokio::test]
async fn test_clone_boxed_handler() {
let h1 = handler(test_handler);
let h2 = h1.clone();
let req1 = HttpRequest::new("GET", "/test".to_string());
let req2 = HttpRequest::new("GET", "/test".to_string());
let r1 = h1.call(req1).await.unwrap();
let r2 = h2.call(req2).await.unwrap();
assert_eq!(r1.status, 200);
assert_eq!(r2.status, 200);
}
#[test]
fn test_handler_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<BoxedHandler>();
}
}