From d181867355a46566e2e9bf2860471c642d905ed1 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 6 Jan 2022 12:12:24 +0100 Subject: [PATCH] Fix nesting `Router` at `/` leading to double slash in the path (#691) * Fix nesting router at `/` Fixes https://github.com/tokio-rs/axum/issues/690 * Allow nesting at `""` * changelog * remove outdated test --- axum/CHANGELOG.md | 5 ++++- axum/src/routing/mod.rs | 13 +++++++----- axum/src/routing/tests/mod.rs | 7 ------- axum/src/routing/tests/nest.rs | 38 +++++++++++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index a59cc72a..f7b32d26 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **fixed:** Fix using incorrect path prefix when nesting `Router`s at `/` ([#691]) +- **fixed:** Make `nest("", service)` work and mean the same as `nest("/", service)` ([#691]) + +[#691]: https://github.com/tokio-rs/axum/pull/691 # 0.4.3 (21. December, 2021) diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 788d0c1f..eb5b0b94 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -161,13 +161,14 @@ where } #[doc = include_str!("../docs/routing/nest.md")] - pub fn nest(mut self, path: &str, svc: T) -> Self + pub fn nest(mut self, mut path: &str, svc: T) -> Self where T: Service, Response = Response, Error = Infallible> + Clone + Send + 'static, T::Future: Send + 'static, { if path.is_empty() { - panic!("Invalid route: empty path"); + // nesting at `""` and `"/"` should mean the same thing + path = "/"; } if path.contains('*') { @@ -201,10 +202,12 @@ where for (id, nested_path) in node.route_id_to_path { let route = routes.remove(&id).unwrap(); - let full_path = if &*nested_path == "/" { - path.to_owned() + let full_path: Cow = if &*nested_path == "/" { + path.into() + } else if path == "/" { + (&*nested_path).into() } else { - format!("{}{}", path, nested_path) + format!("{}{}", path, nested_path).into() }; self = match route { Endpoint::MethodRouter(method_router) => self.route( diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 67e7f06e..3577387b 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -422,13 +422,6 @@ async fn empty_route() { TestClient::new(app); } -#[tokio::test] -#[should_panic(expected = "Invalid route: empty path")] -async fn empty_route_nested() { - let app = Router::new().nest("", get(|| async {})); - TestClient::new(app); -} - #[tokio::test] async fn middleware_still_run_for_unmatched_requests() { #[derive(Clone)] diff --git a/axum/src/routing/tests/nest.rs b/axum/src/routing/tests/nest.rs index b8cd3ac9..058e9123 100644 --- a/axum/src/routing/tests/nest.rs +++ b/axum/src/routing/tests/nest.rs @@ -78,7 +78,43 @@ async fn wrong_method_nest() { } #[tokio::test] -async fn nesting_at_root() { +async fn nesting_router_at_root() { + let nested = Router::new().route("/foo", get(|uri: Uri| async move { uri.to_string() })); + let app = Router::new().nest("/", nested); + + let client = TestClient::new(app); + + let res = client.get("/").send().await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); + + let res = client.get("/foo").send().await; + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.text().await, "/foo"); + + let res = client.get("/foo/bar").send().await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); +} + +#[tokio::test] +async fn nesting_router_at_empty_path() { + let nested = Router::new().route("/foo", get(|uri: Uri| async move { uri.to_string() })); + let app = Router::new().nest("", nested); + + let client = TestClient::new(app); + + let res = client.get("/").send().await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); + + let res = client.get("/foo").send().await; + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.text().await, "/foo"); + + let res = client.get("/foo/bar").send().await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); +} + +#[tokio::test] +async fn nesting_handler_at_root() { let app = Router::new().nest("/", get(|uri: Uri| async move { uri.to_string() })); let client = TestClient::new(app);