From 006e03ed303c5ae516d9d03171d63e342315f0a8 Mon Sep 17 00:00:00 2001 From: sigoden Date: Sat, 23 Dec 2023 15:40:41 +0800 Subject: [PATCH] fix: serve files with names containing newline char (#328) --- src/server.rs | 12 +++++++++++- tests/fixtures.rs | 9 ++++++++- tests/http.rs | 12 ++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/server.rs b/src/server.rs index 8c426c6..6324317 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1547,13 +1547,23 @@ fn status_no_content(res: &mut Response) { fn set_content_diposition(res: &mut Response, inline: bool, filename: &str) -> Result<()> { let kind = if inline { "inline" } else { "attachment" }; + let filename: String = filename + .chars() + .map(|ch| { + if ch.is_ascii_control() && ch != '\t' { + ' ' + } else { + ch + } + }) + .collect(); let value = if filename.is_ascii() { HeaderValue::from_str(&format!("{kind}; filename=\"{}\"", filename,))? } else { HeaderValue::from_str(&format!( "{kind}; filename=\"{}\"; filename*=UTF-8''{}", filename, - encode_uri(filename), + encode_uri(&filename), ))? }; res.headers_mut().insert(CONTENT_DISPOSITION, value); diff --git a/tests/fixtures.rs b/tests/fixtures.rs index a83722d..906563a 100644 --- a/tests/fixtures.rs +++ b/tests/fixtures.rs @@ -16,7 +16,14 @@ pub const BIN_FILE: &str = "😀.bin"; /// File names for testing purpose #[allow(dead_code)] -pub static FILES: &[&str] = &["test.txt", "test.html", "index.html", BIN_FILE]; +pub static FILES: &[&str] = &[ + "test.txt", + "test.html", + "index.html", + #[cfg(not(target_os = "windows"))] + "file\n1.txt", + BIN_FILE, +]; /// Directory names for testing directory don't exist #[allow(dead_code)] diff --git a/tests/http.rs b/tests/http.rs index 67fb328..88fb513 100644 --- a/tests/http.rs +++ b/tests/http.rs @@ -207,6 +207,18 @@ fn get_file_emoji_path(server: TestServer) -> Result<(), Error> { Ok(()) } +#[cfg(not(target_os = "windows"))] +#[rstest] +fn get_file_newline_path(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}file%0A1.txt", server.url()))?; + assert_eq!(resp.status(), 200); + assert_eq!( + resp.headers().get("content-disposition").unwrap(), + "inline; filename=\"file 1.txt\"" + ); + Ok(()) +} + #[rstest] fn get_file_edit(server: TestServer) -> Result<(), Error> { let resp = fetch!(b"GET", format!("{}index.html?edit", server.url())).send()?;