Avoid one state clone

This is an extraction of a part of https://github.com/tokio-rs/axum/pull/2865
This commit is contained in:
novacrazy 2024-10-09 22:16:18 +02:00 committed by Jonas Platte
parent c2e7fc0b3d
commit 0d4e224cae
No known key found for this signature in database
GPG key ID: 7D261D771D915378
3 changed files with 31 additions and 20 deletions

View file

@ -1097,32 +1097,35 @@ where
} }
pub(crate) fn call_with_state(&self, req: Request, state: S) -> RouteFuture<E> { pub(crate) fn call_with_state(&self, req: Request, state: S) -> RouteFuture<E> {
let method = req.method();
let is_head = *method == Method::HEAD;
macro_rules! call { macro_rules! call {
( (
$req:expr,
$method:expr,
$method_variant:ident, $method_variant:ident,
$svc:expr $svc:expr
) => { ) => {
if $method == Method::$method_variant { if *method == Method::$method_variant {
match $svc { match $svc {
MethodEndpoint::None => {} MethodEndpoint::None => {}
MethodEndpoint::Route(route) => { MethodEndpoint::Route(route) => {
return RouteFuture::from_future(route.clone().oneshot_inner($req)) return RouteFuture::from_future(
.strip_body($method == Method::HEAD); route.clone().oneshot_inner_owned(req),
)
.strip_body(is_head);
} }
MethodEndpoint::BoxedHandler(handler) => { MethodEndpoint::BoxedHandler(handler) => {
let route = handler.clone().into_route(state); let route = handler.clone().into_route(state);
return RouteFuture::from_future(route.clone().oneshot_inner($req)) return RouteFuture::from_future(
.strip_body($method == Method::HEAD); 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 // written with a pattern match like this to ensure we call all routes
let Self { let Self {
get, get,
@ -1138,16 +1141,16 @@ where
allow_header, allow_header,
} = self; } = self;
call!(req, method, HEAD, head); call!(HEAD, head);
call!(req, method, HEAD, get); call!(HEAD, get);
call!(req, method, GET, get); call!(GET, get);
call!(req, method, POST, post); call!(POST, post);
call!(req, method, OPTIONS, options); call!(OPTIONS, options);
call!(req, method, PATCH, patch); call!(PATCH, patch);
call!(req, method, PUT, put); call!(PUT, put);
call!(req, method, DELETE, delete); call!(DELETE, delete);
call!(req, method, TRACE, trace); call!(TRACE, trace);
call!(req, method, CONNECT, connect); call!(CONNECT, connect);
let future = fallback.clone().call_with_state(req, state); let future = fallback.clone().call_with_state(req, state);

View file

@ -49,6 +49,14 @@ impl<E> Route<E> {
self.0.get_mut().unwrap().clone().oneshot(req) 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<BoxCloneService<Request, Response, E>, Request> {
self.0.into_inner().unwrap().oneshot(req)
}
pub(crate) fn layer<L, NewError>(self, layer: L) -> Route<NewError> pub(crate) fn layer<L, NewError>(self, layer: L) -> Route<NewError>
where where
L: Layer<Route<E>> + Clone + Send + 'static, L: Layer<Route<E>> + Clone + Send + 'static,

View file

@ -952,7 +952,7 @@ async fn state_isnt_cloned_too_much() {
client.get("/").await; client.get("/").await;
assert_eq!(COUNT.load(Ordering::SeqCst), 4); assert_eq!(COUNT.load(Ordering::SeqCst), 3);
} }
#[crate::test] #[crate::test]