Skip to content

Commit 88f9c87

Browse files
committed
backport PR 2865 to main
backport #2865 to main
1 parent 19b99ea commit 88f9c87

File tree

5 files changed

+40
-28
lines changed

5 files changed

+40
-28
lines changed

axum/src/routing/method_routing.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,27 +1101,24 @@ where
11011101
macro_rules! call {
11021102
(
11031103
$req:expr,
1104-
$method:expr,
11051104
$method_variant:ident,
11061105
$svc:expr
11071106
) => {
1108-
if $method == Method::$method_variant {
1107+
if *req.method() == Method::$method_variant {
11091108
match $svc {
11101109
MethodEndpoint::None => {}
11111110
MethodEndpoint::Route(route) => {
1112-
return route.clone().oneshot_inner($req);
1111+
return route.clone().oneshot_inner_owned($req);
11131112
}
11141113
MethodEndpoint::BoxedHandler(handler) => {
1115-
let mut route = handler.clone().into_route(state);
1116-
return route.oneshot_inner($req);
1114+
let route = handler.clone().into_route(state);
1115+
return route.oneshot_inner_owned($req);
11171116
}
11181117
}
11191118
}
11201119
};
11211120
}
11221121

1123-
let method = req.method().clone();
1124-
11251122
// written with a pattern match like this to ensure we call all routes
11261123
let Self {
11271124
get,
@@ -1137,16 +1134,16 @@ where
11371134
allow_header,
11381135
} = self;
11391136

1140-
call!(req, method, HEAD, head);
1141-
call!(req, method, HEAD, get);
1142-
call!(req, method, GET, get);
1143-
call!(req, method, POST, post);
1144-
call!(req, method, OPTIONS, options);
1145-
call!(req, method, PATCH, patch);
1146-
call!(req, method, PUT, put);
1147-
call!(req, method, DELETE, delete);
1148-
call!(req, method, TRACE, trace);
1149-
call!(req, method, CONNECT, connect);
1137+
call!(req, HEAD, head);
1138+
call!(req, HEAD, get);
1139+
call!(req, GET, get);
1140+
call!(req, POST, post);
1141+
call!(req, OPTIONS, options);
1142+
call!(req, PATCH, patch);
1143+
call!(req, PUT, put);
1144+
call!(req, DELETE, delete);
1145+
call!(req, TRACE, trace);
1146+
call!(req, CONNECT, connect);
11501147

11511148
let future = fallback.clone().call_with_state(req, state);
11521149

axum/src/routing/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -668,12 +668,12 @@ where
668668
}
669669
}
670670

671-
fn call_with_state(&mut self, req: Request, state: S) -> RouteFuture<E> {
671+
fn call_with_state(self, req: Request, state: S) -> RouteFuture<E> {
672672
match self {
673-
Fallback::Default(route) | Fallback::Service(route) => route.oneshot_inner(req),
673+
Fallback::Default(route) | Fallback::Service(route) => route.oneshot_inner_owned(req),
674674
Fallback::BoxedHandler(handler) => {
675-
let mut route = handler.clone().into_route(state);
676-
route.oneshot_inner(req)
675+
let route = handler.clone().into_route(state);
676+
route.oneshot_inner_owned(req)
677677
}
678678
}
679679
}

axum/src/routing/path_router.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,11 @@ where
367367
}
368368
}
369369

370-
let path = req.uri().path().to_owned();
370+
// split apart to get multiple mutable references to parts,
371+
// this avoids the need to clone the URI/path.
372+
let (mut parts, body) = req.into_parts();
371373

372-
match self.node.at(&path) {
374+
match self.node.at(parts.uri.path()) {
373375
Ok(match_) => {
374376
let id = *match_.value;
375377

@@ -378,27 +380,28 @@ where
378380
crate::extract::matched_path::set_matched_path_for_request(
379381
id,
380382
&self.node.route_id_to_path,
381-
req.extensions_mut(),
383+
&mut parts.extensions,
382384
);
383385
}
384386

385-
url_params::insert_url_params(req.extensions_mut(), match_.params);
387+
url_params::insert_url_params(&mut parts.extensions, match_.params);
386388

387389
let endpoint = self
388390
.routes
389391
.get(&id)
390392
.expect("no route for id. This is a bug in axum. Please file an issue");
391393

394+
let req = Request::from_parts(parts, body);
392395
match endpoint {
393396
Endpoint::MethodRouter(method_router) => {
394397
Ok(method_router.call_with_state(req, state))
395398
}
396-
Endpoint::Route(route) => Ok(route.clone().call(req)),
399+
Endpoint::Route(route) => Ok(route.clone().call_owned(req)),
397400
}
398401
}
399402
// explicitly handle all variants in case matchit adds
400403
// new ones we need to handle differently
401-
Err(MatchError::NotFound) => Err((req, state)),
404+
Err(MatchError::NotFound) => Err((Request::from_parts(parts, body), state)),
402405
}
403406
}
404407

axum/src/routing/route.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,23 @@ impl<E> Route<E> {
4242
))
4343
}
4444

45+
/// Variant of [`Route::call`] that takes ownership of the route to avoid cloning.
46+
pub(crate) fn call_owned(self, req: Request<Body>) -> RouteFuture<E> {
47+
let req = req.map(Body::new);
48+
self.oneshot_inner_owned(req).not_top_level()
49+
}
50+
4551
pub(crate) fn oneshot_inner(&mut self, req: Request) -> RouteFuture<E> {
4652
let method = req.method().clone();
4753
RouteFuture::new(method, self.0.clone().oneshot(req))
4854
}
4955

56+
/// Variant of [`Route::oneshot_inner`] that takes ownership of the route to avoid cloning.
57+
pub(crate) fn oneshot_inner_owned(self, req: Request) -> RouteFuture<E> {
58+
let method = req.method().clone();
59+
RouteFuture::new(method, self.0.oneshot(req))
60+
}
61+
5062
pub(crate) fn layer<L, NewError>(self, layer: L) -> Route<NewError>
5163
where
5264
L: Layer<Route<E>> + Clone + Send + 'static,

axum/src/routing/tests/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,7 @@ async fn state_isnt_cloned_too_much() {
941941

942942
client.get("/").await;
943943

944-
assert_eq!(COUNT.load(Ordering::SeqCst), 4);
944+
assert_eq!(COUNT.load(Ordering::SeqCst), 3);
945945
}
946946

947947
#[crate::test]

0 commit comments

Comments
 (0)