From 3d07d40e02d40a671f0c84f2ecb6244738a7dd94 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 1 Nov 2021 22:37:16 +0100 Subject: [PATCH] Remove `Sync` requirement from response bodies (#440) As we learned [in Tonic] bodies don't need to be `Sync` because they can only be polled from one thread at a time. This changes axum's bodies to no longer require `Sync` and makes `BoxBody` an alias for `UnsyncBoxBody`. [in Tonic]: https://github.com/hyperium/tonic/issues/117 --- CHANGELOG.md | 5 +++++ Cargo.toml | 4 ++-- src/body.rs | 6 +++--- src/error_handling/mod.rs | 8 ++++---- src/extract/extractor_middleware.rs | 4 ++-- src/extract/multipart.rs | 2 +- src/handler/mod.rs | 4 ++-- src/response/headers.rs | 4 ++-- src/response/mod.rs | 24 ++++++++++++++++++------ src/routing/method_not_allowed.rs | 2 +- src/routing/mod.rs | 8 ++++---- src/routing/not_found.rs | 2 +- src/routing/service_method_routing.rs | 4 ++-- 13 files changed, 47 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a1e62e..f1f1376a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 thus wasn't necessary - **breaking:** Vendor `AddExtensionLayer` and `AddExtension` to reduce public dependencies + - **breaking:** `body::BoxBody` is now a type alias for + `http_body::combinators::UnsyncBoxBody` and thus is no longer `Sync`. This + is because bodies are streams and requiring streams to be `Sync` is + unnecessary. + - **added:** Implement `IntoResponse` for `http_body::combinators::UnsyncBoxBody`. - Routing: - Big internal refactoring of routing leading to several improvements ([#363]) - **added:** Wildcard routes like `.route("/api/users/*rest", service)` are now supported. diff --git a/Cargo.toml b/Cargo.toml index 9adbb91b..d4acebe5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,8 @@ bitflags = "1.0" bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2" -http-body = "0.4.3" -hyper = { version = "0.14", features = ["server", "tcp", "stream"] } +http-body = "0.4.4" +hyper = { version = "0.14.14", features = ["server", "tcp", "stream"] } matchit = "0.4.4" percent-encoding = "2.1" pin-project-lite = "0.2.7" diff --git a/src/body.rs b/src/body.rs index 8e9c6a94..cc11a15c 100644 --- a/src/body.rs +++ b/src/body.rs @@ -20,15 +20,15 @@ pub use bytes::Bytes; /// /// This is used in axum as the response body type for applications. Its /// necessary to unify multiple response bodies types into one. -pub type BoxBody = http_body::combinators::BoxBody; +pub type BoxBody = http_body::combinators::UnsyncBoxBody; /// Convert a [`http_body::Body`] into a [`BoxBody`]. pub fn box_body(body: B) -> BoxBody where - B: http_body::Body + Send + Sync + 'static, + B: http_body::Body + Send + 'static, B::Error: Into, { - body.map_err(Error::new).boxed() + body.map_err(Error::new).boxed_unsync() } pub(crate) fn empty() -> BoxBody { diff --git a/src/error_handling/mod.rs b/src/error_handling/mod.rs index 176ba608..54456dad 100644 --- a/src/error_handling/mod.rs +++ b/src/error_handling/mod.rs @@ -114,8 +114,8 @@ where S: Service, Response = Response> + Clone, F: FnOnce(S::Error) -> Res + Clone, Res: IntoResponse, - ResBody: http_body::Body + Send + Sync + 'static, - ResBody::Error: Into + Send + Sync + 'static, + ResBody: http_body::Body + Send + 'static, + ResBody::Error: Into, { type Response = Response; type Error = Infallible; @@ -181,8 +181,8 @@ pub mod future { Fut: Future, E>>, F: FnOnce(E) -> Res, Res: IntoResponse, - B: http_body::Body + Send + Sync + 'static, - B::Error: Into + Send + Sync + 'static, + B: http_body::Body + Send + 'static, + B::Error: Into, { type Output = Result, Infallible>; diff --git a/src/extract/extractor_middleware.rs b/src/extract/extractor_middleware.rs index 7ed8cb76..5a68f802 100644 --- a/src/extract/extractor_middleware.rs +++ b/src/extract/extractor_middleware.rs @@ -154,7 +154,7 @@ where E: FromRequest + 'static, ReqBody: Default + Send + 'static, S: Service, Response = Response> + Clone, - ResBody: http_body::Body + Send + Sync + 'static, + ResBody: http_body::Body + Send + 'static, ResBody::Error: Into, { type Response = Response; @@ -213,7 +213,7 @@ where E: FromRequest, S: Service, Response = Response>, ReqBody: Default, - ResBody: http_body::Body + Send + Sync + 'static, + ResBody: http_body::Body + Send + 'static, ResBody::Error: Into, { type Output = Result, S::Error>; diff --git a/src/extract/multipart.rs b/src/extract/multipart.rs index fcfd0642..e1d7104c 100644 --- a/src/extract/multipart.rs +++ b/src/extract/multipart.rs @@ -53,7 +53,7 @@ pub struct Multipart { impl FromRequest for Multipart where B: http_body::Body + Default + Unpin + Send + 'static, - B::Error: Into + 'static, + B::Error: Into, { type Rejection = MultipartRejection; diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 64cf4250..50ac8032 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -281,8 +281,8 @@ where S::Future: Send, T: 'static, ReqBody: Send + 'static, - ResBody: http_body::Body + Send + Sync + 'static, - ResBody::Error: Into + Send + Sync + 'static, + ResBody: http_body::Body + Send + 'static, + ResBody::Error: Into, { type Sealed = sealed::Hidden; diff --git a/src/response/headers.rs b/src/response/headers.rs index 2b086e1f..c4c9bcb6 100644 --- a/src/response/headers.rs +++ b/src/response/headers.rs @@ -115,7 +115,7 @@ where impl IntoResponse for (Headers, T) where T: IntoResponse, - T::Body: Body + Send + Sync + 'static, + T::Body: Body + Send + 'static, ::Error: Into, H: IntoIterator, K: TryInto, @@ -141,7 +141,7 @@ where impl IntoResponse for (StatusCode, Headers, T) where T: IntoResponse, - T::Body: Body + Send + Sync + 'static, + T::Body: Body + Send + 'static, ::Error: Into, H: IntoIterator, K: TryInto, diff --git a/src/response/mod.rs b/src/response/mod.rs index ec2f8a63..3f36eed7 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -163,7 +163,7 @@ pub trait IntoResponse { /// [`axum::body::Empty`]: crate::body::Empty /// [`axum::body::Full`]: crate::body::Full /// [`axum::body::BoxBody`]: crate::body::BoxBody - type Body: http_body::Body + Send + Sync + 'static; + type Body: http_body::Body + Send + 'static; /// The error type `Self::Body` might generate. /// @@ -217,7 +217,7 @@ where impl IntoResponse for Response where - B: http_body::Body + Send + Sync + 'static, + B: http_body::Body + Send + 'static, B::Error: Into, { type Body = B; @@ -257,10 +257,22 @@ where } } +impl IntoResponse for http_body::combinators::UnsyncBoxBody +where + E: Into + 'static, +{ + type Body = Self; + type BodyError = E; + + fn into_response(self) -> Response { + Response::new(self) + } +} + impl IntoResponse for MapData where - B: http_body::Body + Send + Sync + 'static, - F: FnMut(B::Data) -> Bytes + Send + Sync + 'static, + B: http_body::Body + Send + 'static, + F: FnMut(B::Data) -> Bytes + Send + 'static, B::Error: Into, { type Body = Self; @@ -273,8 +285,8 @@ where impl IntoResponse for MapErr where - B: http_body::Body + Send + Sync + 'static, - F: FnMut(B::Error) -> E + Send + Sync + 'static, + B: http_body::Body + Send + 'static, + F: FnMut(B::Error) -> E + Send + 'static, E: Into, { type Body = Self; diff --git a/src/routing/method_not_allowed.rs b/src/routing/method_not_allowed.rs index 5dbc400f..a1d47665 100644 --- a/src/routing/method_not_allowed.rs +++ b/src/routing/method_not_allowed.rs @@ -41,7 +41,7 @@ impl fmt::Debug for MethodNotAllowed { impl Service> for MethodNotAllowed where - B: Send + Sync + 'static, + B: Send + 'static, { type Response = Response; type Error = E; diff --git a/src/routing/mod.rs b/src/routing/mod.rs index 361b849d..89587fb5 100644 --- a/src/routing/mod.rs +++ b/src/routing/mod.rs @@ -77,7 +77,7 @@ impl Clone for Router { impl Default for Router where - B: Send + Sync + 'static, + B: Send + 'static, { fn default() -> Self { Self::new() @@ -99,7 +99,7 @@ const NEST_TAIL_PARAM_CAPTURE: &str = "/*axum_nest"; impl Router where - B: Send + Sync + 'static, + B: Send + 'static, { /// Create a new `Router`. /// @@ -240,7 +240,7 @@ where + Send + 'static, >>::Future: Send + 'static, - LayeredResBody: http_body::Body + Send + Sync + 'static, + LayeredResBody: http_body::Body + Send + 'static, LayeredResBody::Error: Into, { let layer = ServiceBuilder::new() @@ -361,7 +361,7 @@ where impl Service> for Router where - B: Send + Sync + 'static, + B: Send + 'static, { type Response = Response; type Error = Infallible; diff --git a/src/routing/not_found.rs b/src/routing/not_found.rs index 8f7b689d..ef02dec2 100644 --- a/src/routing/not_found.rs +++ b/src/routing/not_found.rs @@ -16,7 +16,7 @@ pub(crate) struct NotFound; impl Service> for NotFound where - B: Send + Sync + 'static, + B: Send + 'static, { type Response = Response; type Error = Infallible; diff --git a/src/routing/service_method_routing.rs b/src/routing/service_method_routing.rs index 49747fa7..2dcb09d5 100644 --- a/src/routing/service_method_routing.rs +++ b/src/routing/service_method_routing.rs @@ -464,7 +464,7 @@ impl MethodRouter { impl Service> for MethodRouter where S: Service, Response = Response> + Clone, - ResBody: http_body::Body + Send + Sync + 'static, + ResBody: http_body::Body + Send + 'static, ResBody::Error: Into, F: Service, Response = Response, Error = S::Error> + Clone, { @@ -514,7 +514,7 @@ pin_project! { impl Future for MethodRouterFuture where S: Service, Response = Response> + Clone, - ResBody: http_body::Body + Send + Sync + 'static, + ResBody: http_body::Body + Send + 'static, ResBody::Error: Into, F: Service, Response = Response, Error = S::Error>, {