mirror of
https://github.com/tokio-rs/axum.git
synced 2025-03-08 08:56:24 +01:00
Reduce size of response body types (#11)
Wrapping everything in `crate::body::Either` wasn't actually necessary ans probably causes large body types. You can instead box the bodies in the leaf services.
This commit is contained in:
parent
7a051eb2d0
commit
59944c231f
6 changed files with 126 additions and 189 deletions
113
src/body.rs
113
src/body.rs
|
@ -1,8 +1,7 @@
|
|||
//! HTTP body utilities.
|
||||
|
||||
use bytes::Bytes;
|
||||
use http_body::{Empty, Full, SizeHint};
|
||||
use pin_project::pin_project;
|
||||
use http_body::{Empty, Full};
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt,
|
||||
|
@ -34,6 +33,16 @@ impl BoxBody {
|
|||
inner: Box::pin(body.map_err(|error| BoxStdError(error.into()))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> Self {
|
||||
Self::new(Empty::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BoxBody {
|
||||
fn default() -> Self {
|
||||
BoxBody::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BoxBody {
|
||||
|
@ -96,103 +105,3 @@ impl fmt::Display for BoxStdError {
|
|||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type that combines two body types into one.
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct Or<A, B>(#[pin] Either<A, B>);
|
||||
|
||||
impl<A, B> Or<A, B> {
|
||||
#[inline]
|
||||
pub(crate) fn a(a: A) -> Self {
|
||||
Or(Either::A(a))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn b(b: B) -> Self {
|
||||
Or(Either::B(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Default for Or<A, B> {
|
||||
fn default() -> Self {
|
||||
Self(Either::Empty(Empty::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project(project = EitherProj)]
|
||||
#[derive(Debug)]
|
||||
enum Either<A, B> {
|
||||
Empty(Empty<Bytes>), // required for `Default`
|
||||
A(#[pin] A),
|
||||
B(#[pin] B),
|
||||
}
|
||||
|
||||
impl<A, B> http_body::Body for Or<A, B>
|
||||
where
|
||||
A: http_body::Body<Data = Bytes>,
|
||||
A::Error: Into<BoxError>,
|
||||
B: http_body::Body<Data = Bytes>,
|
||||
B::Error: Into<BoxError>,
|
||||
{
|
||||
type Data = Bytes;
|
||||
type Error = BoxStdError;
|
||||
|
||||
#[inline]
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
match self.project().0.project() {
|
||||
EitherProj::Empty(inner) => Pin::new(inner).poll_data(cx).map(map_option_error),
|
||||
EitherProj::A(inner) => inner.poll_data(cx).map(map_option_error),
|
||||
EitherProj::B(inner) => inner.poll_data(cx).map(map_option_error),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
|
||||
match self.project().0.project() {
|
||||
EitherProj::Empty(inner) => Pin::new(inner)
|
||||
.poll_trailers(cx)
|
||||
.map_err(Into::into)
|
||||
.map_err(BoxStdError),
|
||||
EitherProj::A(inner) => inner
|
||||
.poll_trailers(cx)
|
||||
.map_err(Into::into)
|
||||
.map_err(BoxStdError),
|
||||
EitherProj::B(inner) => inner
|
||||
.poll_trailers(cx)
|
||||
.map_err(Into::into)
|
||||
.map_err(BoxStdError),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
match &self.0 {
|
||||
Either::Empty(inner) => inner.size_hint(),
|
||||
Either::A(inner) => inner.size_hint(),
|
||||
Either::B(inner) => inner.size_hint(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_end_stream(&self) -> bool {
|
||||
match &self.0 {
|
||||
Either::Empty(inner) => inner.is_end_stream(),
|
||||
Either::A(inner) => inner.is_end_stream(),
|
||||
Either::B(inner) => inner.is_end_stream(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_option_error<T, E>(opt: Option<Result<T, E>>) -> Option<Result<T, BoxStdError>>
|
||||
where
|
||||
E: Into<BoxError>,
|
||||
{
|
||||
opt.map(|result| result.map_err(Into::<BoxError>::into).map_err(BoxStdError))
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
//! the [`extract`](crate::extract) module.
|
||||
|
||||
use crate::{
|
||||
body::{self, Body, BoxBody},
|
||||
body::{Body, BoxBody},
|
||||
extract::FromRequest,
|
||||
response::IntoResponse,
|
||||
routing::{EmptyRouter, MethodFilter, RouteFuture},
|
||||
|
@ -643,17 +643,12 @@ impl<S, F> OnMethod<S, F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, F, SB, FB> Service<Request<Body>> for OnMethod<S, F>
|
||||
impl<S, F> Service<Request<Body>> for OnMethod<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible> + Clone,
|
||||
|
||||
SB: http_body::Body<Data = Bytes>,
|
||||
SB::Error: Into<BoxError>,
|
||||
FB: http_body::Body<Data = Bytes>,
|
||||
FB::Error: Into<BoxError>,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
{
|
||||
type Response = Response<body::Or<SB, FB>>;
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
|
|
|
@ -479,10 +479,10 @@
|
|||
//! let app = route(
|
||||
//! // Any request to `/` goes to a service
|
||||
//! "/",
|
||||
//! service_fn(|_: Request<Body>| async {
|
||||
//! service::any(service_fn(|_: Request<Body>| async {
|
||||
//! let res = Response::new(Body::from("Hi from `GET /`"));
|
||||
//! Ok::<_, Infallible>(res)
|
||||
//! })
|
||||
//! }))
|
||||
//! ).route(
|
||||
//! // GET `/static/Cargo.toml` goes to a service from tower-http
|
||||
//! "/static/Cargo.toml",
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
//! Routing between [`Service`]s.
|
||||
|
||||
use crate::{
|
||||
body::{self, BoxBody},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use crate::{body::BoxBody, response::IntoResponse};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use futures_util::{future, ready};
|
||||
|
@ -294,17 +291,12 @@ impl<S, F> RoutingDsl for Route<S, F> {}
|
|||
|
||||
impl<S, F> crate::sealed::Sealed for Route<S, F> {}
|
||||
|
||||
impl<S, F, SB, FB> Service<Request<Body>> for Route<S, F>
|
||||
impl<S, F> Service<Request<Body>> for Route<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible> + Clone,
|
||||
|
||||
SB: http_body::Body<Data = Bytes>,
|
||||
SB::Error: Into<BoxError>,
|
||||
FB: http_body::Body<Data = Bytes>,
|
||||
FB::Error: Into<BoxError>,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
{
|
||||
type Response = Response<body::Or<SB, FB>>;
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
|
@ -357,26 +349,17 @@ where
|
|||
B(#[pin] Oneshot<F, Request<Body>>),
|
||||
}
|
||||
|
||||
impl<S, F, SB, FB> Future for RouteFuture<S, F>
|
||||
impl<S, F> Future for RouteFuture<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible>,
|
||||
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible>,
|
||||
|
||||
SB: http_body::Body<Data = Bytes>,
|
||||
SB::Error: Into<BoxError>,
|
||||
FB: http_body::Body<Data = Bytes>,
|
||||
FB::Error: Into<BoxError>,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible>,
|
||||
F: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible>,
|
||||
{
|
||||
type Output = Result<Response<body::Or<SB, FB>>, Infallible>;
|
||||
type Output = Result<Response<BoxBody>, Infallible>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.project().0.project() {
|
||||
RouteFutureInnerProj::A(inner) => inner
|
||||
.poll(cx)
|
||||
.map(|result| result.map(|res| res.map(body::Or::a))),
|
||||
RouteFutureInnerProj::B(inner) => inner
|
||||
.poll(cx)
|
||||
.map(|result| result.map(|res| res.map(body::Or::b))),
|
||||
RouteFutureInnerProj::A(inner) => inner.poll(cx),
|
||||
RouteFutureInnerProj::B(inner) => inner.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +389,7 @@ impl RoutingDsl for EmptyRouter {}
|
|||
impl crate::sealed::Sealed for EmptyRouter {}
|
||||
|
||||
impl Service<Request<Body>> for EmptyRouter {
|
||||
type Response = Response<Body>;
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = EmptyRouterFuture;
|
||||
|
||||
|
@ -415,7 +398,7 @@ impl Service<Request<Body>> for EmptyRouter {
|
|||
}
|
||||
|
||||
fn call(&mut self, _req: Request<Body>) -> Self::Future {
|
||||
let mut res = Response::new(Body::empty());
|
||||
let mut res = Response::new(BoxBody::empty());
|
||||
*res.status_mut() = StatusCode::NOT_FOUND;
|
||||
EmptyRouterFuture(future::ok(res))
|
||||
}
|
||||
|
@ -424,7 +407,7 @@ impl Service<Request<Body>> for EmptyRouter {
|
|||
opaque_future! {
|
||||
/// Response future for [`EmptyRouter`].
|
||||
pub type EmptyRouterFuture =
|
||||
future::Ready<Result<Response<Body>, Infallible>>;
|
||||
future::Ready<Result<Response<BoxBody>, Infallible>>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -786,17 +769,12 @@ impl<S, F> RoutingDsl for Nested<S, F> {}
|
|||
|
||||
impl<S, F> crate::sealed::Sealed for Nested<S, F> {}
|
||||
|
||||
impl<S, F, SB, FB> Service<Request<Body>> for Nested<S, F>
|
||||
impl<S, F> Service<Request<Body>> for Nested<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible> + Clone,
|
||||
|
||||
SB: http_body::Body<Data = Bytes>,
|
||||
SB::Error: Into<BoxError>,
|
||||
FB: http_body::Body<Data = Bytes>,
|
||||
FB::Error: Into<BoxError>,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
{
|
||||
type Response = Response<body::Or<SB, FB>>;
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
|
|
|
@ -84,25 +84,38 @@
|
|||
//! [load shed]: tower::load_shed
|
||||
|
||||
use crate::{
|
||||
body::{self, Body, BoxBody},
|
||||
body::{Body, BoxBody},
|
||||
response::IntoResponse,
|
||||
routing::{EmptyRouter, MethodFilter, RouteFuture},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures_util::ready;
|
||||
use http::{Request, Response};
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
future::Future,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::{util::Oneshot, BoxError, Service, ServiceExt as _};
|
||||
|
||||
pub mod future;
|
||||
|
||||
/// Route requests to the given service regardless of the HTTP method.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn any<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
on(MethodFilter::Any, svc)
|
||||
}
|
||||
|
||||
/// Route `CONNECT` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn connect<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn connect<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -112,7 +125,7 @@ where
|
|||
/// Route `DELETE` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn delete<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn delete<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -139,7 +152,7 @@ where
|
|||
///
|
||||
/// You can only add services who cannot fail (their error type must be
|
||||
/// [`Infallible`]). To gracefully handle errors see [`ServiceExt::handle_error`].
|
||||
pub fn get<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn get<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -149,7 +162,7 @@ where
|
|||
/// Route `HEAD` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn head<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn head<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -159,7 +172,7 @@ where
|
|||
/// Route `OPTIONS` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn options<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn options<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -169,7 +182,7 @@ where
|
|||
/// Route `PATCH` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn patch<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn patch<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -179,7 +192,7 @@ where
|
|||
/// Route `POST` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn post<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn post<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -189,7 +202,7 @@ where
|
|||
/// Route `PUT` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn put<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn put<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -199,7 +212,7 @@ where
|
|||
/// Route `TRACE` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn trace<S>(svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn trace<S>(svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -223,13 +236,13 @@ where
|
|||
/// // Requests to `POST /` will go to `service`.
|
||||
/// let app = route("/", service::on(MethodFilter::Post, service));
|
||||
/// ```
|
||||
pub fn on<S>(method: MethodFilter, svc: S) -> OnMethod<S, EmptyRouter>
|
||||
pub fn on<S>(method: MethodFilter, svc: S) -> OnMethod<BoxResponseBody<S>, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
OnMethod {
|
||||
method,
|
||||
svc,
|
||||
svc: BoxResponseBody(svc),
|
||||
fallback: EmptyRouter,
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +261,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// its HTTP method.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn any<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn any<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -258,7 +271,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `CONNECT` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn connect<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn connect<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -268,7 +281,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `DELETE` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn delete<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn delete<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -301,7 +314,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// You can only add services who cannot fail (their error type must be
|
||||
/// [`Infallible`]). To gracefully handle errors see
|
||||
/// [`ServiceExt::handle_error`].
|
||||
pub fn get<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn get<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -311,7 +324,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `HEAD` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn head<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn head<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -321,7 +334,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `OPTIONS` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn options<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn options<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -331,7 +344,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `PATCH` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn patch<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn patch<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -341,7 +354,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `POST` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn post<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn post<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -351,7 +364,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `PUT` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn put<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn put<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -361,7 +374,7 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// Chain an additional service that will only accept `TRACE` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn trace<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
pub fn trace<T>(self, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
|
@ -390,13 +403,13 @@ impl<S, F> OnMethod<S, F> {
|
|||
/// // Requests to `DELETE /` will go to `service`
|
||||
/// let app = route("/", service::on(MethodFilter::Delete, service));
|
||||
/// ```
|
||||
pub fn on<T>(self, method: MethodFilter, svc: T) -> OnMethod<T, Self>
|
||||
pub fn on<T>(self, method: MethodFilter, svc: T) -> OnMethod<BoxResponseBody<T>, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
OnMethod {
|
||||
method,
|
||||
svc,
|
||||
svc: BoxResponseBody(svc),
|
||||
fallback: self,
|
||||
}
|
||||
}
|
||||
|
@ -404,17 +417,12 @@ impl<S, F> OnMethod<S, F> {
|
|||
|
||||
// this is identical to `routing::OnMethod`'s implementation. Would be nice to find a way to clean
|
||||
// that up, but not sure its possible.
|
||||
impl<S, F, SB, FB> Service<Request<Body>> for OnMethod<S, F>
|
||||
impl<S, F> Service<Request<Body>> for OnMethod<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible> + Clone,
|
||||
|
||||
SB: http_body::Body<Data = Bytes>,
|
||||
SB::Error: Into<BoxError>,
|
||||
FB: http_body::Body<Data = Bytes>,
|
||||
FB::Error: Into<BoxError>,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
F: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
{
|
||||
type Response = Response<body::Or<SB, FB>>;
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
|
@ -541,3 +549,47 @@ pub trait ServiceExt<B>: Service<Request<Body>, Response = Response<B>> {
|
|||
}
|
||||
|
||||
impl<S, B> ServiceExt<B> for S where S: Service<Request<Body>, Response = Response<B>> {}
|
||||
|
||||
/// A [`Service`] that boxes response bodies.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BoxResponseBody<S>(S);
|
||||
|
||||
impl<S, B> Service<Request<Body>> for BoxResponseBody<S>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<B>, Error = Infallible> + Clone,
|
||||
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
B::Error: Into<BoxError> + Send + Sync + 'static,
|
||||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = BoxResponseBodyFuture<Oneshot<S, Request<Body>>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
let fut = self.0.clone().oneshot(req);
|
||||
BoxResponseBodyFuture(fut)
|
||||
}
|
||||
}
|
||||
|
||||
/// Response future for [`BoxResponseBody`].
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct BoxResponseBodyFuture<F>(#[pin] F);
|
||||
|
||||
impl<F, B> Future for BoxResponseBodyFuture<F>
|
||||
where
|
||||
F: Future<Output = Result<Response<B>, Infallible>>,
|
||||
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
B::Error: Into<BoxError> + Send + Sync + 'static,
|
||||
{
|
||||
type Output = Result<Response<BoxBody>, Infallible>;
|
||||
|
||||
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let res = ready!(self.project().0.poll(cx))?;
|
||||
let res = res.map(BoxBody::new);
|
||||
Poll::Ready(Ok(res))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,10 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::{routing::EmptyRouter, service::OnMethod};
|
||||
use crate::{
|
||||
routing::EmptyRouter,
|
||||
service::{BoxResponseBody, OnMethod},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use future::ResponseFuture;
|
||||
use futures_util::{sink::SinkExt, stream::StreamExt};
|
||||
|
@ -38,7 +41,7 @@ pub mod future;
|
|||
/// each connection.
|
||||
///
|
||||
/// See the [module docs](crate::ws) for more details.
|
||||
pub fn ws<F, Fut>(callback: F) -> OnMethod<WebSocketUpgrade<F>, EmptyRouter>
|
||||
pub fn ws<F, Fut>(callback: F) -> OnMethod<BoxResponseBody<WebSocketUpgrade<F>>, EmptyRouter>
|
||||
where
|
||||
F: FnOnce(WebSocket) -> Fut + Clone + Send + 'static,
|
||||
Fut: Future<Output = ()> + Send + 'static,
|
||||
|
|
Loading…
Add table
Reference in a new issue