index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2022-07-06 19:32:46.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2022-07-06 19:32:46.0 +00:00:00
commit
5163564817b5e9ee058a1bf83e680a13bd983fc2 [patch]
tree
84317d1cd075afa4bd5e4d995de4f03be53f0729
parent
8353f19dc223c90e104a2925718b43372d29247c
download
5163564817b5e9ee058a1bf83e680a13bd983fc2.tar.gz

feat: rewrites with pattern matching support



Diff

 src/handler.rs         | 13 ++++++++++---
 src/lib.rs             |  1 +
 src/rewrites.rs        | 19 +++++++++++++++++++
 src/settings/file.rs   | 11 ++++++++++-
 src/settings/mod.rs    | 40 ++++++++++++++++++++++++++++++++++++++--
 tests/toml/config.toml | 16 ++++++++++++----
 6 files changed, 90 insertions(+), 10 deletions(-)

diff --git a/src/handler.rs b/src/handler.rs
index dbb7905..a749670 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -3,7 +3,7 @@ use std::{future::Future, net::SocketAddr, path::PathBuf, sync::Arc};

use crate::{
    basic_auth, compression, control_headers, cors, custom_headers, error_page, fallback_page,
    security_headers, settings::Advanced, static_files, Error, Result,
    rewrites, security_headers, settings::Advanced, static_files, Error, Result,
};

/// It defines options for a request handler.
@@ -43,7 +43,7 @@ impl RequestHandler {
        let uri = req.uri();

        let root_dir = &self.opts.root_dir;
        let uri_path = uri.path();
        let mut uri_path = uri.path();
        let uri_query = uri.query();
        let dir_listing = self.opts.dir_listing;
        let dir_listing_order = self.opts.dir_listing_order;
@@ -65,7 +65,7 @@ impl RequestHandler {
        );

        async move {
            // Check for disallowed HTTP methods and reject request accordently
            // Check for disallowed HTTP methods and reject requests accordingly
            if !(method == Method::GET || method == Method::HEAD || method == Method::OPTIONS) {
                return error_page::error_response(
                    uri,
@@ -128,6 +128,13 @@ impl RequestHandler {
                }
            }

            // Rewrites
            if let Some(advanced) = &self.opts.advanced_opts {
                if let Some(uri) = rewrites::rewrite_uri_path(uri_path, &advanced.rewrites) {
                    uri_path = uri
                }
            }

            // Static files
            match static_files::handle(
                method,
diff --git a/src/lib.rs b/src/lib.rs
index 9caa7ea..8b72a33 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@ pub mod fallback_page;
pub mod handler;
pub mod helpers;
pub mod logger;
pub mod rewrites;
pub mod security_headers;
pub mod server;
pub mod service;
diff --git a/src/rewrites.rs b/src/rewrites.rs
new file mode 100644
index 0000000..a71eee3
--- /dev/null
+++ b/src/rewrites.rs
@@ -0,0 +1,19 @@
use crate::settings::Rewrites;

/// It returns a rewrite's destination path if the current request uri
/// matches againt the provided rewrites array.
pub fn rewrite_uri_path<'a>(
    uri_path: &'a str,
    rewrites_opts_vec: &'a Option<Vec<Rewrites>>,
) -> Option<&'a str> {
    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());
            }
        }
    }

    None
}
diff --git a/src/settings/file.rs b/src/settings/file.rs
index 22be141..d54f356 100644
--- a/src/settings/file.rs
+++ b/src/settings/file.rs
@@ -7,7 +7,7 @@ use std::{collections::BTreeSet, path::PathBuf};

use crate::{helpers, Context, Result};

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub enum LogLevel {
    Error,
@@ -37,12 +37,21 @@ pub struct Headers {
    pub headers: HeaderMap,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Rewrites {
    pub source: String,
    pub destination: String,
}

/// Advanced server options only available in configuration file mode.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Advanced {
    // Headers
    pub headers: Option<Vec<Headers>>,
    // Rewrites
    pub rewrites: Option<Vec<Rewrites>>,
}

/// General server options available in configuration file mode.
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index 4b793de..1806a02 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -20,9 +20,18 @@ pub struct Headers {
    pub headers: HeaderMap,
}

/// The `Rewrites` file options.
pub struct Rewrites {
    /// Source pattern glob matcher
    pub source: GlobMatcher,
    /// A local file that must exist
    pub destination: String,
}

/// The `advanced` file options.
pub struct Advanced {
    pub headers: Option<Vec<Headers>>,
    pub rewrites: Option<Vec<Rewrites>>,
}

/// The full server CLI and File options.
@@ -81,7 +90,7 @@ impl Settings {

                config_file = Some(path_resolved);

                // Assign the corresponding file option values
                // File-based "general" options
                if let Some(general) = settings.general {
                    if let Some(v) = general.host {
                        host = v
@@ -151,7 +160,7 @@ impl Settings {
                    }
                }

                // Prepare the "advanced" options
                // File-based "advanced" options
                if let Some(advanced) = settings.advanced {
                    // 1. Custom HTTP headers assignment
                    let headers_entries = match advanced.headers {
@@ -179,8 +188,35 @@ impl Settings {
                        _ => None,
                    };

                    // 2. Rewrites assignment
                    let rewrites_entries = match advanced.rewrites {
                        Some(rewrites_entries) => {
                            let mut rewrites_vec: Vec<Rewrites> = Vec::new();

                            // Compile a glob pattern for each rewrite sources entry
                            for rewrites_entry in rewrites_entries.iter() {
                                let source = Glob::new(&rewrites_entry.source)
                                    .with_context(|| {
                                        format!(
                                            "can not compile glob pattern for rewrite source: {}",
                                            &rewrites_entry.source
                                        )
                                    })?
                                    .compile_matcher();

                                rewrites_vec.push(Rewrites {
                                    source,
                                    destination: rewrites_entry.destination.to_owned(),
                                });
                            }
                            Some(rewrites_vec)
                        }
                        _ => None,
                    };

                    settings_advanced = Some(Advanced {
                        headers: headers_entries,
                        rewrites: rewrites_entries,
                    });
                }
            }
diff --git a/tests/toml/config.toml b/tests/toml/config.toml
index 47ff090..838c37c 100644
--- a/tests/toml/config.toml
+++ b/tests/toml/config.toml
@@ -2,7 +2,7 @@

#### Address & Root dir
host = "::"
port = 8087
port = 8787
root = "docker/public"

#### Logging
@@ -45,9 +45,6 @@ grace-period = 0
#### Page fallback for 404s
page-fallback = ""

#### Page fallback for 404s
page-fallback = ""

#### Log request Remote Address if available
log-remote-address = false

@@ -79,3 +76,14 @@ Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload"
[[advanced.headers]]
source = "**/*.{jpg,jpeg,png,ico,gif}"
headers.Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload"


### URL Rewrites

[[advanced.rewrites]]
source = "**/*.{png,ico,gif}"
destination = "/assets/favicon.ico"

[[advanced.rewrites]]
source = "**/*.{jpg,jpeg}"
destination = "/images/nomad.png"