mirror of
https://github.com/tokio-rs/axum.git
synced 2024-12-29 15:49:16 +01:00
Change Handler
to have an associated Future
type (#879)
* Change `Handler` to have an associated `Future` type This removes `#[async_trait]` from `Handler` and replaces that with an associated `Future` type. As hinted at in #878 I'm working on something with types that need to implement `Handler`. I'm doing that by wrapping other `Handler` types so I can implement `Handler` by simply delegating and thus don't need to allocate another box for `#[async_trait]`. This change makes that possible. It does make `Handler` less ergonomic to implement but thats a very niche feature so I'm fine with that. It wouldn't be appropriate for `FromRequest` IMO. * changelog
This commit is contained in:
parent
6175f95f41
commit
56e2d57320
4 changed files with 84 additions and 30 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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<F> =
|
||||
Map<
|
||||
BoxFuture<'static, Response>,
|
||||
F,
|
||||
fn(Response) -> Result<Response, Infallible>,
|
||||
>;
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// The response future for [`Layered`](super::Layered).
|
||||
pub struct LayeredFuture<S, ReqBody>
|
||||
where
|
||||
S: Service<Request<ReqBody>>,
|
||||
{
|
||||
#[pin]
|
||||
inner: Map<Oneshot<S, Request<ReqBody>>, fn(Result<S::Response, S::Error>) -> Response>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, ReqBody> LayeredFuture<S, ReqBody>
|
||||
where
|
||||
S: Service<Request<ReqBody>>,
|
||||
{
|
||||
pub(super) fn new(
|
||||
inner: Map<Oneshot<S, Request<ReqBody>>, fn(Result<S::Response, S::Error>) -> Response>,
|
||||
) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, ReqBody> Future for LayeredFuture<S, ReqBody>
|
||||
where
|
||||
S: Service<Request<ReqBody>>,
|
||||
{
|
||||
type Output = Response;
|
||||
|
||||
#[inline]
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll<Self::Output> {
|
||||
self.project().inner.poll(cx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ where
|
|||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
type Future = super::future::IntoServiceFuture;
|
||||
type Future = super::future::IntoServiceFuture<H::Future>;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<T, B = Body>: Clone + Send + Sized + 'static {
|
||||
/// The type of future calling this handler returns.
|
||||
type Future: Future<Output = Response> + Send + 'static;
|
||||
|
||||
/// Call the handler with the given request.
|
||||
async fn call(self, req: Request<B>) -> Response;
|
||||
fn call(self, req: Request<B>) -> Self::Future;
|
||||
|
||||
/// Apply a [`tower::Layer`] to the handler.
|
||||
///
|
||||
|
@ -247,7 +248,6 @@ pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<F, Fut, Res, B> 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<B>) -> Response {
|
||||
self().await.into_response()
|
||||
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
|
||||
fn call(self, _req: Request<B>) -> Self::Future {
|
||||
Box::pin(async move { self().await.into_response() })
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_handler {
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
#[async_trait]
|
||||
#[allow(non_snake_case)]
|
||||
impl<F, Fut, B, Res, $($ty,)*> Handler<($($ty,)*), B> for F
|
||||
where
|
||||
|
@ -272,19 +273,23 @@ macro_rules! impl_handler {
|
|||
Res: IntoResponse,
|
||||
$( $ty: FromRequest<B> + Send,)*
|
||||
{
|
||||
async fn call(self, req: Request<B>) -> Response {
|
||||
let mut req = RequestParts::new(req);
|
||||
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
|
||||
$(
|
||||
let $ty = match $ty::from_request(&mut req).await {
|
||||
Ok(value) => value,
|
||||
Err(rejection) => return rejection.into_response(),
|
||||
};
|
||||
)*
|
||||
fn call(self, req: Request<B>) -> 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<S, T, ReqBody, ResBody> Handler<T, ReqBody> for Layered<S, T>
|
||||
where
|
||||
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
|
||||
|
@ -329,11 +333,18 @@ where
|
|||
ResBody: HttpBody<Data = Bytes> + Send + 'static,
|
||||
ResBody::Error: Into<BoxError>,
|
||||
{
|
||||
async fn call(self, req: Request<ReqBody>) -> Response {
|
||||
match self.svc.oneshot(req).await {
|
||||
Ok(res) => res.map(boxed),
|
||||
Err(res) => res.into_response(),
|
||||
}
|
||||
type Future = future::LayeredFuture<S, ReqBody>;
|
||||
|
||||
fn call(self, req: Request<ReqBody>) -> Self::Future {
|
||||
use futures_util::future::{FutureExt, Map};
|
||||
|
||||
let future: Map<_, fn(Result<S::Response, S::Error>) -> _> =
|
||||
self.svc.oneshot(req).map(|result| match result {
|
||||
Ok(res) => res.map(boxed),
|
||||
Err(res) => res.into_response(),
|
||||
});
|
||||
|
||||
future::LayeredFuture::new(future)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue