From 23d608d433e13541d3ff9a1e3a5d292c39aa4365 Mon Sep 17 00:00:00 2001 From: Defelo Date: Thu, 4 Sep 2025 20:54:29 +0200 Subject: [PATCH] Guess mime type based on path Try to determine the mime type of a file first based on its path, then based on its content. The previous behavior (looking only at the file content) inferred a mime type of `text/xml` for `.svg` files (instead of `image/svg+xml`) which prevented them from being shown when embedded e.g. in a markdown document. --- radicle-httpd/Cargo.lock | 17 +++++++++++++++++ radicle-httpd/Cargo.toml | 1 + radicle-httpd/src/raw.rs | 20 +++++++++++--------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/radicle-httpd/Cargo.lock b/radicle-httpd/Cargo.lock index 591fc8886..153d9aec8 100644 --- a/radicle-httpd/Cargo.lock +++ b/radicle-httpd/Cargo.lock @@ -1468,6 +1468,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -2019,6 +2029,7 @@ dependencies = [ "infer", "lexopt", "lru", + "mime_guess", "nonempty 0.12.0", "pretty_assertions", "radicle", @@ -2941,6 +2952,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-display-width" version = "0.3.0" diff --git a/radicle-httpd/Cargo.toml b/radicle-httpd/Cargo.toml index 4b02e3a79..7c6977c33 100644 --- a/radicle-httpd/Cargo.toml +++ b/radicle-httpd/Cargo.toml @@ -30,6 +30,7 @@ hyper = { version = "1.6.0", default-features = false } infer = { version = "0.19.0" } lexopt = { version = "0.3.1" } lru = { version = "0.16.0" } +mime_guess = { version = "2.0.5" } nonempty = { version = "0.12.0", features = ["serialize"] } radicle = { version = "0.18.0" } radicle-surf = { version = "0.22.0", default-features = false, features = ["serde"] } diff --git a/radicle-httpd/src/raw.rs b/radicle-httpd/src/raw.rs index 780b10f4e..1656d8c57 100644 --- a/radicle-httpd/src/raw.rs +++ b/radicle-httpd/src/raw.rs @@ -74,7 +74,7 @@ async fn file_by_commit_handler( let repo: Repository = repo.backend.into(); let blob = repo.blob(sha, &path)?; - blob_response(blob) + blob_response(blob, &path) } async fn archive_by_refname_handler( @@ -173,22 +173,24 @@ async fn file_by_canonical_head_handler( let repo: Repository = repo.backend.into(); let blob = repo.blob(sha, &path)?; - blob_response(blob) + blob_response(blob, &path) } -fn blob_response(blob: Blob) -> Result<(StatusCode, HeaderMap, Vec), Error> { +fn blob_response( + blob: Blob, + path: &str, +) -> Result<(StatusCode, HeaderMap, Vec), Error> { let mut response_headers = HeaderMap::new(); if blob.size() > MAX_BLOB_SIZE { return Ok::<_, Error>((StatusCode::PAYLOAD_TOO_LARGE, response_headers, vec![])); } - let content = blob.content(); - let mime = infer::get(content).map(|i| i.mime_type().to_string()); + let mime = mime_guess::from_path(path) + .first_raw() + .or_else(|| infer::get(blob.content()).map(|i| i.mime_type())) + .unwrap_or("application/octet-stream"); - response_headers.insert( - header::CONTENT_TYPE, - HeaderValue::from_str(&mime.unwrap_or("application/octet-stream".to_string()))?, - ); + response_headers.insert(header::CONTENT_TYPE, HeaderValue::from_str(mime)?); Ok::<_, Error>((StatusCode::OK, response_headers, blob.content().to_owned())) }