mirror of
https://github.com/tokio-rs/axum.git
synced 2025-10-01 06:51:05 +00: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
@ -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])
|
`IntoResponseParts` so `([("x-foo", "foo")], response)` now works ([#797])
|
||||||
- **breaking:** `InvalidJsonBody` has been replaced with `JsonDataError` to clearly signal that the
|
- **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
|
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
|
- **changed:** New `JsonSyntaxError` variant added to `JsonRejection`. This is returned when the
|
||||||
request body contains syntactically invalid JSON
|
request body contains syntactically invalid JSON
|
||||||
- **fixed:** Set `Allow` header when responding with `405 Method Not Allowed` ([#733])
|
- **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
|
[#827]: https://github.com/tokio-rs/axum/pull/827
|
||||||
[#841]: https://github.com/tokio-rs/axum/pull/841
|
[#841]: https://github.com/tokio-rs/axum/pull/841
|
||||||
[#842]: https://github.com/tokio-rs/axum/pull/842
|
[#842]: https://github.com/tokio-rs/axum/pull/842
|
||||||
|
[#879]: https://github.com/tokio-rs/axum/pull/879
|
||||||
|
|
||||||
# 0.4.4 (13. January, 2022)
|
# 0.4.4 (13. January, 2022)
|
||||||
|
|
||||||
|
@ -1,14 +1,52 @@
|
|||||||
//! Handler future types.
|
//! Handler future types.
|
||||||
|
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
use futures_util::future::{BoxFuture, Map};
|
use futures_util::future::Map;
|
||||||
use std::convert::Infallible;
|
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! {
|
opaque_future! {
|
||||||
/// The response future for [`IntoService`](super::IntoService).
|
/// The response future for [`IntoService`](super::IntoService).
|
||||||
pub type IntoServiceFuture =
|
pub type IntoServiceFuture<F> =
|
||||||
Map<
|
Map<
|
||||||
BoxFuture<'static, Response>,
|
F,
|
||||||
fn(Response) -> Result<Response, Infallible>,
|
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 Response = Response;
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
type Future = super::future::IntoServiceFuture;
|
type Future = super::future::IntoServiceFuture<H::Future>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
@ -74,7 +74,8 @@ where
|
|||||||
use futures_util::future::FutureExt;
|
use futures_util::future::FutureExt;
|
||||||
|
|
||||||
let handler = self.handler.clone();
|
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)
|
super::future::IntoServiceFuture::new(future)
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,8 @@ use crate::{
|
|||||||
routing::IntoMakeService,
|
routing::IntoMakeService,
|
||||||
BoxError,
|
BoxError,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use http::Request;
|
use http::Request;
|
||||||
use std::{fmt, future::Future, marker::PhantomData};
|
use std::{fmt, future::Future, marker::PhantomData, pin::Pin};
|
||||||
use tower::ServiceExt;
|
use tower::ServiceExt;
|
||||||
use tower_layer::Layer;
|
use tower_layer::Layer;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
@ -98,10 +97,12 @@ pub use self::into_service::IntoService;
|
|||||||
/// implemented to closures of the right types.
|
/// implemented to closures of the right types.
|
||||||
///
|
///
|
||||||
/// See the [module docs](crate::handler) for more details.
|
/// See the [module docs](crate::handler) for more details.
|
||||||
#[async_trait]
|
|
||||||
pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
|
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.
|
/// 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.
|
/// 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
|
impl<F, Fut, Res, B> Handler<(), B> for F
|
||||||
where
|
where
|
||||||
F: FnOnce() -> Fut + Clone + Send + 'static,
|
F: FnOnce() -> Fut + Clone + Send + 'static,
|
||||||
@ -255,14 +255,15 @@ where
|
|||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
B: Send + 'static,
|
B: Send + 'static,
|
||||||
{
|
{
|
||||||
async fn call(self, _req: Request<B>) -> Response {
|
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||||
self().await.into_response()
|
|
||||||
|
fn call(self, _req: Request<B>) -> Self::Future {
|
||||||
|
Box::pin(async move { self().await.into_response() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_handler {
|
macro_rules! impl_handler {
|
||||||
( $($ty:ident),* $(,)? ) => {
|
( $($ty:ident),* $(,)? ) => {
|
||||||
#[async_trait]
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
impl<F, Fut, B, Res, $($ty,)*> Handler<($($ty,)*), B> for F
|
impl<F, Fut, B, Res, $($ty,)*> Handler<($($ty,)*), B> for F
|
||||||
where
|
where
|
||||||
@ -272,19 +273,23 @@ macro_rules! impl_handler {
|
|||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
$( $ty: FromRequest<B> + Send,)*
|
$( $ty: FromRequest<B> + Send,)*
|
||||||
{
|
{
|
||||||
async fn call(self, req: Request<B>) -> Response {
|
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||||
let mut req = RequestParts::new(req);
|
|
||||||
|
|
||||||
$(
|
fn call(self, req: Request<B>) -> Self::Future {
|
||||||
let $ty = match $ty::from_request(&mut req).await {
|
Box::pin(async move {
|
||||||
Ok(value) => value,
|
let mut req = RequestParts::new(req);
|
||||||
Err(rejection) => return rejection.into_response(),
|
|
||||||
};
|
|
||||||
)*
|
|
||||||
|
|
||||||
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>
|
impl<S, T, ReqBody, ResBody> Handler<T, ReqBody> for Layered<S, T>
|
||||||
where
|
where
|
||||||
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
|
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
|
||||||
@ -329,11 +333,18 @@ where
|
|||||||
ResBody: HttpBody<Data = Bytes> + Send + 'static,
|
ResBody: HttpBody<Data = Bytes> + Send + 'static,
|
||||||
ResBody::Error: Into<BoxError>,
|
ResBody::Error: Into<BoxError>,
|
||||||
{
|
{
|
||||||
async fn call(self, req: Request<ReqBody>) -> Response {
|
type Future = future::LayeredFuture<S, ReqBody>;
|
||||||
match self.svc.oneshot(req).await {
|
|
||||||
Ok(res) => res.map(boxed),
|
fn call(self, req: Request<ReqBody>) -> Self::Future {
|
||||||
Err(res) => res.into_response(),
|
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…
x
Reference in New Issue
Block a user