From f8a0d81d79453c5364105361948bd439d9dd3c90 Mon Sep 17 00:00:00 2001
From: David Pedersen <david.pdrsn@gmail.com>
Date: Sat, 21 Aug 2021 15:01:30 +0200
Subject: [PATCH] Remove tower from axum's public API (#229)

Instead rely on `tower-service` and `tower-layer`. `tower` itself is
only used internally.

Fixes https://github.com/tokio-rs/axum/issues/186
---
 CHANGELOG.md                        |  4 +++
 Cargo.toml                          |  3 ++
 src/body.rs                         |  2 +-
 src/buffer.rs                       |  3 +-
 src/error.rs                        |  2 +-
 src/extract/connect_info.rs         |  2 +-
 src/extract/extractor_middleware.rs |  4 ++-
 src/extract/form.rs                 |  2 +-
 src/extract/multipart.rs            |  2 +-
 src/extract/rejection.rs            |  2 +-
 src/extract/request_parts.rs        |  2 +-
 src/handler/future.rs               |  3 +-
 src/handler/into_service.rs         |  2 +-
 src/handler/mod.rs                  |  5 +++-
 src/json.rs                         |  2 +-
 src/lib.rs                          |  3 ++
 src/macros.rs                       |  2 +-
 src/response/headers.rs             |  7 +++--
 src/response/mod.rs                 | 19 +-----------
 src/response/sse.rs                 |  2 +-
 src/routing/future.rs               | 12 ++++++--
 src/routing/mod.rs                  | 46 +++++++++++++++++++++++++----
 src/routing/or.rs                   |  3 +-
 src/service/future.rs               |  4 ++-
 src/service/mod.rs                  |  4 ++-
 src/tests/mod.rs                    |  4 ++-
 src/tests/or.rs                     |  6 ++--
 27 files changed, 102 insertions(+), 50 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81b8f8de..d0d0343e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,6 +47,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Convert `RequestAlreadyExtracted` to an enum with each possible error variant ([#167](https://github.com/tokio-rs/axum/pull/167))
 - `Router::check_infallible` now returns a `CheckInfallible` service. This
   is to improve compile times.
+- `Router::into_make_service` now returns `routing::IntoMakeService` rather than
+  `tower::make::Shared` ([#229](https://github.com/tokio-rs/axum/pull/229))
+- All usage of `tower::BoxError` has been replaced with `axum::BoxError` ([#229](https://github.com/tokio-rs/axum/pull/229))
+- `tower::util::Either` no longer implements `IntoResponse` ([#229](https://github.com/tokio-rs/axum/pull/229))
 - These future types have been moved
     - `extract::extractor_middleware::ExtractorMiddlewareResponseFuture` moved
       to `extract::extractor_middleware::future::ResponseFuture` ([#133](https://github.com/tokio-rs/axum/pull/133))
diff --git a/Cargo.toml b/Cargo.toml
index 20543eb0..c0accab0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,8 @@ serde_urlencoded = "0.7"
 tokio = { version = "1", features = ["time"] }
 tokio-util = "0.6"
 tower = { version = "0.4", default-features = false, features = ["util", "buffer", "make"] }
+tower-service = "0.3"
+tower-layer = "0.3"
 tower-http = { version = "0.1", features = ["add-extension", "map-response-body"] }
 sync_wrapper = "0.1.1"
 
@@ -56,6 +58,7 @@ uuid = { version = "0.8", features = ["serde", "v4"] }
 tokio-stream = "0.1"
 
 [dev-dependencies.tower]
+package = "tower"
 version = "0.4"
 features = [
     "util",
diff --git a/src/body.rs b/src/body.rs
index 677af2a7..e6e83e1e 100644
--- a/src/body.rs
+++ b/src/body.rs
@@ -1,7 +1,7 @@
 //! HTTP body utilities.
 
+use crate::BoxError;
 use crate::Error;
-use tower::BoxError;
 
 #[doc(no_inline)]
 pub use http_body::{Body as HttpBody, Empty, Full};
diff --git a/src/buffer.rs b/src/buffer.rs
index 4b85927c..f76b8fd4 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -8,7 +8,8 @@ use std::{
 };
 use tokio::sync::{mpsc, oneshot, OwnedSemaphorePermit, Semaphore};
 use tokio_util::sync::PollSemaphore;
-use tower::{Service, ServiceExt};
+use tower::ServiceExt;
+use tower_service::Service;
 
 /// A version of [`tower::buffer::Buffer`] which panicks on channel related errors, thus keeping
 /// the error type of the service.
diff --git a/src/error.rs b/src/error.rs
index c21a443c..cac48907 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,5 +1,5 @@
+use crate::BoxError;
 use std::{error::Error as StdError, fmt};
-use tower::BoxError;
 
 /// Errors that can happen when using axum.
 #[derive(Debug)]
diff --git a/src/extract/connect_info.rs b/src/extract/connect_info.rs
index 6fd23c0f..905d2336 100644
--- a/src/extract/connect_info.rs
+++ b/src/extract/connect_info.rs
@@ -14,8 +14,8 @@ use std::{
     net::SocketAddr,
     task::{Context, Poll},
 };
-use tower::Service;
 use tower_http::add_extension::AddExtension;
+use tower_service::Service;
 
 /// A [`MakeService`] created from a router.
 ///
diff --git a/src/extract/extractor_middleware.rs b/src/extract/extractor_middleware.rs
index 79351f84..806f37d6 100644
--- a/src/extract/extractor_middleware.rs
+++ b/src/extract/extractor_middleware.rs
@@ -3,6 +3,7 @@
 //! See [`extractor_middleware`] for more details.
 
 use super::{FromRequest, RequestParts};
+use crate::BoxError;
 use crate::{body::BoxBody, response::IntoResponse};
 use bytes::Bytes;
 use futures_util::{future::BoxFuture, ready};
@@ -15,7 +16,8 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
-use tower::{BoxError, Layer, Service};
+use tower_layer::Layer;
+use tower_service::Service;
 
 /// Convert an extractor into a middleware.
 ///
diff --git a/src/extract/form.rs b/src/extract/form.rs
index 1aa03240..fdc5564a 100644
--- a/src/extract/form.rs
+++ b/src/extract/form.rs
@@ -1,10 +1,10 @@
 use super::{has_content_type, rejection::*, take_body, FromRequest, RequestParts};
+use crate::BoxError;
 use async_trait::async_trait;
 use bytes::Buf;
 use http::Method;
 use serde::de::DeserializeOwned;
 use std::ops::Deref;
-use tower::BoxError;
 
 /// Extractor that deserializes `application/x-www-form-urlencoded` requests
 /// into some type.
diff --git a/src/extract/multipart.rs b/src/extract/multipart.rs
index 75afed76..b2b6b26f 100644
--- a/src/extract/multipart.rs
+++ b/src/extract/multipart.rs
@@ -3,6 +3,7 @@
 //! See [`Multipart`] for more details.
 
 use super::{rejection::*, BodyStream, FromRequest, RequestParts};
+use crate::BoxError;
 use async_trait::async_trait;
 use bytes::Bytes;
 use futures_util::stream::Stream;
@@ -13,7 +14,6 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
-use tower::BoxError;
 
 /// Extractor that parses `multipart/form-data` requests commonly used with file uploads.
 ///
diff --git a/src/extract/rejection.rs b/src/extract/rejection.rs
index 289577af..d8632e70 100644
--- a/src/extract/rejection.rs
+++ b/src/extract/rejection.rs
@@ -1,6 +1,7 @@
 //! Rejection response types.
 
 use super::IntoResponse;
+use crate::BoxError;
 use crate::{
     body::{box_body, BoxBody},
     Error,
@@ -8,7 +9,6 @@ use crate::{
 use bytes::Bytes;
 use http_body::Full;
 use std::convert::Infallible;
-use tower::BoxError;
 
 define_rejection! {
     #[status = INTERNAL_SERVER_ERROR]
diff --git a/src/extract/request_parts.rs b/src/extract/request_parts.rs
index 26a53999..ada9d2c1 100644
--- a/src/extract/request_parts.rs
+++ b/src/extract/request_parts.rs
@@ -1,4 +1,5 @@
 use super::{rejection::*, take_body, Extension, FromRequest, RequestParts};
+use crate::BoxError;
 use async_trait::async_trait;
 use bytes::Bytes;
 use futures_util::stream::Stream;
@@ -8,7 +9,6 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
-use tower::BoxError;
 
 #[async_trait]
 impl<B> FromRequest<B> for Request<B>
diff --git a/src/handler/future.rs b/src/handler/future.rs
index 999b96a9..71dcbe3b 100644
--- a/src/handler/future.rs
+++ b/src/handler/future.rs
@@ -16,7 +16,8 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
-use tower::{util::Oneshot, Service};
+use tower::util::Oneshot;
+use tower_service::Service;
 
 pin_project! {
     /// The response future for [`OnMethod`](super::OnMethod).
diff --git a/src/handler/into_service.rs b/src/handler/into_service.rs
index ffc0fde0..321fa1bd 100644
--- a/src/handler/into_service.rs
+++ b/src/handler/into_service.rs
@@ -7,7 +7,7 @@ use std::{
     marker::PhantomData,
     task::{Context, Poll},
 };
-use tower::Service;
+use tower_service::Service;
 
 /// An adapter that makes a [`Handler`] into a [`Service`].
 ///
diff --git a/src/handler/mod.rs b/src/handler/mod.rs
index 8fb0630c..5e262d34 100644
--- a/src/handler/mod.rs
+++ b/src/handler/mod.rs
@@ -7,6 +7,7 @@ use crate::{
     routing::{EmptyRouter, MethodFilter},
     service::HandleError,
     util::Either,
+    BoxError,
 };
 use async_trait::async_trait;
 use bytes::Bytes;
@@ -18,7 +19,9 @@ use std::{
     marker::PhantomData,
     task::{Context, Poll},
 };
-use tower::{BoxError, Layer, Service, ServiceExt};
+use tower::ServiceExt;
+use tower_layer::Layer;
+use tower_service::Service;
 
 pub mod future;
 mod into_service;
diff --git a/src/json.rs b/src/json.rs
index 693ee8b4..56f1abfa 100644
--- a/src/json.rs
+++ b/src/json.rs
@@ -1,3 +1,4 @@
+use crate::BoxError;
 use crate::{
     extract::{has_content_type, rejection::*, take_body, FromRequest, RequestParts},
     response::IntoResponse,
@@ -15,7 +16,6 @@ use std::{
     convert::Infallible,
     ops::{Deref, DerefMut},
 };
-use tower::BoxError;
 
 /// JSON Extractor/Response
 ///
diff --git a/src/lib.rs b/src/lib.rs
index ec9fbe96..4d72df36 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -832,3 +832,6 @@ pub use tower_http::add_extension::{AddExtension, AddExtensionLayer};
 
 #[doc(inline)]
 pub use self::{error::Error, json::Json, routing::Router};
+
+/// Alias for a type-erased error type.
+pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
diff --git a/src/macros.rs b/src/macros.rs
index 8bed0eb3..bba7ea31 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -81,7 +81,7 @@ macro_rules! define_rejection {
         impl $name {
             pub(crate) fn from_err<E>(err: E) -> Self
             where
-                E: Into<tower::BoxError>,
+                E: Into<crate::BoxError>,
             {
                 Self(crate::Error::new(err))
             }
diff --git a/src/response/headers.rs b/src/response/headers.rs
index 812c97cd..03e24a85 100644
--- a/src/response/headers.rs
+++ b/src/response/headers.rs
@@ -1,11 +1,14 @@
 use super::IntoResponse;
-use crate::body::{box_body, BoxBody};
+use crate::{
+    body::{box_body, BoxBody},
+    BoxError,
+};
 use bytes::Bytes;
 use http::header::{HeaderMap, HeaderName, HeaderValue};
 use http::{Response, StatusCode};
 use http_body::{Body, Full};
 use std::{convert::TryInto, fmt};
-use tower::{util::Either, BoxError};
+use tower::util::Either;
 
 /// A response with headers.
 ///
diff --git a/src/response/mod.rs b/src/response/mod.rs
index 584cefb5..fbed2fb8 100644
--- a/src/response/mod.rs
+++ b/src/response/mod.rs
@@ -2,7 +2,7 @@
 
 use crate::{
     body::{box_body, BoxBody},
-    Error,
+    BoxError, Error,
 };
 use bytes::Bytes;
 use http::{header, HeaderMap, HeaderValue, Response, StatusCode};
@@ -11,7 +11,6 @@ use http_body::{
     Empty, Full,
 };
 use std::{borrow::Cow, convert::Infallible};
-use tower::{util::Either, BoxError};
 
 mod headers;
 mod redirect;
@@ -154,22 +153,6 @@ impl IntoResponse for Infallible {
     }
 }
 
-impl<T, K> IntoResponse for Either<T, K>
-where
-    T: IntoResponse,
-    K: IntoResponse,
-{
-    type Body = BoxBody;
-    type BodyError = Error;
-
-    fn into_response(self) -> Response<Self::Body> {
-        match self {
-            Either::A(inner) => inner.into_response().map(box_body),
-            Either::B(inner) => inner.into_response().map(box_body),
-        }
-    }
-}
-
 impl<T, E> IntoResponse for Result<T, E>
 where
     T: IntoResponse,
diff --git a/src/response/sse.rs b/src/response/sse.rs
index 4a069971..ab9436d8 100644
--- a/src/response/sse.rs
+++ b/src/response/sse.rs
@@ -28,6 +28,7 @@
 //! ```
 
 use crate::response::IntoResponse;
+use crate::BoxError;
 use bytes::Bytes;
 use futures_util::{
     ready,
@@ -48,7 +49,6 @@ use std::{
 };
 use sync_wrapper::SyncWrapper;
 use tokio::time::Sleep;
-use tower::BoxError;
 
 /// Create a new [`Sse`] response that will respond with the given stream of
 /// [`Event`]s.
diff --git a/src/routing/future.rs b/src/routing/future.rs
index 4800e98f..418fd512 100644
--- a/src/routing/future.rs
+++ b/src/routing/future.rs
@@ -1,10 +1,11 @@
 //! Future types.
 
-use crate::{body::BoxBody, buffer::MpscBuffer, routing::FromEmptyRouter};
+use crate::{body::BoxBody, buffer::MpscBuffer, routing::FromEmptyRouter, BoxError};
 use futures_util::ready;
 use http::{Request, Response};
 use pin_project_lite::pin_project;
 use std::{
+    convert::Infallible,
     fmt,
     future::Future,
     pin::Pin,
@@ -12,8 +13,9 @@ use std::{
 };
 use tower::{
     util::{BoxService, Oneshot},
-    BoxError, Service, ServiceExt,
+    ServiceExt,
 };
+use tower_service::Service;
 
 pub use super::or::ResponseFuture as OrResponseFuture;
 
@@ -179,3 +181,9 @@ where
         self.project().inner.poll(cx)
     }
 }
+
+opaque_future! {
+    /// Response future from [`MakeRouteService`] services.
+    pub type MakeRouteServiceFuture<S> =
+        futures_util::future::Ready<Result<S, Infallible>>;
+}
diff --git a/src/routing/mod.rs b/src/routing/mod.rs
index e66fd62b..3de4d341 100644
--- a/src/routing/mod.rs
+++ b/src/routing/mod.rs
@@ -10,6 +10,7 @@ use crate::{
     },
     service::HandleError,
     util::ByteStr,
+    BoxError,
 };
 use bytes::Bytes;
 use http::{Request, Response, StatusCode, Uri};
@@ -24,17 +25,19 @@ use std::{
 };
 use tower::{
     util::{BoxService, ServiceExt},
-    BoxError, Layer, Service, ServiceBuilder,
+    ServiceBuilder,
 };
 use tower_http::map_response_body::MapResponseBodyLayer;
+use tower_layer::Layer;
+use tower_service::Service;
 
 pub mod future;
+
+mod method_filter;
 mod or;
 
 pub use self::{method_filter::MethodFilter, or::Or};
 
-mod method_filter;
-
 /// The router type for composing handlers and services.
 #[derive(Debug, Clone)]
 pub struct Router<S> {
@@ -358,11 +361,11 @@ impl<S> Router<S> {
     /// ```
     ///
     /// [`MakeService`]: tower::make::MakeService
-    pub fn into_make_service(self) -> tower::make::Shared<S>
+    pub fn into_make_service(self) -> IntoMakeService<S>
     where
         S: Clone,
     {
-        tower::make::Shared::new(self.svc)
+        IntoMakeService::new(self.svc)
     }
 
     /// Convert this router into a [`MakeService`], that will store `C`'s
@@ -1019,6 +1022,39 @@ where
     }
 }
 
+/// A [`MakeService`] that produces axum router services.
+///
+/// [`MakeService`]: tower::make::MakeService
+#[derive(Debug, Clone)]
+pub struct IntoMakeService<S> {
+    service: S,
+}
+
+impl<S> IntoMakeService<S> {
+    fn new(service: S) -> Self {
+        Self { service }
+    }
+}
+
+impl<S, T> Service<T> for IntoMakeService<S>
+where
+    S: Clone,
+{
+    type Response = S;
+    type Error = Infallible;
+    type Future = future::MakeRouteServiceFuture<S>;
+
+    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+        Poll::Ready(Ok(()))
+    }
+
+    fn call(&mut self, _target: T) -> Self::Future {
+        future::MakeRouteServiceFuture {
+            future: futures_util::future::ready(Ok(self.service.clone())),
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/src/routing/or.rs b/src/routing/or.rs
index eb00b86f..4699cea9 100644
--- a/src/routing/or.rs
+++ b/src/routing/or.rs
@@ -10,7 +10,8 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
-use tower::{util::Oneshot, Service, ServiceExt};
+use tower::{util::Oneshot, ServiceExt};
+use tower_service::Service;
 
 /// [`tower::Service`] that is the combination of two routers.
 ///
diff --git a/src/service/future.rs b/src/service/future.rs
index b80aac5a..37192ccd 100644
--- a/src/service/future.rs
+++ b/src/service/future.rs
@@ -4,6 +4,7 @@ use crate::{
     body::{box_body, BoxBody},
     response::IntoResponse,
     util::{Either, EitherProj},
+    BoxError,
 };
 use bytes::Bytes;
 use futures_util::ready;
@@ -15,7 +16,8 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
-use tower::{util::Oneshot, BoxError, Service};
+use tower::util::Oneshot;
+use tower_service::Service;
 
 pin_project! {
     /// Response future for [`HandleError`](super::HandleError).
diff --git a/src/service/mod.rs b/src/service/mod.rs
index c8833a35..d7951fb9 100644
--- a/src/service/mod.rs
+++ b/src/service/mod.rs
@@ -96,6 +96,7 @@
 //! [`Redirect`]: tower_http::services::Redirect
 //! [load shed]: tower::load_shed
 
+use crate::BoxError;
 use crate::{
     body::BoxBody,
     response::IntoResponse,
@@ -108,7 +109,8 @@ use std::{
     marker::PhantomData,
     task::{Context, Poll},
 };
-use tower::{util::Oneshot, BoxError, Service, ServiceExt as _};
+use tower::{util::Oneshot, ServiceExt as _};
+use tower_service::Service;
 
 pub mod future;
 
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 8094ec73..86fb72bb 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,5 +1,6 @@
 #![allow(clippy::blacklisted_name)]
 
+use crate::BoxError;
 use crate::{
     extract,
     handler::{any, delete, get, on, patch, post, Handler},
@@ -23,7 +24,8 @@ use std::{
     task::{Context, Poll},
     time::Duration,
 };
-use tower::{make::Shared, service_fn, BoxError, Service};
+use tower::{make::Shared, service_fn};
+use tower_service::Service;
 
 mod get_to_head;
 mod handle_error;
diff --git a/src/tests/or.rs b/src/tests/or.rs
index e9b55a15..db62b26e 100644
--- a/src/tests/or.rs
+++ b/src/tests/or.rs
@@ -1,10 +1,8 @@
+use super::*;
+use crate::{extract::OriginalUri, response::IntoResponse, Json};
 use serde_json::{json, Value};
 use tower::{limit::ConcurrencyLimitLayer, timeout::TimeoutLayer};
 
-use crate::{extract::OriginalUri, response::IntoResponse, Json};
-
-use super::*;
-
 #[tokio::test]
 async fn basic() {
     let one = Router::new()