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