diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 77064e03..8ffdbe6f 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -73,6 +73,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `IntoResponseParts` so `([("x-foo", "foo")], response)` now works ([#797]) - **breaking:** `InvalidJsonBody` has been replaced with `JsonDataError` to clearly signal that the request body was syntactically valid JSON but couldn't be deserialized into the target type +- **breaking:** `Handler` is no longer an `#[async_trait]` but instead has an + associated `Future` type. That allows users to build their own `Handler` types + without paying the cost of `#[async_trait]` ([#879]) - **changed:** New `JsonSyntaxError` variant added to `JsonRejection`. This is returned when the request body contains syntactically invalid JSON - **fixed:** Set `Allow` header when responding with `405 Method Not Allowed` ([#733]) @@ -104,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#827]: https://github.com/tokio-rs/axum/pull/827 [#841]: https://github.com/tokio-rs/axum/pull/841 [#842]: https://github.com/tokio-rs/axum/pull/842 +[#879]: https://github.com/tokio-rs/axum/pull/879 # 0.4.4 (13. January, 2022) diff --git a/axum/src/handler/future.rs b/axum/src/handler/future.rs index 6bd39b35..b8b3e4e8 100644 --- a/axum/src/handler/future.rs +++ b/axum/src/handler/future.rs @@ -1,14 +1,52 @@ //! Handler future types. use crate::response::Response; -use futures_util::future::{BoxFuture, Map}; -use std::convert::Infallible; +use futures_util::future::Map; +use http::Request; +use pin_project_lite::pin_project; +use std::{convert::Infallible, future::Future, pin::Pin, task::Context}; +use tower::util::Oneshot; +use tower_service::Service; opaque_future! { /// The response future for [`IntoService`](super::IntoService). - pub type IntoServiceFuture = + pub type IntoServiceFuture = Map< - BoxFuture<'static, Response>, + F, fn(Response) -> Result, >; } + +pin_project! { + /// The response future for [`Layered`](super::Layered). + pub struct LayeredFuture + where + S: Service>, + { + #[pin] + inner: Map>, fn(Result) -> Response>, + } +} + +impl LayeredFuture +where + S: Service>, +{ + pub(super) fn new( + inner: Map>, fn(Result) -> Response>, + ) -> Self { + Self { inner } + } +} + +impl Future for LayeredFuture +where + S: Service>, +{ + type Output = Response; + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll { + self.project().inner.poll(cx) + } +} diff --git a/axum/src/handler/into_service.rs b/axum/src/handler/into_service.rs index 1cacfa20..34f36b2d 100644 --- a/axum/src/handler/into_service.rs +++ b/axum/src/handler/into_service.rs @@ -60,7 +60,7 @@ where { type Response = Response; type Error = Infallible; - type Future = super::future::IntoServiceFuture; + type Future = super::future::IntoServiceFuture; #[inline] fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -74,7 +74,8 @@ where use futures_util::future::FutureExt; let handler = self.handler.clone(); - let future = Handler::call(handler, req).map(Ok::<_, Infallible> as _); + let future = Handler::call(handler, req); + let future = future.map(Ok as _); super::future::IntoServiceFuture::new(future) } diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index fc58831c..5818a70a 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -80,9 +80,8 @@ use crate::{ routing::IntoMakeService, BoxError, }; -use async_trait::async_trait; use http::Request; -use std::{fmt, future::Future, marker::PhantomData}; +use std::{fmt, future::Future, marker::PhantomData, pin::Pin}; use tower::ServiceExt; use tower_layer::Layer; use tower_service::Service; @@ -98,10 +97,12 @@ pub use self::into_service::IntoService; /// implemented to closures of the right types. /// /// See the [module docs](crate::handler) for more details. -#[async_trait] pub trait Handler: Clone + Send + Sized + 'static { + /// The type of future calling this handler returns. + type Future: Future + Send + 'static; + /// Call the handler with the given request. - async fn call(self, req: Request) -> Response; + fn call(self, req: Request) -> Self::Future; /// Apply a [`tower::Layer`] to the handler. /// @@ -247,7 +248,6 @@ pub trait Handler: Clone + Send + Sized + 'static { } } -#[async_trait] impl Handler<(), B> for F where F: FnOnce() -> Fut + Clone + Send + 'static, @@ -255,14 +255,15 @@ where Res: IntoResponse, B: Send + 'static, { - async fn call(self, _req: Request) -> Response { - self().await.into_response() + type Future = Pin + Send>>; + + fn call(self, _req: Request) -> Self::Future { + Box::pin(async move { self().await.into_response() }) } } macro_rules! impl_handler { ( $($ty:ident),* $(,)? ) => { - #[async_trait] #[allow(non_snake_case)] impl Handler<($($ty,)*), B> for F where @@ -272,19 +273,23 @@ macro_rules! impl_handler { Res: IntoResponse, $( $ty: FromRequest + Send,)* { - async fn call(self, req: Request) -> Response { - let mut req = RequestParts::new(req); + type Future = Pin + Send>>; - $( - let $ty = match $ty::from_request(&mut req).await { - Ok(value) => value, - Err(rejection) => return rejection.into_response(), - }; - )* + fn call(self, req: Request) -> Self::Future { + Box::pin(async move { + let mut req = RequestParts::new(req); - let res = self($($ty,)*).await; + $( + let $ty = match $ty::from_request(&mut req).await { + Ok(value) => value, + Err(rejection) => return rejection.into_response(), + }; + )* - res.into_response() + let res = self($($ty,)*).await; + + res.into_response() + }) } } }; @@ -318,7 +323,6 @@ where } } -#[async_trait] impl Handler for Layered where S: Service, Response = Response> + Clone + Send + 'static, @@ -329,11 +333,18 @@ where ResBody: HttpBody + Send + 'static, ResBody::Error: Into, { - async fn call(self, req: Request) -> Response { - match self.svc.oneshot(req).await { - Ok(res) => res.map(boxed), - Err(res) => res.into_response(), - } + type Future = future::LayeredFuture; + + fn call(self, req: Request) -> Self::Future { + use futures_util::future::{FutureExt, Map}; + + let future: Map<_, fn(Result) -> _> = + self.svc.oneshot(req).map(|result| match result { + Ok(res) => res.map(boxed), + Err(res) => res.into_response(), + }); + + future::LayeredFuture::new(future) } }