From 78f0e6e650444886069f4d4ae4e6593f221b98d4 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:03:00 +0200 Subject: [PATCH] Keep file name when a redirect Location is query-only Found while testing crates/fetch/src/http.rs:139 (Url::join) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/fetch/src/http.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/fetch/src/http.rs b/crates/fetch/src/http.rs index 0b1b0ae98..f99454ede 100644 --- a/crates/fetch/src/http.rs +++ b/crates/fetch/src/http.rs @@ -137,6 +137,10 @@ impl Url { } // relative to the current directory let dir = self.path.split('?').next().unwrap_or("/"); + // a query-only reference keeps the whole path, file name included (RFC 3986 ยง5.2.2) + if l.starts_with('?') { + return Url::parse(&format!("{base}{dir}{l}")); + } let dir = dir.rsplit_once('/').map_or("", |(d, _)| d); Url::parse(&format!("{base}{dir}/{l}")) } @@ -618,6 +622,12 @@ mod tests { assert!(Url::parse("https://example.com/x?secret=abc\r\nInjected: 1").is_err()); } + #[test] + fn join_query_only_keeps_file_name() { + let u = Url::parse("https://a.example/x/y/file?q=1").unwrap(); + assert_eq!(u.join("?page=2").unwrap().path, "/x/y/file?page=2"); + } + #[test] fn parses_urls() { let u = Url::parse("https://cdn.example.com/models/sam3/model.safetensors?sig=1#x").unwrap();