index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2020-05-05 0:30:52.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2020-05-05 0:30:52.0 +00:00:00
commit
1af7d280132ebf42e7160d61f483c0fe5174236e [patch]
tree
9efc59f4290622a758a27e3f9b188cb5608335e0
parent
b2d2f40e30c4ca309d5fe8db9fddc5f167a33e9f
download
1af7d280132ebf42e7160d61f483c0fe5174236e.tar.gz

feat: CORS support (resolves #18)



Diff

 Cargo.lock         | 11 +++++++++++
 Cargo.toml         |  1 +
 README.md          | 12 ++++++++----
 Tasks.Dev.toml     |  1 +
 src/config.rs      |  3 +++
 src/main.rs        |  6 ++++++
 src/staticfiles.rs | 23 ++++++++++++++++++++++-
 7 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 81c96c8..4b0fe29 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -295,6 +295,16 @@ dependencies = [
]

[[package]]
name = "iron-cors"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24b02b8856c7f14e443c483e802cf0ce693f3bec19f49d2c9a242b18f88c9b70"
dependencies = [
 "iron",
 "log 0.4.8",
]

[[package]]
name = "iron-test"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -913,6 +923,7 @@ dependencies = [
 "hyper",
 "hyper-native-tls",
 "iron",
 "iron-cors",
 "iron-test",
 "iron_staticfile_middleware",
 "log 0.4.8",
diff --git a/Cargo.toml b/Cargo.toml
index c6aa6c8..0bf721b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,7 @@ iron_staticfile_middleware = { git = "https://github.com/joseluisq/iron-staticfi
hyper-native-tls = "0.3"
nix = "0.14"
signal = "0.7"
iron-cors = "0.8"

[dev-dependencies]
openssl = { version = "0.10", features = ["vendored"] }
diff --git a/README.md b/README.md
index 5868a17..01aa142 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,7 @@ Server can be configured either via environment variables or their equivalent co
| `SERVER_TLS` | Enables TLS/SSL support. Make sure also to adjust current server port. | Default `false` |
| `SERVER_TLS_PKCS12` | A cryptographic identity [PKCS #12]https://docs.rs/native-tls/0.2.3/native_tls/struct.Identity.html#method.from_pkcs12 bundle file path containing a [X509 certificate]https://en.wikipedia.org/wiki/X.509 along with its corresponding private key and chain of certificates to a trusted root. | Default empty |
| `SERVER_TLS_PKCS12_PASSWD` | A specified password to decrypt the private key. | Default empty |
| `SERVER_CORS_ALLOW_ORIGINS` | Specify a CORS list of allowed origin hosts separated by comas with no whitespaces. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host. See [Iron CORS crate]https://docs.rs/iron-cors/0.8.0/iron_cors/#mode-1-whitelist. | Default empty (which means CORS is disabled) |

### Command-line arguments

@@ -64,22 +65,25 @@ OPTIONS:
        --assets <assets>
            Assets directory path for add cache headers functionality [env: SERVER_ASSETS=]  [default: ./public/assets]

        --host <host>                              Host address (E.g 127.0.0.1) [env: SERVER_HOST=]  [default: [::]]
        --cors-allow-origins <cors-allow-origins>
            Specify a CORS list of allowed origin hosts separated by comas with no whitespaces. Host ports or protocols
            aren't being checked. Use an asterisk (*) to allow any host [env: SERVER_CORS_ALLOW_ORIGINS=]  [default: ]
        --host <host>                                Host address (E.g 127.0.0.1) [env: SERVER_HOST=]  [default: [::]]
        --log-level <log-level>
            Specify a logging level in lower case [env: SERVER_LOG_LEVEL=]  [default: error]

        --name <name>                              Name for server [env: SERVER_NAME=]  [default: my-static-server]
        --name <name>                                Name for server [env: SERVER_NAME=]  [default: my-static-server]
        --page404 <page404>
            HTML file path for 404 errors. If path is not specified or simply don't exists then server will use a
            generic HTML error message [env: SERVER_ERROR_PAGE_404=]  [default: ./public/404.html]
        --page50x <page50x>
            HTML file path for 50x errors. If path is not specified or simply don't exists then server will use a
            generic HTML error message [env: SERVER_ERROR_PAGE_50X=]  [default: ./public/50x.html]
        --port <port>                              Host port [env: SERVER_PORT=]  [default: 80]
        --port <port>                                Host port [env: SERVER_PORT=]  [default: 80]
        --root <root>
            Root directory path of static files [env: SERVER_ROOT=]  [default: ./public]

        --tls <tls>                                Enables TLS/SSL support [env: SERVER_TLS=]
        --tls <tls>                                  Enables TLS/SSL support [env: SERVER_TLS=]
        --tls-pkcs12 <tls-pkcs12>
            A cryptographic identity PKCS #12 bundle file path containing a X509 certificate along with its
            corresponding private key and chain of certificates to a trusted root [env: SERVER_TLS_PKCS12=]  [default: ]
diff --git a/Tasks.Dev.toml b/Tasks.Dev.toml
index 495fe2a..300554b 100644
--- a/Tasks.Dev.toml
+++ b/Tasks.Dev.toml
@@ -4,6 +4,7 @@ E_URL = "http://locahost"
SERVER_LOG_LEVEL = "trace"
SERVER_ROOT = "./public"
SERVER_ASSETS = "./public/assets"
SERVER_CORS_ALLOW_ORIGINS = "*"

[tasks.watch]
command = "cargo"
diff --git a/src/config.rs b/src/config.rs
index 8223481..4b7448d 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -44,4 +44,7 @@ pub struct Options {
    #[structopt(long, default_value = "error", env = "SERVER_LOG_LEVEL")]
    /// Specify a logging level in lower case.
    pub log_level: String,
    #[structopt(long, default_value = "", env = "SERVER_CORS_ALLOW_ORIGINS")]
    /// Specify a CORS list of allowed origin hosts separated by comas with no whitespaces. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host.
    pub cors_allow_origins: String,
}
diff --git a/src/main.rs b/src/main.rs
index 61eea09..1f7c6aa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,6 +47,7 @@ fn main() {
        assets_dir: opts.assets,
        page_50x_path: opts.page50x,
        page_404_path: opts.page404,
        cors_allow_origins: opts.cors_allow_origins,
    });

    if opts.tls {
@@ -121,6 +122,7 @@ mod test {
            assets_dir: opts.assets,
            page_50x_path: opts.page50x,
            page_404_path: opts.page404,
            cors_allow_origins: "".to_string(),
        });

        let response = request::head("http://127.0.0.1/", Headers::new(), &files.handle())
@@ -143,6 +145,7 @@ mod test {
            assets_dir: opts.assets,
            page_50x_path: opts.page50x,
            page_404_path: opts.page404,
            cors_allow_origins: "".to_string(),
        });

        let res = request::head("http://127.0.0.1/", Headers::new(), &files.handle())
@@ -170,6 +173,7 @@ mod test {
            assets_dir: assets.path().to_str().unwrap().to_string(),
            page_50x_path: opts.page50x,
            page_404_path: opts.page404,
            cors_allow_origins: "".to_string(),
        });

        let res = request::head("http://127.0.0.1/", Headers::new(), &files.handle())
@@ -191,6 +195,7 @@ mod test {
            assets_dir: opts.assets,
            page_50x_path: opts.page50x,
            page_404_path: opts.page404,
            cors_allow_origins: "".to_string(),
        });

        let res = request::head("http://127.0.0.1/unknown", Headers::new(), &files.handle())
