]> OzVa Git service - ozva-cloud/commitdiff
fix: URL-encoded filename when downloading in safari (#203)
authorsigoden <sigoden@gmail.com>
Fri, 31 Mar 2023 14:52:07 +0000 (22:52 +0800)
committerGitHub <noreply@github.com>
Fri, 31 Mar 2023 14:52:07 +0000 (22:52 +0800)
* fix: URL-encoded filename when downloading in safari

* add test

src/server.rs
tests/http.rs

index d026d8c743dd331507f264036677bc92ad945071..d43b4503e58cd73f6af5b4b95ea67039933400d6 100644 (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 {
index 6ae77909cf51b2cdcf1e1ba488bcaebf02581e65..cc2e29543cf28ca01678e9db242ed6aab2efdbba 100644 (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()?;