From 8eed999611326ddafae3ec9572d3d965b992ee0a Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Sat, 14 Oct 2023 08:37:31 -0400 Subject: [PATCH] fix: properly handle trailing `/` in splat routes (closes #1764) (#1890) --- router/Cargo.toml | 1 + router/src/history/url.rs | 4 +-- router/src/matching/matcher.rs | 14 ++++++++++- router/tests/matcher.rs | 46 ++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/router/Cargo.toml b/router/Cargo.toml index 7368c6a094..c6ad810e01 100644 --- a/router/Cargo.toml +++ b/router/Cargo.toml @@ -31,6 +31,7 @@ wasm-bindgen = { version = "0.2" } wasm-bindgen-futures = { version = "0.4" } lru = { version = "0.11", optional = true } serde_json = "1.0.96" +itertools = "0.11.0" [dependencies.web-sys] version = "0.3" diff --git a/router/src/history/url.rs b/router/src/history/url.rs index 078459b9d5..7efd780f54 100644 --- a/router/src/history/url.rs +++ b/router/src/history/url.rs @@ -36,8 +36,8 @@ impl TryFrom<&str> for Url { type Error = String; fn try_from(url: &str) -> Result { - let fake_host = "http://leptos"; - let url = web_sys::Url::new_with_base(url, fake_host).map_js_error()?; + let url = + web_sys::Url::new(&format!("http://leptos{url}")).map_js_error()?; Ok(Self { origin: url.origin(), pathname: url.pathname(), diff --git a/router/src/matching/matcher.rs b/router/src/matching/matcher.rs index 1e9fdc1a56..a5e329834b 100644 --- a/router/src/matching/matcher.rs +++ b/router/src/matching/matcher.rs @@ -62,6 +62,8 @@ impl Matcher { // 2) location has add'l segments, there's no splat, and partial matches not allowed if loc_len < self.len || (len_diff > 0 && self.splat.is_none() && !self.partial) + || (self.splat.is_none() + && location.split('/').count() > (2 * (loc_segments.len() + 1))) { None } @@ -86,11 +88,21 @@ impl Matcher { if let Some(splat) = &self.splat { if !splat.is_empty() { - let value = if len_diff > 0 { + let mut value = if len_diff > 0 { loc_segments[self.len..].join("/") } else { "".into() }; + + // add trailing slashes to splat + let trailing_slashes = location + .chars() + .rev() + .take_while(|n| *n == '/') + .skip(1) + .collect::(); + value.push_str(&trailing_slashes); + params.insert(splat.into(), value); } } diff --git a/router/tests/matcher.rs b/router/tests/matcher.rs index 0afd908793..c544a67fcd 100644 --- a/router/tests/matcher.rs +++ b/router/tests/matcher.rs @@ -78,6 +78,22 @@ cfg_if! { ); } + #[test] + fn create_matcher_should_include_remaining_unmatched_location_as_param_when_ending_in_asterisk_and_name_2( + ) { + let matcher = Matcher::new("/foo/*something"); + let matched = matcher.test("/foo/baz/qux"); + assert_eq!( + matched, + Some(PathMatch { + path: "/foo".into(), + params: params_map!( + "something" => "baz/qux" + ) + }) + ); + } + #[test] fn create_matcher_should_include_empty_param_when_perfect_match_ends_in_asterisk_and_name() { let matcher = Matcher::new("/foo/bar/*something"); @@ -92,5 +108,35 @@ cfg_if! { }) ); } + + #[test] + fn matcher_should_include_multiple_slashes_in_a_splat_route() { + let matcher = Matcher::new("/*any"); + let matched = matcher.test("////"); + assert_eq!( + matched, + Some(PathMatch { + path: "".into(), + params: params_map!( + "any" => "///" + ) + }) + ); + } + + #[test] + fn matcher_should_include_multiple_slashes_in_a_splat_route_after_others() { + let matcher = Matcher::new("/foo/bar/*any"); + let matched = matcher.test("/foo/bar////"); + assert_eq!( + matched, + Some(PathMatch { + path: "/foo/bar".into(), + params: params_map!( + "any" => "///" + ) + }) + ); + } } }