From 0d4e224caeaeeb3b40127c00ffeabd01563d5674 Mon Sep 17 00:00:00 2001 From: novacrazy Date: Wed, 9 Oct 2024 22:16:18 +0200 Subject: [PATCH] Avoid one state clone This is an extraction of a part of https://github.com/tokio-rs/axum/pull/2865 --- axum/src/routing/method_routing.rs | 41 ++++++++++++++++-------------- axum/src/routing/route.rs | 8 ++++++ axum/src/routing/tests/mod.rs | 2 +- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 5ed4f6a9..28fa314f 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1097,32 +1097,35 @@ where } pub(crate) fn call_with_state(&self, req: Request, state: S) -> RouteFuture { + let method = req.method(); + let is_head = *method == Method::HEAD; + macro_rules! call { ( - $req:expr, - $method:expr, $method_variant:ident, $svc:expr ) => { - if $method == Method::$method_variant { + if *method == Method::$method_variant { match $svc { MethodEndpoint::None => {} MethodEndpoint::Route(route) => { - return RouteFuture::from_future(route.clone().oneshot_inner($req)) - .strip_body($method == Method::HEAD); + return RouteFuture::from_future( + route.clone().oneshot_inner_owned(req), + ) + .strip_body(is_head); } MethodEndpoint::BoxedHandler(handler) => { let route = handler.clone().into_route(state); - return RouteFuture::from_future(route.clone().oneshot_inner($req)) - .strip_body($method == Method::HEAD); + return RouteFuture::from_future( + route.clone().oneshot_inner_owned(req), + ) + .strip_body(is_head); } } } }; } - let method = req.method().clone(); - // written with a pattern match like this to ensure we call all routes let Self { get, @@ -1138,16 +1141,16 @@ where allow_header, } = self; - call!(req, method, HEAD, head); - call!(req, method, HEAD, get); - call!(req, method, GET, get); - call!(req, method, POST, post); - call!(req, method, OPTIONS, options); - call!(req, method, PATCH, patch); - call!(req, method, PUT, put); - call!(req, method, DELETE, delete); - call!(req, method, TRACE, trace); - call!(req, method, CONNECT, connect); + call!(HEAD, head); + call!(HEAD, get); + call!(GET, get); + call!(POST, post); + call!(OPTIONS, options); + call!(PATCH, patch); + call!(PUT, put); + call!(DELETE, delete); + call!(TRACE, trace); + call!(CONNECT, connect); let future = fallback.clone().call_with_state(req, state); diff --git a/axum/src/routing/route.rs b/axum/src/routing/route.rs index 242e25f3..25d41daf 100644 --- a/axum/src/routing/route.rs +++ b/axum/src/routing/route.rs @@ -49,6 +49,14 @@ impl Route { self.0.get_mut().unwrap().clone().oneshot(req) } + /// Variant of [`Route::oneshot_inner`] that takes ownership of the route to avoid cloning. + pub(crate) fn oneshot_inner_owned( + self, + req: Request, + ) -> Oneshot, Request> { + self.0.into_inner().unwrap().oneshot(req) + } + pub(crate) fn layer(self, layer: L) -> Route where L: Layer> + Clone + Send + 'static, diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 144c870d..3dad0728 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -952,7 +952,7 @@ async fn state_isnt_cloned_too_much() { client.get("/").await; - assert_eq!(COUNT.load(Ordering::SeqCst), 4); + assert_eq!(COUNT.load(Ordering::SeqCst), 3); } #[crate::test]