From 06955e9061413b14f39aee7c24259f51463e0d78 Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:59:29 +0200 Subject: [PATCH] feat: `redirect` option for url rewrites feature (#231) the values can be: - `301` for "Moved Permanently" - `302` for "Found" (Temporary Redirect) example: ```toml [[advanced.rewrites]] source = "**/*.{jpg,jpeg}" destination = "/images/generic.png" redirect = 301 ``` --- docs/content/features/url-rewrites.md | 14 ++++++++++++-- src/handler.rs | 28 +++++++++++++++++++++++++--- src/rewrites.rs | 4 ++-- src/settings/file.rs | 4 +++- src/settings/mod.rs | 5 +++++ tests/toml/config.toml | 5 +++-- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/docs/content/features/url-rewrites.md b/docs/content/features/url-rewrites.md index 47d99ea..9e23ef1 100644 --- a/docs/content/features/url-rewrites.md +++ b/docs/content/features/url-rewrites.md @@ -1,6 +1,6 @@ # URL Rewrites -**SWS** provides the ability to rewrite request URLs with pattern matching support. +**SWS** provides the ability to rewrite request URLs with pattern-matching support. URI rewrites are particularly useful with pattern matching ([globs](https://en.wikipedia.org/wiki/Glob_(programming))), as the server can accept any URL that matches the pattern and let the client-side code decide what to display. @@ -12,6 +12,7 @@ Each table entry should have two key/value pairs: - One `source` key containing a string _glob pattern_. - One `destination` string containing the local file path. +- Optional `redirect` number containing the HTTP response code. !!! info "Note" The incoming request(s) will reach the `destination` only if the request(s) URI matches the `source` pattern. @@ -22,7 +23,15 @@ The source is a [Glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) ### Destination -A local file path which must exist. It has to look something like `/some/directory/file.html`. It is worth noting that the `/` at the beginning indicates the server's root directory. +A local file path must exist. It has to look something like `/some/directory/file.html`. It is worth noting that the `/` at the beginning indicates the server's root directory. + +### Redirect + +An optional number that indicates the HTTP response code (redirect). +The values can be: + +- `301` for "Moved Permanently" +- `302` for "Found" (Temporary Redirect) ## Examples @@ -38,4 +47,5 @@ destination = "/assets/generic1.png" [[advanced.rewrites]] source = "**/*.{jpg,jpeg}" destination = "/images/generic2.png" +redirect = 302 ``` diff --git a/src/handler.rs b/src/handler.rs index b13932c..d5abf44 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -23,7 +23,7 @@ use crate::{ control_headers, cors, custom_headers, error_page, exts::http::MethodExt, redirects, rewrites, security_headers, - settings::Advanced, + settings::{file::RedirectsKind, Advanced}, static_files::{self, HandleOpts}, Error, Result, }; @@ -232,8 +232,30 @@ impl RequestHandler { } // Rewrites - if let Some(uri) = rewrites::rewrite_uri_path(uri_path, &advanced.rewrites) { - uri_path = uri + if let Some(rewrite) = rewrites::rewrite_uri_path(uri_path, &advanced.rewrites) { + uri_path = rewrite.destination.as_str(); + if let Some(redirect_type) = &rewrite.redirect { + let loc = match HeaderValue::from_str(uri_path) { + Ok(val) => val, + Err(err) => { + tracing::error!("invalid header value from current uri: {:?}", err); + return error_page::error_response( + uri, + method, + &StatusCode::INTERNAL_SERVER_ERROR, + &self.opts.page404, + &self.opts.page50x, + ); + } + }; + let mut resp = Response::new(Body::empty()); + resp.headers_mut().insert(hyper::header::LOCATION, loc); + *resp.status_mut() = match redirect_type { + RedirectsKind::Permanent => StatusCode::MOVED_PERMANENTLY, + RedirectsKind::Temporary => StatusCode::FOUND, + }; + return Ok(resp); + } } } diff --git a/src/rewrites.rs b/src/rewrites.rs index 66e5550..ebff85a 100644 --- a/src/rewrites.rs +++ b/src/rewrites.rs @@ -13,12 +13,12 @@ use crate::settings::Rewrites; pub fn rewrite_uri_path<'a>( uri_path: &'a str, rewrites_opts_vec: &'a Option>, -) -> Option<&'a str> { +) -> Option<&'a Rewrites> { if let Some(rewrites_vec) = rewrites_opts_vec { for rewrites_entry in rewrites_vec.iter() { // Match source glob pattern against request uri path if rewrites_entry.source.is_match(uri_path) { - return Some(rewrites_entry.destination.as_str()); + return Some(rewrites_entry); } } } diff --git a/src/settings/file.rs b/src/settings/file.rs index 5651050..3feee8d 100644 --- a/src/settings/file.rs +++ b/src/settings/file.rs @@ -74,7 +74,7 @@ pub struct Redirects { pub source: String, /// Redirect destination. pub destination: String, - /// Redirect type. + /// Redirect type either 301 (Moved Permanently) or 302 (Found). pub kind: RedirectsKind, } @@ -86,6 +86,8 @@ pub struct Rewrites { pub source: String, /// Rewrite destination. pub destination: String, + /// Optional redirect type either 301 (Moved Permanently) or 302 (Found). + pub redirect: Option, } /// Advanced server options only available in configuration file mode. diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 6ee58d6..b259bf3 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -21,6 +21,8 @@ pub use cli::Commands; use cli::General; +use self::file::RedirectsKind; + /// The `headers` file options. pub struct Headers { /// Source pattern glob matcher @@ -35,6 +37,8 @@ pub struct Rewrites { pub source: GlobMatcher, /// A local file that must exist pub destination: String, + /// Optional redirect type either 301 (Moved Permanently) or 302 (Found). + pub redirect: Option, } /// The `Redirects` file options. @@ -327,6 +331,7 @@ impl Settings { rewrites_vec.push(Rewrites { source, destination: rewrites_entry.destination.to_owned(), + redirect: rewrites_entry.redirect.to_owned(), }); } Some(rewrites_vec) diff --git a/tests/toml/config.toml b/tests/toml/config.toml index f59a6db..1043890 100644 --- a/tests/toml/config.toml +++ b/tests/toml/config.toml @@ -109,9 +109,10 @@ kind = 302 ### URL Rewrites [[advanced.rewrites]] -source = "**/*.{png,ico,gif}" +source = "**/*.{png,gif}" destination = "/assets/favicon.ico" +# redirect = 301 [[advanced.rewrites]] source = "**/*.{jpg,jpeg}" -destination = "/images/nomad.png" +destination = "/assets/favicon.ico" -- libgit2 1.7.2