From e630c88a58a0805ae950db065c9a2cfbb2ca35d4 Mon Sep 17 00:00:00 2001 From: curvature Date: Tue, 18 Jun 2024 12:13:13 +0800 Subject: [PATCH] Support clean URLs --- README.md | 1 + memory-serve/src/lib.rs | 36 ++++++++++++++++++++++++++++++++++-- static/about.html | 14 ++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 static/about.html diff --git a/README.md b/README.md index 8c39889..88d158c 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ the following configuration methods: | `[MemoryServe::html_cache_control]` | `CacheConrol::Short` | Cache control header to serve on HTML files | `[MemoryServe::cache_control]` | `CacheConrol::Medium` | Cache control header to serve on other files | `[MemoryServe::add_alias]` | `[]` | Create a route / file alias +| `[MemoryServe::enable_clean_url]` | `false` | Enable clean URLs See `Cache control` for the cache control options. diff --git a/memory-serve/src/lib.rs b/memory-serve/src/lib.rs index 3ddbc97..891a8a3 100644 --- a/memory-serve/src/lib.rs +++ b/memory-serve/src/lib.rs @@ -28,6 +28,7 @@ struct ServeOptions { cache_control: CacheControl, enable_brotli: bool, enable_gzip: bool, + enable_clean_url: bool, } impl Default for ServeOptions { @@ -40,6 +41,7 @@ impl Default for ServeOptions { cache_control: CacheControl::Medium, enable_brotli: !cfg!(debug_assertions), enable_gzip: !cfg!(debug_assertions), + enable_clean_url: false, } } } @@ -113,6 +115,15 @@ impl MemoryServe { self } + /// Whether to enable clean URLs. When set to `true`, the routing path for + /// HTML files will not include the extension so that a file located at + /// "/about.html" maps to "/about" instead of "/about.html". + pub fn enable_clean_url(mut self, enable_clean_url: bool) -> Self { + self.options.enable_clean_url = enable_clean_url; + + self + } + /// The Cache-Control header to set for HTML files. /// See [Cache control](index.html#cache-control) for options. pub fn html_cache_control(mut self, html_cache_control: CacheControl) -> Self { @@ -195,7 +206,12 @@ impl MemoryServe { router = router.route("/", get(handler)); } - router = router.route(asset.route, get(handler)); + let path = if options.enable_clean_url && asset.route.ends_with(".html") { + &asset.route[..asset.route.len() - 5] + } else { + asset.route + }; + router = router.route(path, get(handler)); // add all aliases that point to the asset route for (from, to) in self.aliases.iter() { @@ -260,6 +276,7 @@ mod tests { assert_eq!( routes, [ + "/about.html", "/assets/icon.jpg", "/assets/index.css", "/assets/index.js", @@ -270,6 +287,7 @@ mod tests { assert_eq!( content_types, [ + "text/html", "image/jpeg", "text/css", "application/javascript", @@ -278,11 +296,12 @@ mod tests { ] ); if cfg!(debug_assertions) { - assert_eq!(etags, ["", "", "", "", ""]); + assert_eq!(etags, ["", "", "", "", "", ""]); } else { assert_eq!( etags, [ + "56a0dcb83ec56b6c967966a1c06c7b1392e261069d0844aa4e910ca5c1e8cf58", "e64f4683bf82d854df40b7246666f6f0816666ad8cd886a8e159535896eb03d6", "ec4edeea111c854901385011f403e1259e3f1ba016dcceabb6d566316be3677b", "86a7fdfd19700843e5f7344a63d27e0b729c2554c8572903ceee71f5658d2ecf", @@ -402,6 +421,19 @@ mod tests { assert_eq!(code, 200); } + #[tokio::test] + async fn clean_url() { + let memory_router = MemoryServe::new(load_assets!("../static")) + .enable_clean_url(true) + .into_router(); + + let (code, _) = get(memory_router.clone(), "/about.html", "accept", "*").await; + assert_eq!(code, 404); + + let (code, _) = get(memory_router.clone(), "/about", "accept", "*").await; + assert_eq!(code, 200); + } + #[tokio::test] async fn fallback() { let memory_router = MemoryServe::new(load_assets!("../static")).into_router(); diff --git a/static/about.html b/static/about.html new file mode 100644 index 0000000..517ffe3 --- /dev/null +++ b/static/about.html @@ -0,0 +1,14 @@ + + + + + + RUST IS AWESOME + + + + + +

About Page

+ +