fix: URL-encoded filename when downloading in safari (#203)

* fix: URL-encoded filename when downloading in safari

* add test
This commit is contained in:
sigoden 2023-03-31 22:52:07 +08:00 committed by GitHub
parent e43554b795
commit fb5b50f059
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 11 deletions

View file

@ -479,13 +479,7 @@ impl Server {
async fn handle_zip_dir(&self, path: &Path, head_only: bool, res: &mut Response) -> Result<()> {
let (mut writer, reader) = tokio::io::duplex(BUF_SIZE);
let filename = try_get_file_name(path)?;
res.headers_mut().insert(
CONTENT_DISPOSITION,
HeaderValue::from_str(&format!(
"attachment; filename=\"{}.zip\"",
encode_uri(filename),
))?,
);
set_content_diposition(res, false, &format!("{}.zip", filename))?;
res.headers_mut()
.insert("content-type", HeaderValue::from_static("application/zip"));
if head_only {
@ -644,10 +638,7 @@ impl Server {
);
let filename = try_get_file_name(path)?;
res.headers_mut().insert(
CONTENT_DISPOSITION,
HeaderValue::from_str(&format!("inline; filename=\"{}\"", encode_uri(filename),))?,
);
set_content_diposition(res, true, filename)?;
res.headers_mut().typed_insert(AcceptRanges::bytes());
@ -1359,6 +1350,21 @@ fn status_no_content(res: &mut Response) {
*res.status_mut() = StatusCode::NO_CONTENT;
}
fn set_content_diposition(res: &mut Response, inline: bool, filename: &str) -> Result<()> {
let kind = if inline { "inline" } else { "attachment" };
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),
))?
};
res.headers_mut().insert(CONTENT_DISPOSITION, value);
Ok(())
}
fn is_hidden(hidden: &[String], file_name: &str, is_dir_type: bool) -> bool {
hidden.iter().any(|v| {
if is_dir_type {

View file

@ -184,6 +184,17 @@ fn get_file_404(server: TestServer) -> Result<(), Error> {
Ok(())
}
#[rstest]
fn get_file_emoji_path(server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}{BIN_FILE}", server.url()))?;
assert_eq!(resp.status(), 200);
assert_eq!(
resp.headers().get("content-disposition").unwrap(),
"inline; filename=\"😀.bin\"; filename*=UTF-8''%F0%9F%98%80.bin"
);
Ok(())
}
#[rstest]
fn get_file_edit(server: TestServer) -> Result<(), Error> {
let resp = fetch!(b"GET", format!("{}index.html?edit", server.url())).send()?;