#![doc = include_str!("../docs/handlers_intro.md")]
use crate::{
body::{box_body, BoxBody},
extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
FromRequest, RequestParts,
},
response::IntoResponse,
routing::IntoMakeService,
BoxError,
};
use async_trait::async_trait;
use bytes::Bytes;
use http::{Request, Response};
use std::{fmt, future::Future, marker::PhantomData};
use tower::ServiceExt;
use tower_layer::Layer;
use tower_service::Service;
pub mod future;
mod into_service;
pub use self::into_service::IntoService;
pub(crate) mod sealed {
#![allow(unreachable_pub, missing_docs, missing_debug_implementations)]
pub trait HiddenTrait {}
pub struct Hidden;
impl HiddenTrait for Hidden {}
}
#[async_trait]
pub trait Handler<B, T>: Clone + Send + Sized + 'static {
#[doc(hidden)]
type Sealed: sealed::HiddenTrait;
async fn call(self, req: Request<B>) -> Response<BoxBody>;
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
where
L: Layer<IntoService<Self, B, T>>,
{
Layered::new(layer.layer(self.into_service()))
}
fn into_service(self) -> IntoService<Self, B, T> {
IntoService::new(self)
}
fn into_make_service(self) -> IntoMakeService<IntoService<Self, B, T>> {
IntoMakeService::new(self.into_service())
}
fn into_make_service_with_connect_info<C, Target>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, B, T>, C>
where
C: Connected<Target>,
{
IntoMakeServiceWithConnectInfo::new(self.into_service())
}
}
#[async_trait]
impl<F, Fut, Res, B> Handler<B, ()> for F
where
F: FnOnce() -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
B: Send + 'static,
{
type Sealed = sealed::Hidden;
async fn call(self, _req: Request<B>) -> Response<BoxBody> {
self().await.into_response().map(box_body)
}
}
macro_rules! impl_handler {
( $($ty:ident),* $(,)? ) => {
#[async_trait]
#[allow(non_snake_case)]
impl<F, Fut, B, Res, $($ty,)*> Handler<B, ($($ty,)*)> for F
where
F: FnOnce($($ty,)*) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send,
B: Send + 'static,
Res: IntoResponse,
$( $ty: FromRequest<B> + Send,)*
{
type Sealed = sealed::Hidden;
async fn call(self, req: Request<B>) -> Response<BoxBody> {
let mut req = RequestParts::new(req);
$(
let $ty = match $ty::from_request(&mut req).await {
Ok(value) => value,
Err(rejection) => return rejection.into_response().map(box_body),
};
)*
let res = self($($ty,)*).await;
res.into_response().map(box_body)
}
}
};
}
impl_handler!(T1);
impl_handler!(T1, T2);
impl_handler!(T1, T2, T3);
impl_handler!(T1, T2, T3, T4);
impl_handler!(T1, T2, T3, T4, T5);
impl_handler!(T1, T2, T3, T4, T5, T6);
impl_handler!(T1, T2, T3, T4, T5, T6, T7);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
pub struct Layered<S, T> {
svc: S,
_input: PhantomData<fn() -> T>,
}
impl<S, T> fmt::Debug for Layered<S, T>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Layered").field("svc", &self.svc).finish()
}
}
impl<S, T> Clone for Layered<S, T>
where
S: Clone,
{
fn clone(&self) -> Self {
Self::new(self.svc.clone())
}
}
#[async_trait]
impl<S, T, ReqBody, ResBody> Handler<ReqBody, T> for Layered<S, T>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Error: IntoResponse,
S::Future: Send,
T: 'static,
ReqBody: Send + 'static,
ResBody: http_body::Body<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>,
{
type Sealed = sealed::Hidden;
async fn call(self, req: Request<ReqBody>) -> Response<BoxBody> {
match self
.svc
.oneshot(req)
.await
.map_err(IntoResponse::into_response)
{
Ok(res) => res.map(box_body),
Err(res) => res.map(box_body),
}
}
}
impl<S, T> Layered<S, T> {
pub(crate) fn new(svc: S) -> Self {
Self {
svc,
_input: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::*;
use http::StatusCode;
#[tokio::test]
async fn handler_into_service() {
async fn handle(body: String) -> impl IntoResponse {
format!("you said: {}", body)
}
let client = TestClient::new(handle.into_service());
let res = client.post("/").body("hi there!").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "you said: hi there!");
}
#[test]
fn traits() {
use crate::routing::MethodRouter;
use crate::test_helpers::*;
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
}
}