From f9dc96fdcef1c7dbd668d0731a76ea3f7079984d Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 9 Oct 2022 22:55:28 +0200 Subject: [PATCH] Don't internally `Arc` the state (#1460) --- axum-extra/src/handler/mod.rs | 12 +-- axum-extra/src/handler/or.rs | 6 +- axum-extra/src/routing/mod.rs | 2 +- axum-extra/src/routing/resource.rs | 2 +- .../from_request/pass/state_explicit_parts.rs | 2 +- axum/src/handler/boxed.rs | 17 ++-- .../into_service_state_in_extension.rs | 84 +++++++++++++++++++ axum/src/handler/mod.rs | 17 ++-- axum/src/handler/service.rs | 15 ++-- axum/src/middleware/from_extractor.rs | 33 ++++---- axum/src/middleware/from_fn.rs | 27 +++--- axum/src/middleware/map_request.rs | 32 +++---- axum/src/middleware/map_response.rs | 30 +++---- axum/src/middleware/mod.rs | 13 +-- axum/src/routing/method_routing.rs | 32 +++---- axum/src/routing/mod.rs | 61 ++++---------- axum/src/routing/service.rs | 4 +- axum/src/test_helpers/test_client.rs | 2 +- 18 files changed, 206 insertions(+), 185 deletions(-) create mode 100644 axum/src/handler/into_service_state_in_extension.rs diff --git a/axum-extra/src/handler/mod.rs b/axum-extra/src/handler/mod.rs index 75b4d2cf..305c9c8c 100644 --- a/axum-extra/src/handler/mod.rs +++ b/axum-extra/src/handler/mod.rs @@ -6,7 +6,7 @@ use axum::{ response::{IntoResponse, Response}, }; use futures_util::future::{BoxFuture, FutureExt, Map}; -use std::{future::Future, marker::PhantomData, sync::Arc}; +use std::{future::Future, marker::PhantomData}; mod or; @@ -24,11 +24,7 @@ pub trait HandlerCallWithExtractors: Sized { type Future: Future + Send + 'static; /// Call the handler with the extracted inputs. - fn call( - self, - extractors: T, - state: Arc, - ) -> >::Future; + fn call(self, extractors: T, state: S) -> >::Future; /// Conver this `HandlerCallWithExtractors` into [`Handler`]. fn into_handler(self) -> IntoHandler { @@ -133,7 +129,7 @@ macro_rules! impl_handler_call_with { fn call( self, ($($ty,)*): ($($ty,)*), - _state: Arc, + _state: S, ) -> >::Future { self($($ty,)*).map(IntoResponse::into_response) } @@ -178,7 +174,7 @@ where { type Future = BoxFuture<'static, Response>; - fn call(self, req: http::Request, state: Arc) -> Self::Future { + fn call(self, req: http::Request, state: S) -> Self::Future { Box::pin(async move { match T::from_request(req, &state).await { Ok(t) => self.handler.call(t, state).await, diff --git a/axum-extra/src/handler/or.rs b/axum-extra/src/handler/or.rs index cf470ea5..2ef24e71 100644 --- a/axum-extra/src/handler/or.rs +++ b/axum-extra/src/handler/or.rs @@ -7,7 +7,7 @@ use axum::{ response::{IntoResponse, Response}, }; use futures_util::future::{BoxFuture, Either as EitherFuture, FutureExt, Map}; -use std::{future::Future, marker::PhantomData, sync::Arc}; +use std::{future::Future, marker::PhantomData}; /// [`Handler`] that runs one [`Handler`] and if that rejects it'll fallback to another /// [`Handler`]. @@ -37,7 +37,7 @@ where fn call( self, extractors: Either, - state: Arc, + state: S, ) -> , S, B>>::Future { match extractors { Either::E1(lt) => self @@ -68,7 +68,7 @@ where // this puts `futures_util` in our public API but thats fine in axum-extra type Future = BoxFuture<'static, Response>; - fn call(self, req: Request, state: Arc) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { Box::pin(async move { let (mut parts, body) = req.into_parts(); diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index d3a8e554..a17751c8 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -178,7 +178,7 @@ pub trait RouterExt: sealed::Sealed { impl RouterExt for Router where B: axum::body::HttpBody + Send + 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { #[cfg(feature = "typed-routing")] fn typed_get(self, handler: H) -> Self diff --git a/axum-extra/src/routing/resource.rs b/axum-extra/src/routing/resource.rs index 5d5150e0..7648d9f5 100644 --- a/axum-extra/src/routing/resource.rs +++ b/axum-extra/src/routing/resource.rs @@ -53,7 +53,7 @@ where impl Resource where B: axum::body::HttpBody + Send + 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { /// Create a `Resource` with the given name and state. /// diff --git a/axum-macros/tests/from_request/pass/state_explicit_parts.rs b/axum-macros/tests/from_request/pass/state_explicit_parts.rs index 5581ef5f..2aeb6e3a 100644 --- a/axum-macros/tests/from_request/pass/state_explicit_parts.rs +++ b/axum-macros/tests/from_request/pass/state_explicit_parts.rs @@ -18,7 +18,7 @@ struct Extractor { other: Query>, } -#[derive(Default)] +#[derive(Default, Clone)] struct AppState { inner: InnerState, } diff --git a/axum/src/handler/boxed.rs b/axum/src/handler/boxed.rs index 34b5c31d..bfdd0d0f 100644 --- a/axum/src/handler/boxed.rs +++ b/axum/src/handler/boxed.rs @@ -1,4 +1,4 @@ -use std::{convert::Infallible, sync::Arc}; +use std::convert::Infallible; use super::Handler; use crate::routing::Route; @@ -7,7 +7,7 @@ pub(crate) struct BoxedHandler(Box BoxedHandler where - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, B: Send + 'static, { pub(crate) fn new(handler: H) -> Self @@ -17,7 +17,7 @@ where { Self(Box::new(MakeErasedHandler { handler, - into_route: |handler, state| Route::new(Handler::with_state_arc(handler, state)), + into_route: |handler, state| Route::new(Handler::with_state(handler, state)), })) } } @@ -38,7 +38,7 @@ impl BoxedHandler { })) } - pub(crate) fn into_route(self, state: Arc) -> Route { + pub(crate) fn into_route(self, state: S) -> Route { self.0.into_route(state) } } @@ -51,12 +51,13 @@ impl Clone for BoxedHandler { trait ErasedHandler: Send { fn clone_box(&self) -> Box>; - fn into_route(self: Box, state: Arc) -> Route; + + fn into_route(self: Box, state: S) -> Route; } struct MakeErasedHandler { handler: H, - into_route: fn(H, Arc) -> Route, + into_route: fn(H, S) -> Route, } impl ErasedHandler for MakeErasedHandler @@ -69,7 +70,7 @@ where Box::new(self.clone()) } - fn into_route(self: Box, state: Arc) -> Route { + fn into_route(self: Box, state: S) -> Route { (self.into_route)(self.handler, state) } } @@ -103,7 +104,7 @@ where }) } - fn into_route(self: Box, state: Arc) -> Route { + fn into_route(self: Box, state: S) -> Route { (self.layer)(self.handler.into_route(state)) } } diff --git a/axum/src/handler/into_service_state_in_extension.rs b/axum/src/handler/into_service_state_in_extension.rs new file mode 100644 index 00000000..4e6a3f24 --- /dev/null +++ b/axum/src/handler/into_service_state_in_extension.rs @@ -0,0 +1,84 @@ +use super::Handler; +use crate::response::Response; +use http::Request; +use std::{ + convert::Infallible, + fmt, + marker::PhantomData, + task::{Context, Poll}, +}; +use tower_service::Service; + +pub(crate) struct IntoServiceStateInExtension { + handler: H, + _marker: PhantomData (T, S, B)>, +} + +#[test] +fn traits() { + use crate::test_helpers::*; + assert_send::>(); + assert_sync::>(); +} + +impl IntoServiceStateInExtension { + pub(crate) fn new(handler: H) -> Self { + Self { + handler, + _marker: PhantomData, + } + } +} + +impl fmt::Debug for IntoServiceStateInExtension { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IntoServiceStateInExtension") + .finish_non_exhaustive() + } +} + +impl Clone for IntoServiceStateInExtension +where + H: Clone, +{ + fn clone(&self) -> Self { + Self { + handler: self.handler.clone(), + _marker: PhantomData, + } + } +} + +impl Service> for IntoServiceStateInExtension +where + H: Handler + Clone + Send + 'static, + B: Send + 'static, + S: Send + Sync + 'static, +{ + type Response = Response; + type Error = Infallible; + type Future = super::future::IntoServiceFuture; + + #[inline] + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + // `IntoServiceStateInExtension` can only be constructed from async functions which are always ready, or + // from `Layered` which bufferes in `::call` and is therefore + // also always ready. + Poll::Ready(Ok(())) + } + + fn call(&mut self, mut req: Request) -> Self::Future { + use futures_util::future::FutureExt; + + let state = req + .extensions_mut() + .remove::() + .expect("state extension missing. This is a bug in axum, please file an issue"); + + let handler = self.handler.clone(); + let future = Handler::call(handler, req, state); + let future = future.map(Ok as _); + + super::future::IntoServiceFuture::new(future) + } +} diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index 68a6deb0..986b1342 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -44,7 +44,7 @@ use crate::{ routing::IntoMakeService, }; use http::Request; -use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin, sync::Arc}; +use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin}; use tower::ServiceExt; use tower_layer::Layer; use tower_service::Service; @@ -101,7 +101,7 @@ pub trait Handler: Clone + Send + Sized + 'static { type Future: Future + Send + 'static; /// Call the handler with the given request. - fn call(self, req: Request, state: Arc) -> Self::Future; + fn call(self, req: Request, state: S) -> Self::Future; /// Apply a [`tower::Layer`] to the handler. /// @@ -152,11 +152,6 @@ pub trait Handler: Clone + Send + Sized + 'static { /// Convert the handler into a [`Service`] by providing the state fn with_state(self, state: S) -> HandlerService { - self.with_state_arc(Arc::new(state)) - } - - /// Convert the handler into a [`Service`] by providing the state - fn with_state_arc(self, state: Arc) -> HandlerService { HandlerService::new(self, state) } } @@ -170,7 +165,7 @@ where { type Future = Pin + Send>>; - fn call(self, _req: Request, _state: Arc) -> Self::Future { + fn call(self, _req: Request, _state: S) -> Self::Future { Box::pin(async move { self().await.into_response() }) } } @@ -192,7 +187,7 @@ macro_rules! impl_handler { { type Future = Pin + Send>>; - fn call(self, req: Request, state: Arc) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { Box::pin(async move { let (mut parts, body) = req.into_parts(); let state = &state; @@ -269,10 +264,10 @@ where { type Future = future::LayeredFuture; - fn call(self, req: Request, state: Arc) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { use futures_util::future::{FutureExt, Map}; - let svc = self.handler.with_state_arc(state); + let svc = self.handler.with_state(state); let svc = self.layer.layer(svc); let future: Map< diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 4e6b63b3..05b9b018 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -8,20 +8,18 @@ use std::{ convert::Infallible, fmt, marker::PhantomData, - sync::Arc, task::{Context, Poll}, }; use tower_service::Service; /// An adapter that makes a [`Handler`] into a [`Service`]. /// -/// Created with [`Handler::with_state`], [`Handler::with_state_arc`] or -/// [`HandlerWithoutStateExt::into_service`]. +/// Created with [`Handler::with_state`] or [`HandlerWithoutStateExt::into_service`]. /// /// [`HandlerWithoutStateExt::into_service`]: super::HandlerWithoutStateExt::into_service pub struct HandlerService { handler: H, - state: Arc, + state: S, _marker: PhantomData (T, B)>, } @@ -119,7 +117,7 @@ fn traits() { } impl HandlerService { - pub(super) fn new(handler: H, state: Arc) -> Self { + pub(super) fn new(handler: H, state: S) -> Self { Self { handler, state, @@ -137,11 +135,12 @@ impl fmt::Debug for HandlerService { impl Clone for HandlerService where H: Clone, + S: Clone, { fn clone(&self) -> Self { Self { handler: self.handler.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _marker: PhantomData, } } @@ -151,7 +150,7 @@ impl Service> for HandlerService where H: Handler + Clone + Send + 'static, B: Send + 'static, - S: Send + Sync, + S: Clone + Send + Sync, { type Response = Response; type Error = Infallible; @@ -169,7 +168,7 @@ where use futures_util::future::FutureExt; let handler = self.handler.clone(); - let future = Handler::call(handler, req, Arc::clone(&self.state)); + let future = Handler::call(handler, req, self.state.clone()); let future = future.map(Ok as _); super::future::IntoServiceFuture::new(future) diff --git a/axum/src/middleware/from_extractor.rs b/axum/src/middleware/from_extractor.rs index 6fd15f73..b10785d3 100644 --- a/axum/src/middleware/from_extractor.rs +++ b/axum/src/middleware/from_extractor.rs @@ -10,7 +10,6 @@ use std::{ future::Future, marker::PhantomData, pin::Pin, - sync::Arc, task::{Context, Poll}, }; use tower_layer::Layer; @@ -99,13 +98,6 @@ pub fn from_extractor() -> FromExtractorLayer { /// /// See [`State`](crate::extract::State) for more details about accessing state. pub fn from_extractor_with_state(state: S) -> FromExtractorLayer { - from_extractor_with_state_arc(Arc::new(state)) -} - -/// Create a middleware from an extractor with the given [`Arc`]'ed state. -/// -/// See [`State`](crate::extract::State) for more details about accessing state. -pub fn from_extractor_with_state_arc(state: Arc) -> FromExtractorLayer { FromExtractorLayer { state, _marker: PhantomData, @@ -119,14 +111,17 @@ pub fn from_extractor_with_state_arc(state: Arc) -> FromExtractorLayer< /// /// [`Layer`]: tower::Layer pub struct FromExtractorLayer { - state: Arc, + state: S, _marker: PhantomData E>, } -impl Clone for FromExtractorLayer { +impl Clone for FromExtractorLayer +where + S: Clone, +{ fn clone(&self) -> Self { Self { - state: Arc::clone(&self.state), + state: self.state.clone(), _marker: PhantomData, } } @@ -144,13 +139,16 @@ where } } -impl Layer for FromExtractorLayer { +impl Layer for FromExtractorLayer +where + S: Clone, +{ type Service = FromExtractor; fn layer(&self, inner: T) -> Self::Service { FromExtractor { inner, - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: PhantomData, } } @@ -161,7 +159,7 @@ impl Layer for FromExtractorLayer { /// See [`from_extractor`] for more details. pub struct FromExtractor { inner: T, - state: Arc, + state: S, _extractor: PhantomData E>, } @@ -175,11 +173,12 @@ fn traits() { impl Clone for FromExtractor where T: Clone, + S: Clone, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: PhantomData, } } @@ -205,7 +204,7 @@ where B: Default + Send + 'static, T: Service> + Clone, T::Response: IntoResponse, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { type Response = Response; type Error = T::Error; @@ -217,7 +216,7 @@ where } fn call(&mut self, req: Request) -> Self::Future { - let state = Arc::clone(&self.state); + let state = self.state.clone(); let extract_future = Box::pin(async move { let (mut parts, body) = req.into_parts(); let extracted = E::from_request_parts(&mut parts, &state).await; diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index a3361f50..3324ad87 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -9,7 +9,6 @@ use std::{ future::Future, marker::PhantomData, pin::Pin, - sync::Arc, task::{Context, Poll}, }; use tower::{util::BoxCloneService, ServiceBuilder}; @@ -140,15 +139,6 @@ pub fn from_fn(f: F) -> FromFnLayer { /// # let app: Router<_> = app; /// ``` pub fn from_fn_with_state(state: S, f: F) -> FromFnLayer { - from_fn_with_state_arc(Arc::new(state), f) -} - -/// Create a middleware from an async function with the given [`Arc`]'ed state. -/// -/// See [`from_fn_with_state`] for an example. -/// -/// See [`State`](crate::extract::State) for more details about accessing state. -pub fn from_fn_with_state_arc(state: Arc, f: F) -> FromFnLayer { FromFnLayer { f, state, @@ -163,18 +153,19 @@ pub fn from_fn_with_state_arc(state: Arc, f: F) -> FromFnLayer { f: F, - state: Arc, + state: S, _extractor: PhantomData T>, } impl Clone for FromFnLayer where F: Clone, + S: Clone, { fn clone(&self) -> Self { Self { f: self.f.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: self._extractor, } } @@ -183,13 +174,14 @@ where impl Layer for FromFnLayer where F: Clone, + S: Clone, { type Service = FromFn; fn layer(&self, inner: I) -> Self::Service { FromFn { f: self.f.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), inner, _extractor: PhantomData, } @@ -215,7 +207,7 @@ where pub struct FromFn { f: F, inner: I, - state: Arc, + state: S, _extractor: PhantomData T>, } @@ -223,12 +215,13 @@ impl Clone for FromFn where F: Clone, I: Clone, + S: Clone, { fn clone(&self) -> Self { Self { f: self.f.clone(), inner: self.inner.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: self._extractor, } } @@ -253,7 +246,7 @@ macro_rules! impl_service { I::Response: IntoResponse, I::Future: Send + 'static, B: Send + 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { type Response = Response; type Error = Infallible; @@ -268,7 +261,7 @@ macro_rules! impl_service { let ready_inner = std::mem::replace(&mut self.inner, not_ready_inner); let mut f = self.f.clone(); - let state = Arc::clone(&self.state); + let state = self.state.clone(); let future = Box::pin(async move { let (mut parts, body) = req.into_parts(); diff --git a/axum/src/middleware/map_request.rs b/axum/src/middleware/map_request.rs index b1378345..f574681d 100644 --- a/axum/src/middleware/map_request.rs +++ b/axum/src/middleware/map_request.rs @@ -9,7 +9,6 @@ use std::{ future::Future, marker::PhantomData, pin::Pin, - sync::Arc, task::{Context, Poll}, }; use tower_layer::Layer; @@ -112,7 +111,7 @@ use tower_service::Service; /// # let _: Router = app; /// ``` /// -/// Note that to access state you must use either [`map_request_with_state`] or [`map_request_with_state_arc`]. +/// Note that to access state you must use either [`map_request_with_state`]. pub fn map_request(f: F) -> MapRequestLayer { map_request_with_state((), f) } @@ -155,16 +154,6 @@ pub fn map_request(f: F) -> MapRequestLayer { /// # let app: Router<_> = app; /// ``` pub fn map_request_with_state(state: S, f: F) -> MapRequestLayer { - map_request_with_state_arc(Arc::new(state), f) -} - -/// Create a middleware from an async function that transforms a request, with the given [`Arc`]'ed -/// state. -/// -/// See [`map_request_with_state`] for an example. -/// -/// See [`State`](crate::extract::State) for more details about accessing state. -pub fn map_request_with_state_arc(state: Arc, f: F) -> MapRequestLayer { MapRequestLayer { f, state, @@ -177,18 +166,19 @@ pub fn map_request_with_state_arc(state: Arc, f: F) -> MapRequestLay /// Created with [`map_request`]. See that function for more details. pub struct MapRequestLayer { f: F, - state: Arc, + state: S, _extractor: PhantomData T>, } impl Clone for MapRequestLayer where F: Clone, + S: Clone, { fn clone(&self) -> Self { Self { f: self.f.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: self._extractor, } } @@ -197,13 +187,14 @@ where impl Layer for MapRequestLayer where F: Clone, + S: Clone, { type Service = MapRequest; fn layer(&self, inner: I) -> Self::Service { MapRequest { f: self.f.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), inner, _extractor: PhantomData, } @@ -229,7 +220,7 @@ where pub struct MapRequest { f: F, inner: I, - state: Arc, + state: S, _extractor: PhantomData T>, } @@ -237,12 +228,13 @@ impl Clone for MapRequest where F: Clone, I: Clone, + S: Clone, { fn clone(&self) -> Self { Self { f: self.f.clone(), inner: self.inner.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: self._extractor, } } @@ -267,7 +259,7 @@ macro_rules! impl_service { I::Response: IntoResponse, I::Future: Send + 'static, B: Send + 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { type Response = Response; type Error = Infallible; @@ -282,7 +274,7 @@ macro_rules! impl_service { let mut ready_inner = std::mem::replace(&mut self.inner, not_ready_inner); let mut f = self.f.clone(); - let state = Arc::clone(&self.state); + let state = self.state.clone(); let future = Box::pin(async move { let (mut parts, body) = req.into_parts(); @@ -363,7 +355,7 @@ mod private { } /// Trait implemented by types that can be returned from [`map_request`], -/// [`map_request_with_state`], and [`map_request_with_state_arc`]. +/// [`map_request_with_state`]. /// /// This trait is sealed such that it cannot be implemented outside this crate. pub trait IntoMapRequestResult: private::Sealed { diff --git a/axum/src/middleware/map_response.rs b/axum/src/middleware/map_response.rs index a8b332d1..5712f78e 100644 --- a/axum/src/middleware/map_response.rs +++ b/axum/src/middleware/map_response.rs @@ -9,7 +9,6 @@ use std::{ future::Future, marker::PhantomData, pin::Pin, - sync::Arc, task::{Context, Poll}, }; use tower_layer::Layer; @@ -67,7 +66,7 @@ use tower_service::Service; /// # let _: Router = app; /// ``` /// -/// Note that to access state you must use either [`map_response_with_state`] or [`map_response_with_state_arc`]. +/// Note that to access state you must use either [`map_response_with_state`]. pub fn map_response(f: F) -> MapResponseLayer { map_response_with_state((), f) } @@ -110,16 +109,6 @@ pub fn map_response(f: F) -> MapResponseLayer { /// # let app: Router<_> = app; /// ``` pub fn map_response_with_state(state: S, f: F) -> MapResponseLayer { - map_response_with_state_arc(Arc::new(state), f) -} - -/// Create a middleware from an async function that transforms a response, with the given [`Arc`]'ed -/// state. -/// -/// See [`map_response_with_state`] for an example. -/// -/// See [`State`](crate::extract::State) for more details about accessing state. -pub fn map_response_with_state_arc(state: Arc, f: F) -> MapResponseLayer { MapResponseLayer { f, state, @@ -132,18 +121,19 @@ pub fn map_response_with_state_arc(state: Arc, f: F) -> MapResponseL /// Created with [`map_response`]. See that function for more details. pub struct MapResponseLayer { f: F, - state: Arc, + state: S, _extractor: PhantomData T>, } impl Clone for MapResponseLayer where F: Clone, + S: Clone, { fn clone(&self) -> Self { Self { f: self.f.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: self._extractor, } } @@ -152,13 +142,14 @@ where impl Layer for MapResponseLayer where F: Clone, + S: Clone, { type Service = MapResponse; fn layer(&self, inner: I) -> Self::Service { MapResponse { f: self.f.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), inner, _extractor: PhantomData, } @@ -184,7 +175,7 @@ where pub struct MapResponse { f: F, inner: I, - state: Arc, + state: S, _extractor: PhantomData T>, } @@ -192,12 +183,13 @@ impl Clone for MapResponse where F: Clone, I: Clone, + S: Clone, { fn clone(&self) -> Self { Self { f: self.f.clone(), inner: self.inner.clone(), - state: Arc::clone(&self.state), + state: self.state.clone(), _extractor: self._extractor, } } @@ -221,7 +213,7 @@ macro_rules! impl_service { I::Future: Send + 'static, B: Send + 'static, ResBody: Send + 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { type Response = Response; type Error = Infallible; @@ -237,7 +229,7 @@ macro_rules! impl_service { let mut ready_inner = std::mem::replace(&mut self.inner, not_ready_inner); let mut f = self.f.clone(); - let _state = Arc::clone(&self.state); + let _state = self.state.clone(); let future = Box::pin(async move { let (mut parts, body) = req.into_parts(); diff --git a/axum/src/middleware/mod.rs b/axum/src/middleware/mod.rs index 5b138828..22dab143 100644 --- a/axum/src/middleware/mod.rs +++ b/axum/src/middleware/mod.rs @@ -8,19 +8,14 @@ mod map_request; mod map_response; pub use self::from_extractor::{ - from_extractor, from_extractor_with_state, from_extractor_with_state_arc, FromExtractor, - FromExtractorLayer, -}; -pub use self::from_fn::{ - from_fn, from_fn_with_state, from_fn_with_state_arc, FromFn, FromFnLayer, Next, + from_extractor, from_extractor_with_state, FromExtractor, FromExtractorLayer, }; +pub use self::from_fn::{from_fn, from_fn_with_state, FromFn, FromFnLayer, Next}; pub use self::map_request::{ - map_request, map_request_with_state, map_request_with_state_arc, IntoMapRequestResult, - MapRequest, MapRequestLayer, + map_request, map_request_with_state, IntoMapRequestResult, MapRequest, MapRequestLayer, }; pub use self::map_response::{ - map_response, map_response_with_state, map_response_with_state_arc, MapResponse, - MapResponseLayer, + map_response, map_response_with_state, MapResponse, MapResponseLayer, }; pub use crate::extension::AddExtension; diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 44067217..e8f3887a 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -17,7 +17,6 @@ use bytes::BytesMut; use std::{ convert::Infallible, fmt, - sync::Arc, task::{Context, Poll}, }; use tower::{service_fn, util::MapResponseLayer}; @@ -85,6 +84,7 @@ macro_rules! top_level_service_fn { T::Response: IntoResponse + 'static, T::Future: Send + 'static, B: Send + 'static, + S: Clone, { on_service(MethodFilter::$method, svc) } @@ -145,7 +145,7 @@ macro_rules! top_level_handler_fn { H: Handler, B: Send + 'static, T: 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { on(MethodFilter::$method, handler) } @@ -328,6 +328,7 @@ where T::Response: IntoResponse + 'static, T::Future: Send + 'static, B: Send + 'static, + S: Clone, { MethodRouter::new().on_service(filter, svc) } @@ -391,6 +392,7 @@ where T::Response: IntoResponse + 'static, T::Future: Send + 'static, B: Send + 'static, + S: Clone, { MethodRouter::new() .fallback_service(svc) @@ -430,7 +432,7 @@ where H: Handler, B: Send + 'static, T: 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { MethodRouter::new().on(filter, handler) } @@ -477,7 +479,7 @@ where H: Handler, B: Send + 'static, T: 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { MethodRouter::new().fallback(handler).skip_allow_header() } @@ -570,6 +572,7 @@ impl fmt::Debug for MethodRouter { impl MethodRouter where B: Send + 'static, + S: Clone, { /// Chain an additional handler that will accept requests matching the given /// `MethodFilter`. @@ -705,6 +708,7 @@ where impl MethodRouter where B: Send + 'static, + S: Clone, { /// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all /// requests. @@ -731,13 +735,6 @@ where /// /// See [`State`](crate::extract::State) for more details about accessing state. pub fn with_state(self, state: S) -> WithState { - self.with_state_arc(Arc::new(state)) - } - - /// Provide the [`Arc`]'ed state. - /// - /// See [`State`](crate::extract::State) for more details about accessing state. - pub fn with_state_arc(self, state: Arc) -> WithState { WithState { get: self.get.into_route(&state), head: self.head.into_route(&state), @@ -752,7 +749,7 @@ where } } - pub(crate) fn map_state(self, state: &Arc) -> MethodRouter + pub(crate) fn map_state(self, state: &S) -> MethodRouter where E: 'static, S: 'static, @@ -841,6 +838,7 @@ where methods: &[&'static str], ) where MethodEndpoint: Clone, + S: Clone, { if endpoint_filter.contains(filter) { if out.is_some() { @@ -1174,6 +1172,7 @@ impl Clone for MethodRouter { impl Default for MethodRouter where B: Send + 'static, + S: Clone, { fn default() -> Self { Self::new() @@ -1186,7 +1185,10 @@ enum MethodEndpoint { BoxedHandler(BoxedHandler), } -impl MethodEndpoint { +impl MethodEndpoint +where + S: Clone, +{ fn is_some(&self) -> bool { matches!(self, Self::Route(_) | Self::BoxedHandler(_)) } @@ -1211,7 +1213,7 @@ impl MethodEndpoint { } } - fn map_state(self, state: &Arc) -> MethodEndpoint { + fn map_state(self, state: &S) -> MethodEndpoint { match self { Self::None => MethodEndpoint::None, Self::Route(route) => MethodEndpoint::Route(route), @@ -1237,7 +1239,7 @@ impl MethodEndpoint { } } - fn into_route(self, state: &Arc) -> Option> { + fn into_route(self, state: &S) -> Option> { match self { Self::None => None, Self::Route(route) => Some(route), diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 35068adf..aa5ada01 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -65,13 +65,16 @@ impl RouteId { /// The router type for composing handlers and services. pub struct Router { - state: Option>, + state: Option, routes: HashMap>, node: Arc, fallback: Fallback, } -impl Clone for Router { +impl Clone for Router +where + S: Clone, +{ fn clone(&self) -> Self { Self { state: self.state.clone(), @@ -85,7 +88,7 @@ impl Clone for Router { impl Default for Router where B: HttpBody + Send + 'static, - S: Default + Send + Sync + 'static, + S: Default + Clone + Send + Sync + 'static, { fn default() -> Self { Self::with_state(S::default()) @@ -125,7 +128,7 @@ where impl Router where B: HttpBody + Send + 'static, - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { /// Create a new `Router` with the given state. /// @@ -134,39 +137,6 @@ where /// Unless you add additional routes this will respond with `404 Not Found` to /// all requests. pub fn with_state(state: S) -> Self { - Self::with_state_arc(Arc::new(state)) - } - - /// Create a new `Router` with the given [`Arc`]'ed state. - /// - /// See [`State`] for more details about accessing state. - /// - /// Unless you add additional routes this will respond with `404 Not Found` to - /// all requests. - /// - /// Note that the state type you extract with [`State`] must implement [`FromRef`]. If - /// you're extracting `S` itself that requires `S` to implement `Clone`. That is still the - /// case, even if you're using this method: - /// - /// ``` - /// use axum::{Router, routing::get, extract::State}; - /// use std::sync::Arc; - /// - /// // `AppState` must implement `Clone` to be extracted... - /// #[derive(Clone)] - /// struct AppState {} - /// - /// // ...even though we're wrapping it an an `Arc` - /// let state = Arc::new(AppState {}); - /// - /// let app: Router = Router::with_state_arc(state).route("/", get(handler)); - /// - /// async fn handler(state: State) {} - /// ``` - /// - /// [`FromRef`]: crate::extract::FromRef - /// [`State`]: crate::extract::State - pub fn with_state_arc(state: Arc) -> Self { Self { state: Some(state), routes: Default::default(), @@ -272,11 +242,11 @@ where #[track_caller] pub fn nest(self, path: &str, mut router: Router) -> Self where - S2: Send + Sync + 'static, + S2: Clone + Send + Sync + 'static, { if router.state.is_none() { let s = self.state.clone(); - router.state = match try_downcast::>, Option>>(s) { + router.state = match try_downcast::, Option>(s) { Ok(state) => state, Err(_) => panic!( "can't nest a `Router` that wants to inherit state of type `{}` \ @@ -335,7 +305,7 @@ where pub fn merge(mut self, other: R) -> Self where R: Into>, - S2: Send + Sync + 'static, + S2: Clone + Send + Sync + 'static, { let Router { state, @@ -373,7 +343,7 @@ where where B: Send + 'static, S: 'static, - S2: 'static, + S2: Clone + 'static, { r.downcast_state().unwrap() } @@ -600,8 +570,11 @@ enum Fallback { BoxedHandler(BoxedHandler), } -impl Fallback { - fn map_state(self, state: &Arc) -> Fallback { +impl Fallback +where + S: Clone, +{ + fn map_state(self, state: &S) -> Fallback { match self { Self::Default(route) => Fallback::Default(route), Self::Service(route) => Fallback::Service(route), @@ -635,7 +608,7 @@ impl Fallback { } } - fn into_route(self, state: &Arc) -> Route { + fn into_route(self, state: &S) -> Route { match self { Self::Default(route) => route, Self::Service(route) => route, diff --git a/axum/src/routing/service.rs b/axum/src/routing/service.rs index 77d2a57d..c8a708b3 100644 --- a/axum/src/routing/service.rs +++ b/axum/src/routing/service.rs @@ -33,7 +33,7 @@ where #[track_caller] pub(super) fn new(router: Router) -> Self where - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { let state = router .state @@ -45,7 +45,7 @@ where .map(|(route_id, endpoint)| { let route = match endpoint { Endpoint::MethodRouter(method_router) => { - Route::new(method_router.with_state_arc(Arc::clone(&state))) + Route::new(method_router.with_state(state.clone())) } Endpoint::Route(route) => route, }; diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 74d82623..26abe0c0 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -17,7 +17,7 @@ pub(crate) struct TestClient { impl TestClient { pub(crate) fn new(router: Router) -> Self where - S: Send + Sync + 'static, + S: Clone + Send + Sync + 'static, { Self::from_service(router.into_service()) }