summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-10-22 22:12:56 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-10-22 22:24:41 -0400
commit214a9e6c1fd729fc2c49eb2a5d41b5651ff5bc61 (patch)
tree2b687e9a3a98645e7d824c19079ecf158bb6c7b7
parent056e56ba2da03636eafba384322e386259817d41 (diff)
Set `charset` params on returned content types.
This is a somewhat indirect change; it removes `mime_guess` in favour of some very, uh, "bespoke" mime detection logic that hardcodes mime types for the small repertoire of file extensions actually present in the UI. `mime_guess` doesn't provide a way to set params as it exports its own `Mime` struct, which doesn't provide `with_params()`.
-rw-r--r--Cargo.lock34
-rw-r--r--Cargo.toml3
-rw-r--r--src/ui/assets.rs32
-rw-r--r--src/ui/mime.rs22
-rw-r--r--src/ui/mod.rs1
-rw-r--r--src/ui/routes/path.rs7
6 files changed, 70 insertions, 29 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1cb656d..2b8eb5e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -813,7 +813,7 @@ dependencies = [
"headers",
"hex-literal",
"itertools",
- "mime_guess",
+ "mime",
"password-hash",
"rand",
"rand_core",
@@ -827,6 +827,7 @@ dependencies = [
"tokio-stream",
"unicode-casefold",
"unicode-normalization",
+ "unix_path",
"uuid",
]
@@ -1094,16 +1095,6 @@ 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 = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2098,12 +2089,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
-name = "unicase"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
-
-[[package]]
name = "unicode-bidi"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2143,6 +2128,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
+name = "unix_path"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af8e291873ae77c4c8d9c9b34d0bee68a35b048fb39c263a5155e0e353783eaf"
+dependencies = [
+ "unix_str",
+]
+
+[[package]]
+name = "unix_str"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ace0b4755d0a2959962769239d56267f8a024fef2d9b32666b3dcd0946b0906"
+
+[[package]]
name = "url"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 101498c..e9c9616 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@ futures = "0.3.31"
headers = "0.4.0"
hex-literal = "0.4.1"
itertools = "0.13.0"
-mime_guess = "2.0.5"
+mime = "0.3.17"
password-hash = { version = "0.5.0", features = ["std"] }
rand = "0.8.5"
rand_core = { version = "0.6.4", features = ["getrandom"] }
@@ -34,6 +34,7 @@ tokio = { version = "1.40.0", features = ["rt", "macros", "rt-multi-thread"] }
tokio-stream = { version = "0.1.16", features = ["sync"] }
unicode-casefold = "0.2.0"
unicode-normalization = "0.1.24"
+unix_path = "1.0.1"
uuid = { version = "1.11.0", features = ["v4"] }
[dev-dependencies]
diff --git a/src/ui/assets.rs b/src/ui/assets.rs
index 342ba59..6a7563a 100644
--- a/src/ui/assets.rs
+++ b/src/ui/assets.rs
@@ -1,29 +1,31 @@
+use ::mime::{FromStrError, Mime};
use axum::{
http::{header, StatusCode},
response::{IntoResponse, Response},
};
-use mime_guess::Mime;
use rust_embed::EmbeddedFile;
-use crate::{error::Internal, ui::error::NotFound};
+use super::{error::NotFound, mime};
+use crate::error::Internal;
#[derive(rust_embed::Embed)]
#[folder = "target/ui"]
pub struct Assets;
impl Assets {
- pub fn load(path: impl AsRef<str>) -> Result<Asset, NotFound<String>> {
+ pub fn load(path: impl AsRef<str>) -> Result<Asset, Error> {
let path = path.as_ref();
- let mime = mime_guess::from_path(path).first_or_octet_stream();
+ let mime = mime::from_path(path)?;
Self::get(path)
.map(|file| Asset(mime, file))
- .ok_or(NotFound(format!("not found: {path}")))
+ .ok_or(Error::NotFound(path.into()))
}
pub fn index() -> Result<Asset, Internal> {
// "not found" in this case really is an internal error, as it should
- // never happen. `index.html` is a known-valid path.
+ // never happen. `index.html` is a known-valid path with a known-valid
+ // file extension.
Ok(Self::load("index.html")?)
}
}
@@ -41,3 +43,21 @@ impl IntoResponse for Asset {
.into_response()
}
}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("not found: {0}")]
+ NotFound(String),
+ #[error(transparent)]
+ Mime(#[from] FromStrError),
+}
+
+impl IntoResponse for Error {
+ fn into_response(self) -> Response {
+ #[allow(clippy::match_wildcard_for_single_variants)]
+ match self {
+ Self::NotFound(_) => NotFound(self.to_string()).into_response(),
+ other => Internal::from(other).into_response(),
+ }
+ }
+}
diff --git a/src/ui/mime.rs b/src/ui/mime.rs
new file mode 100644
index 0000000..9c724f0
--- /dev/null
+++ b/src/ui/mime.rs
@@ -0,0 +1,22 @@
+use mime::Mime;
+use unix_path::Path;
+
+// Extremely manual; using `std::path` here would result in platform-dependent behaviour when it's not appropriate (the URLs passed here always use `/` and are parsed like URLs). Using `unix_path` might be an option, but it's not clearly
+pub fn from_path<P>(path: P) -> Result<Mime, mime::FromStrError>
+where
+ P: AsRef<Path>,
+{
+ let path = path.as_ref();
+ let extension = path.extension().and_then(|ext| ext.to_str());
+ let mime = match extension {
+ Some("css") => "text/css; charset=utf-8",
+ Some("js") => "text/javascript; charset=utf-8",
+ Some("json") => "application/json",
+ Some("html") => "text/html; charset=utf-8",
+ Some("png") => "image/png",
+ _ => "application/octet-stream",
+ };
+ let mime = mime.parse()?;
+
+ Ok(mime)
+}
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index c145382..f8caa48 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -1,6 +1,7 @@
mod assets;
mod error;
mod middleware;
+mod mime;
mod routes;
pub use self::routes::router;
diff --git a/src/ui/routes/path.rs b/src/ui/routes/path.rs
index 2e9a657..a387552 100644
--- a/src/ui/routes/path.rs
+++ b/src/ui/routes/path.rs
@@ -1,12 +1,9 @@
pub mod get {
use axum::extract::Path;
- use crate::ui::{
- assets::{Asset, Assets},
- error::NotFound,
- };
+ use crate::ui::assets::{Asset, Assets, Error};
- pub async fn handler(Path(path): Path<String>) -> Result<Asset, NotFound<String>> {
+ pub async fn handler(Path(path): Path<String>) -> Result<Asset, Error> {
Assets::load(path)
}
}