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()