diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 7acaf796f8..51216b4638 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased - **fixed:** Make `Router` cheaper to clone ([#1123]) +- **fixed:** Fix possible panic when doing trailing slash redirect ([#1124]) [#1123]: https://github.com/tokio-rs/axum/pull/1123 +[#1124]: https://github.com/tokio-rs/axum/pull/1124 # 0.5.9 (20. June, 2022) diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 18f48ec693..11ac540719 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -500,29 +500,35 @@ where match self.node.at(&path) { Ok(match_) => self.call_route(match_, req), - Err(MatchError::MissingTrailingSlash) => { - let new_uri = replace_trailing_slash(req.uri(), &format!("{}/", &path)); + Err(err) => { + let mut fallback = match &self.fallback { + Fallback::Default(inner) => inner.clone(), + Fallback::Custom(inner) => inner.clone(), + }; - RouteFuture::from_response( - Redirect::permanent(&new_uri.to_string()).into_response(), - ) - } - Err(MatchError::ExtraTrailingSlash) => { - let new_uri = replace_trailing_slash(req.uri(), path.strip_suffix('/').unwrap()); + let new_uri = match err { + MatchError::MissingTrailingSlash => { + replace_path(req.uri(), &format!("{}/", &path)) + } + MatchError::ExtraTrailingSlash => { + replace_path(req.uri(), path.strip_suffix('/').unwrap()) + } + MatchError::NotFound => None, + }; - RouteFuture::from_response( - Redirect::permanent(&new_uri.to_string()).into_response(), - ) + if let Some(new_uri) = new_uri { + RouteFuture::from_response( + Redirect::permanent(&new_uri.to_string()).into_response(), + ) + } else { + fallback.call(req) + } } - Err(MatchError::NotFound) => match &self.fallback { - Fallback::Default(inner) => inner.clone().call(req), - Fallback::Custom(inner) => inner.clone().call(req), - }, } } } -fn replace_trailing_slash(uri: &Uri, new_path: &str) -> Uri { +fn replace_path(uri: &Uri, new_path: &str) -> Option { let mut new_path_and_query = new_path.to_owned(); if let Some(query) = uri.query() { new_path_and_query.push('?'); @@ -532,7 +538,7 @@ fn replace_trailing_slash(uri: &Uri, new_path: &str) -> Uri { let mut parts = uri.clone().into_parts(); parts.path_and_query = Some(new_path_and_query.parse().unwrap()); - Uri::from_parts(parts).unwrap() + Uri::from_parts(parts).ok() } /// Wrapper around `matchit::Router` that supports merging two `Router`s. @@ -640,3 +646,14 @@ fn traits() { use crate::test_helpers::*; assert_send::>(); } + +// https://github.com/tokio-rs/axum/issues/1122 +#[test] +fn test_replace_trailing_slash() { + let uri = "api.ipify.org:80".parse::().unwrap(); + assert!(uri.scheme().is_none()); + assert_eq!(uri.authority(), Some(&"api.ipify.org:80".parse().unwrap())); + assert!(uri.path_and_query().is_none()); + + replace_path(&uri, "/foo"); +}