Skip to content

Commit

Permalink
feat: optional redirect for url rewrites feature
Browse files Browse the repository at this point in the history
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
```
  • Loading branch information
joseluisq committed Jun 26, 2023
1 parent 506f54e commit d70ae04
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 10 deletions.
14 changes: 12 additions & 2 deletions docs/content/features/url-rewrites.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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.
Expand All @@ -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

Expand All @@ -38,4 +47,5 @@ destination = "/assets/generic1.png"
[[advanced.rewrites]]
source = "**/*.{jpg,jpeg}"
destination = "/images/generic2.png"
redirect = 302
```
28 changes: 25 additions & 3 deletions src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/rewrites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ use crate::settings::Rewrites;
pub fn rewrite_uri_path<'a>(
uri_path: &'a str,
rewrites_opts_vec: &'a Option<Vec<Rewrites>>,
) -> 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);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/settings/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand All @@ -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<RedirectsKind>,
}

/// Advanced server options only available in configuration file mode.
Expand Down
5 changes: 5 additions & 0 deletions src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<RedirectsKind>,
}

/// The `Redirects` file options.
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions tests/toml/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit d70ae04

Please sign in to comment.