diff --git a/axum-macros/CHANGELOG.md b/axum-macros/CHANGELOG.md index 3346a5ea..d5b25965 100644 --- a/axum-macros/CHANGELOG.md +++ b/axum-macros/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased - **added:** Add `#[derive(TypedPath)]` for use with axum-extra's new "type safe" routing API ([#756]) +- **breaking:** Routes are now required to start with `/`. Previously empty routes or routes such + as `:foo` would be accepted but most likely result in bugs ([#823]) + +[#823]: https://github.com/tokio-rs/axum/pull/823 # 0.1.0 (31. January, 2022) diff --git a/axum-macros/src/typed_path.rs b/axum-macros/src/typed_path.rs index 3336584f..b1416c8b 100644 --- a/axum-macros/src/typed_path.rs +++ b/axum-macros/src/typed_path.rs @@ -284,6 +284,16 @@ fn captures_from_path(segments: &[Segment]) -> Vec { } fn parse_path(path: &LitStr) -> syn::Result> { + let value = path.value(); + if value.is_empty() { + return Err(syn::Error::new_spanned( + path, + "paths must start with a `/`. Use \"/\" for root routes", + )); + } else if !path.value().starts_with('/') { + return Err(syn::Error::new_spanned(path, "paths must start with a `/`")); + } + path.value() .split('/') .map(|segment| { diff --git a/axum-macros/tests/typed_path/fail/route_not_starting_with_slash.rs b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash.rs new file mode 100644 index 00000000..bdba08af --- /dev/null +++ b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash.rs @@ -0,0 +1,7 @@ +use axum_extra::routing::TypedPath; + +#[derive(TypedPath)] +#[typed_path("")] +struct MyPath; + +fn main() {} diff --git a/axum-macros/tests/typed_path/fail/route_not_starting_with_slash.stderr b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash.stderr new file mode 100644 index 00000000..60cb3fa8 --- /dev/null +++ b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash.stderr @@ -0,0 +1,5 @@ +error: paths must start with a `/`. Use "/" for root routes + --> tests/typed_path/fail/route_not_starting_with_slash.rs:4:14 + | +4 | #[typed_path("")] + | ^^ diff --git a/axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs new file mode 100644 index 00000000..33ae38d6 --- /dev/null +++ b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs @@ -0,0 +1,7 @@ +use axum_extra::routing::TypedPath; + +#[derive(TypedPath)] +#[typed_path(":foo")] +struct MyPath; + +fn main() {} diff --git a/axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.stderr b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.stderr new file mode 100644 index 00000000..db8e40f0 --- /dev/null +++ b/axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.stderr @@ -0,0 +1,5 @@ +error: paths must start with a `/` + --> tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs:4:14 + | +4 | #[typed_path(":foo")] + | ^^^^^^ diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index ed3c0f48..dd10a6d2 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -60,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **breaking:** `AddExtensionLayer` has been removed. Use `Extension` instead. It now implements `tower::Layer` ([#807]) - **breaking:** `AddExtension` has been moved from the root module to `middleware` +- **breaking:** Routes are now required to start with `/`. Previously routes such as `:foo` would + be accepted but most likely result in bugs ([#823]) - **fixed:** Set `Allow` header when responding with `405 Method Not Allowed` ([#733]) - **fixed:** Correctly set the `Content-Length` header for response to `HEAD` requests ([#734]) @@ -82,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#801]: https://github.com/tokio-rs/axum/pull/801 [#807]: https://github.com/tokio-rs/axum/pull/807 [#819]: https://github.com/tokio-rs/axum/pull/819 +[#823]: https://github.com/tokio-rs/axum/pull/823 # 0.4.4 (13. January, 2022) diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index cd916c0a..642febf3 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -125,7 +125,9 @@ where T::Future: Send + 'static, { if path.is_empty() { - panic!("Invalid route: empty path"); + panic!("Paths must start with a `/`. Use \"/\" for root routes"); + } else if !path.starts_with('/') { + panic!("Paths must start with a `/`"); } let service = match try_downcast::, _>(service) { diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 9922a611..4dce4073 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -441,7 +441,7 @@ async fn static_and_dynamic_paths() { } #[tokio::test] -#[should_panic(expected = "Invalid route: empty path")] +#[should_panic(expected = "Paths must start with a `/`. Use \"/\" for root routes")] async fn empty_route() { let app = Router::new().route("", get(|| async {})); TestClient::new(app); @@ -678,3 +678,10 @@ async fn head_with_middleware_applied() { // is compressed assert!(!res.headers().contains_key("content-length")); } + +#[tokio::test] +#[should_panic(expected = "Paths must start with a `/`")] +async fn routes_must_start_with_slash() { + let app = Router::new().route(":foo", get(|| async {})); + TestClient::new(app); +}