@@ -212,6 +217,7 @@ mod test {
            assets_dir: opts.assets,
            page_50x_path: opts.page50x,
            page_404_path: opts.page404,
            cors_allow_origins: "".to_string(),
        });

        let response = request::post("http://127.0.0.1/", Headers::new(), "", &files.handle())
diff --git a/src/staticfiles.rs b/src/staticfiles.rs
index dcb0e0a..2eba414 100644
--- a/src/staticfiles.rs
+++ b/src/staticfiles.rs
@@ -1,10 +1,12 @@
use crate::error_page::ErrorPage;
use crate::gzip::GzipMiddleware;
use crate::helpers;
use crate::logger::Logger;
use crate::logger::{log_server, Logger};

use iron::prelude::*;
use iron_cors::CorsMiddleware;
use iron_staticfile_middleware::{Cache, GuessContentType, ModifyWith, Prefix, Staticfile};
use std::collections::HashSet;
use std::time::Duration;

/// An Iron middleware for static files-serving.
@@ -17,6 +19,7 @@ pub struct StaticFilesOptions {
    pub assets_dir: String,
    pub page_50x_path: String,
    pub page_404_path: String,
    pub cors_allow_origins: String,
}

impl StaticFiles {
@@ -65,6 +68,24 @@ impl StaticFiles {
            .parse()
            .expect("Unable to create a default content type header");

        // CORS support
        let allowed_hosts = &self.opts.cors_allow_origins;

        if !allowed_hosts.is_empty() {
            log_server("CORS enabled");
            log_server(&format!("Access-Control-Allow-Origin: {}", allowed_hosts));

            if allowed_hosts == "*" {
                chain.link_around(CorsMiddleware::with_allow_any());
            } else {
                let allowed_hosts = allowed_hosts
                    .split(',')
                    .map(|s| s.to_string())
                    .collect::<HashSet<_>>();
                chain.link_around(CorsMiddleware::with_whitelist(allowed_hosts));
            };
        }

        chain.link_after(ModifyWith::new(Cache::new(one_day)));
        chain.link_after(Prefix::new(&[assets_dirname], Cache::new(one_year)));
        chain.link_after(GuessContentType::new(default_content_type));