From 3cd4a1d6a67a2101057027be84dc4b47c7100ac2 Mon Sep 17 00:00:00 2001 From: David Pedersen <david.pdrsn@gmail.com> Date: Tue, 6 Jul 2021 09:40:25 +0200 Subject: [PATCH] Fix for service as bottom handler (#27) Would previously fail because of a mismatch in error types. --- src/handler/mod.rs | 2 +- src/lib.rs | 4 ++-- src/routing.rs | 42 +++++++++++++++++++++++++++++++----------- src/service/mod.rs | 27 +++++++++++++++------------ src/tests.rs | 11 +++++++++++ 5 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index c015672a..e5fd600b 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -206,7 +206,7 @@ where OnMethod { method, svc: handler.into_service(), - fallback: EmptyRouter, + fallback: EmptyRouter::new(), } } diff --git a/src/lib.rs b/src/lib.rs index 592abbd6..bcd64258 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -679,13 +679,13 @@ pub mod prelude { /// # Panics /// /// Panics if `description` doesn't start with `/`. -pub fn route<S, B>(description: &str, service: S) -> Route<S, EmptyRouter> +pub fn route<S, B>(description: &str, service: S) -> Route<S, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { use routing::RoutingDsl; - routing::EmptyRouter.route(description, service) + routing::EmptyRouter::new().route(description, service) } mod sealed { diff --git a/src/routing.rs b/src/routing.rs index fee6274f..d64aa2c7 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -12,6 +12,7 @@ use std::{ convert::Infallible, fmt, future::Future, + marker::PhantomData, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -362,17 +363,36 @@ fn insert_url_params<B>(req: &mut Request<B>, params: Vec<(String, String)>) { /// /// This is used as the bottom service in a router stack. You shouldn't have to /// use to manually. -#[derive(Debug, Clone, Copy)] -pub struct EmptyRouter; +pub struct EmptyRouter<E = Infallible>(PhantomData<fn() -> E>); -impl RoutingDsl for EmptyRouter {} +impl<E> EmptyRouter<E> { + pub(crate) fn new() -> Self { + Self(PhantomData) + } +} -impl crate::sealed::Sealed for EmptyRouter {} +impl<E> Clone for EmptyRouter<E> { + fn clone(&self) -> Self { + Self(PhantomData) + } +} -impl<B> Service<Request<B>> for EmptyRouter { +impl<E> Copy for EmptyRouter<E> {} + +impl<E> fmt::Debug for EmptyRouter<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("EmptyRouter").finish() + } +} + +impl<E> RoutingDsl for EmptyRouter<E> {} + +impl<E> crate::sealed::Sealed for EmptyRouter<E> {} + +impl<B, E> Service<Request<B>> for EmptyRouter<E> { type Response = Response<BoxBody>; - type Error = Infallible; - type Future = EmptyRouterFuture; + type Error = E; + type Future = EmptyRouterFuture<E>; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { Poll::Ready(Ok(())) @@ -387,8 +407,8 @@ impl<B> Service<Request<B>> for EmptyRouter { opaque_future! { /// Response future for [`EmptyRouter`]. - pub type EmptyRouterFuture = - future::Ready<Result<Response<BoxBody>, Infallible>>; + pub type EmptyRouterFuture<E> = + future::Ready<Result<Response<BoxBody>, E>>; } #[derive(Debug, Clone)] @@ -744,14 +764,14 @@ where /// If necessary you can use [`RoutingDsl::boxed`] to box a group of routes /// making the type easier to name. This is sometimes useful when working with /// `nest`. -pub fn nest<S, B>(description: &str, svc: S) -> Nested<S, EmptyRouter> +pub fn nest<S, B>(description: &str, svc: S) -> Nested<S, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { Nested { pattern: PathPattern::new(description), svc, - fallback: EmptyRouter, + fallback: EmptyRouter::new(), } } diff --git a/src/service/mod.rs b/src/service/mod.rs index 52c9c4d1..954ab403 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -109,7 +109,7 @@ pub mod future; /// Route requests to the given service regardless of the HTTP method. /// /// See [`get`] for an example. -pub fn any<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn any<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -119,7 +119,7 @@ where /// Route `CONNECT` requests to the given service. /// /// See [`get`] for an example. -pub fn connect<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn connect<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -129,7 +129,7 @@ where /// Route `DELETE` requests to the given service. /// /// See [`get`] for an example. -pub fn delete<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn delete<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -156,7 +156,7 @@ where /// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn get<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn get<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -166,7 +166,7 @@ where /// Route `HEAD` requests to the given service. /// /// See [`get`] for an example. -pub fn head<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn head<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -176,7 +176,7 @@ where /// Route `OPTIONS` requests to the given service. /// /// See [`get`] for an example. -pub fn options<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn options<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -186,7 +186,7 @@ where /// Route `PATCH` requests to the given service. /// /// See [`get`] for an example. -pub fn patch<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn patch<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -196,7 +196,7 @@ where /// Route `POST` requests to the given service. /// /// See [`get`] for an example. -pub fn post<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn post<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -206,7 +206,7 @@ where /// Route `PUT` requests to the given service. /// /// See [`get`] for an example. -pub fn put<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn put<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -216,7 +216,7 @@ where /// Route `TRACE` requests to the given service. /// /// See [`get`] for an example. -pub fn trace<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn trace<S, B>(svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -243,7 +243,10 @@ where /// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn on<S, B>(method: MethodFilter, svc: S) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter> +pub fn on<S, B>( + method: MethodFilter, + svc: S, +) -> OnMethod<BoxResponseBody<S, B>, EmptyRouter<S::Error>> where S: Service<Request<B>> + Clone, { @@ -253,7 +256,7 @@ where inner: svc, _request_body: PhantomData, }, - fallback: EmptyRouter, + fallback: EmptyRouter::new(), } } diff --git a/src/tests.rs b/src/tests.rs index fc0e588e..fc8b9646 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -653,6 +653,17 @@ async fn different_request_body_types() { assert_eq!(body, "foo"); } +#[tokio::test] +async fn service_in_bottom() { + async fn handler(_req: Request<hyper::Body>) -> Result<Response<hyper::Body>, hyper::Error> { + Ok(Response::new(hyper::Body::empty())) + } + + let app = route("/", service::get(service_fn(handler))); + + run_in_background(app).await; +} + /// Run a `tower::Service` in the background and get a URI for it. async fn run_in_background<S, ResBody>(svc: S) -> SocketAddr where