index : static-web-server.git

ascending towards madness

author Jose Quintana <1700322+joseluisq@users.noreply.github.com> 2023-06-05 0:05:58.0 +00:00:00
committer GitHub <noreply@github.com> 2023-06-05 0:05:58.0 +00:00:00
commit
2150c74aa37f61f6b7780935bbb6c40522201c8b [patch]
tree
b8fc6e3d1614ca8c8a206a26a3e2da78a81e153d
parent
e183ea32b039c508deee050608d94265f7e2d4e8
download
2150c74aa37f61f6b7780935bbb6c40522201c8b.tar.gz

feat: support for boolean flags without explicit values (#215)

the following boolean flag variants are now possible.

Implicit example:
static-web-server --root public/ --compression -z

Explicit example (note the are no spaces in between):
static-web-server --root public/ --compression=false -z

Note also that this PR migrates to the latest clap v4.

Diff

 Cargo.lock               | 261 ++++++++++++++++++++++++++++--------------------
 Cargo.toml               |   2 +-
 src/directory_listing.rs |  16 +--
 src/server.rs            |   2 +-
 src/settings/cli.rs      | 184 ++++++++++++++++++++--------------
 src/settings/mod.rs      |   4 +-
 6 files changed, 278 insertions(+), 191 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index e2c3d05..0fd3507 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -57,6 +57,55 @@ dependencies = [
]

[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
 "anstyle",
 "anstyle-parse",
 "anstyle-query",
 "anstyle-wincon",
 "colorchoice",
 "is-terminal",
 "utf8parse",
]

[[package]]
name = "anstyle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"

[[package]]
name = "anstyle-parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
dependencies = [
 "utf8parse",
]

[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
 "windows-sys 0.48.0",
]

[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
 "anstyle",
 "windows-sys 0.48.0",
]

[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -79,17 +128,6 @@ dependencies = [
]

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi 0.1.19",
 "libc",
 "winapi",
]

[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -233,42 +271,51 @@ dependencies = [

[[package]]
name = "clap"
version = "3.2.25"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28"
dependencies = [
 "atty",
 "bitflags",
 "clap_builder",
 "clap_derive",
 "clap_lex",
 "indexmap",
 "once_cell",
]

[[package]]
name = "clap_builder"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980"
dependencies = [
 "anstream",
 "anstyle",
 "bitflags",
 "clap_lex",
 "strsim",
 "termcolor",
 "textwrap",
]

[[package]]
name = "clap_derive"
version = "3.2.25"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008"
checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b"
dependencies = [
 "heck",
 "proc-macro-error",
 "proc-macro2",
 "quote",
 "syn 1.0.109",
 "syn",
]

[[package]]
name = "clap_lex"
version = "0.2.4"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
 "os_str_bytes",
]
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"

[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"

[[package]]
name = "core-foundation-sys"
@@ -321,6 +368,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"

[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
 "errno-dragonfly",
 "libc",
 "windows-sys 0.48.0",
]

[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
 "cc",
 "libc",
]

[[package]]
name = "flate2"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -479,21 +547,18 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"

[[package]]
name = "hermit-abi"
version = "0.1.19"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
 "libc",
]

[[package]]
name = "hermit-abi"
version = "0.2.6"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
 "libc",
]
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"

[[package]]
name = "http"
@@ -615,6 +680,29 @@ dependencies = [
]

[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
 "hermit-abi 0.3.1",
 "libc",
 "windows-sys 0.48.0",
]

[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
 "hermit-abi 0.3.1",
 "io-lifetimes",
 "rustix",
 "windows-sys 0.48.0",
]

[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -666,6 +754,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"

[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"

[[package]]
name = "listenfd"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -770,12 +864,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"

[[package]]
name = "os_str_bytes"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"

[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -827,7 +915,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.18",
 "syn",
]

[[package]]
@@ -849,30 +937,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"

[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
 "proc-macro-error-attr",
 "proc-macro2",
 "quote",
 "syn 1.0.109",
 "version_check",
]

[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
 "proc-macro2",
 "quote",
 "version_check",
]

[[package]]
name = "proc-macro2"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -932,6 +996,20 @@ dependencies = [
]

[[package]]
name = "rustix"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
 "bitflags",
 "errno",
 "io-lifetimes",
 "libc",
 "linux-raw-sys",
 "windows-sys 0.48.0",
]

[[package]]
name = "rustls"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1001,7 +1079,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.18",
 "syn",
]

[[package]]
@@ -1032,7 +1110,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.18",
 "syn",
]

[[package]]
@@ -1172,17 +1250,6 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"

[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "syn"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
@@ -1193,21 +1260,6 @@ dependencies = [
]

[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
 "winapi-util",
]

[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"

[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1263,7 +1315,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.18",
 "syn",
]

[[package]]
@@ -1386,6 +1438,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"

[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"

[[package]]
name = "uuid"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1440,7 +1498,7 @@ dependencies = [
 "once_cell",
 "proc-macro2",
 "quote",
 "syn 2.0.18",
 "syn",
 "wasm-bindgen-shared",
]

@@ -1462,7 +1520,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.18",
 "syn",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
]
@@ -1506,15 +1564,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
 "winapi",
]

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index bc9e4c5..da62d8f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,7 +70,7 @@ rustls-pemfile = { version = "1.0", optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_ignored = "0.1"
serde_repr = "0.1"
clap = { version = "3.0", features = ["derive", "env"] }
clap = { version = "4.3", features = ["derive", "env"] }
chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"] }
tokio-rustls = { version = "0.24", optional = true }
diff --git a/src/directory_listing.rs b/src/directory_listing.rs
index 952db87..fb13ed5 100644
--- a/src/directory_listing.rs
+++ b/src/directory_listing.rs
@@ -9,7 +9,7 @@
#![allow(missing_docs)]

use chrono::{DateTime, Local, NaiveDateTime, Utc};
use clap::arg_enum;
use clap::ValueEnum;
use futures_util::future::Either;
use futures_util::{future, FutureExt};
use headers::{ContentLength, ContentType, HeaderMapExt};
@@ -25,14 +25,12 @@ use std::time::{SystemTime, UNIX_EPOCH};

use crate::{exts::http::MethodExt, Context, Result};

arg_enum! {
    #[derive(Debug, Serialize, Deserialize, Clone)]
    #[serde(rename_all = "lowercase")]
    /// Directory listing output format for file entries.
    pub enum DirListFmt {
        Html,
        Json,
    }
#[derive(Debug, Serialize, Deserialize, Clone, ValueEnum)]
#[serde(rename_all = "lowercase")]
/// Directory listing output format for file entries.
pub enum DirListFmt {
    Html,
    Json,
}

/// Provides directory listing support for the current request.
diff --git a/src/server.rs b/src/server.rs
index 93630b2..0fbbfe1 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -196,7 +196,7 @@ impl Server {

        // Directory listing format
        let dir_listing_format = general.directory_listing_format;
        tracing::info!("directory listing format: {}", dir_listing_format);
        tracing::info!("directory listing format: {:?}", dir_listing_format);

        // Cache control headers option
        let cache_control_headers = general.cache_control_headers;
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index aa2b896..d6e34fc 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -5,28 +5,28 @@

//! The server CLI options

use clap::StructOpt;
use clap::Parser;
use std::path::PathBuf;

use crate::directory_listing::DirListFmt;

/// General server configuration available in CLI and config file options.
#[derive(Debug, StructOpt)]
#[structopt(about, author, version)]
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct General {
    #[structopt(long, short = 'a', default_value = "::", env = "SERVER_HOST")]
    #[arg(long, short = 'a', default_value = "::", env = "SERVER_HOST")]
    /// Host address (E.g 127.0.0.1 or ::1)
    pub host: String,

    #[structopt(long, short = 'p', default_value = "80", env = "SERVER_PORT")]
    #[arg(long, short = 'p', default_value = "80", env = "SERVER_PORT")]
    /// Host port
    pub port: u16,

    #[structopt(
    #[arg(
        long,
        short = 'f',
        env = "SERVER_LISTEN_FD",
        conflicts_with_all(&["host", "port", "https-redirect"])
        conflicts_with_all(&["host", "port", "https_redirect"])
    )]
    /// Instead of binding to a TCP port, accept incoming connections to an already-bound TCP
    /// socket listener on the specified file descriptor number (usually zero). Requires that the
@@ -39,7 +39,7 @@ pub struct General {

    #[cfg_attr(
        not(wasm),
        structopt(
        arg(
            long,
            short = 'n',
            default_value = "1",
@@ -48,7 +48,7 @@ pub struct General {
    )]
    #[cfg_attr(
        wasm,
        structopt(
        arg(
            long,
            short = 'n',
            default_value = "2",
@@ -63,7 +63,7 @@ pub struct General {

    #[cfg_attr(
        not(wasm),
        structopt(
        arg(
            long,
            short = 'b',
            default_value = "512",
@@ -72,7 +72,7 @@ pub struct General {
    )]
    #[cfg_attr(
        wasm,
        structopt(
        arg(
            long,
            short = 'b',
            default_value = "20",
@@ -82,11 +82,11 @@ pub struct General {
    /// Maximum number of blocking threads
    pub max_blocking_threads: usize,

    #[structopt(long, short = 'd', default_value = "./public", env = "SERVER_ROOT")]
    #[arg(long, short = 'd', default_value = "./public", env = "SERVER_ROOT")]
    /// Root directory path of static files.
    pub root: PathBuf,

    #[structopt(
    #[arg(
        long,
        default_value = "./public/50x.html",
        env = "SERVER_ERROR_PAGE_50X"
@@ -94,7 +94,7 @@ pub struct General {
    /// HTML file path for 50x errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message.
    pub page50x: PathBuf,

    #[structopt(
    #[arg(
        long,
        default_value = "./public/404.html",
        env = "SERVER_ERROR_PAGE_404"
@@ -102,15 +102,15 @@ pub struct General {
    /// HTML file path for 404 errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message.
    pub page404: PathBuf,

    #[structopt(long, env = "SERVER_FALLBACK_PAGE")]
    #[arg(long, env = "SERVER_FALLBACK_PAGE")]
    /// HTML file path that is used for GET requests when the requested path doesn't exist. The fallback page is served with a 200 status code, useful when using client routers. If the path is not specified or simply doesn't exist then this feature will not be active.
    pub page_fallback: Option<PathBuf>,

    #[structopt(long, short = 'g', default_value = "error", env = "SERVER_LOG_LEVEL")]
    #[arg(long, short = 'g', default_value = "error", env = "SERVER_LOG_LEVEL")]
    /// Specify a logging level in lower case. Values: error, warn, info, debug or trace
    pub log_level: String,

    #[structopt(
    #[arg(
        long,
        short = 'c',
        default_value = "",
@@ -119,7 +119,7 @@ pub struct General {
    /// Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host.
    pub cors_allow_origins: String,

    #[structopt(
    #[arg(
        long,
        short = 'j',
        default_value = "origin, content-type",
@@ -128,7 +128,7 @@ pub struct General {
    /// Specify an optional CORS list of allowed headers separated by commas. Default "origin, content-type". It requires `--cors-allow-origins` to be used along with.
    pub cors_allow_headers: String,

    #[structopt(
    #[arg(
        long,
        default_value = "origin, content-type",
        env = "SERVER_CORS_EXPOSE_HEADERS"
@@ -136,35 +136,41 @@ pub struct General {
    /// Specify an optional CORS list of exposed headers separated by commas. Default "origin, content-type". It requires `--cors-expose-origins` to be used along with.
    pub cors_expose_headers: String,

    #[structopt(
    #[arg(
        long,
        short = 't',
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_HTTP2_TLS"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_HTTP2_TLS",
    )]
    #[cfg(feature = "http2")]
    #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
    /// Enable HTTP/2 with TLS support.
    pub http2: bool,

    #[structopt(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_CERT")]
    #[arg(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_CERT")]
    #[cfg(feature = "http2")]
    #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
    /// Specify the file path to read the certificate.
    pub http2_tls_cert: Option<PathBuf>,

    #[structopt(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_KEY")]
    #[arg(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_KEY")]
    #[cfg(feature = "http2")]
    #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
    /// Specify the file path to read the private key.
    pub http2_tls_key: Option<PathBuf>,

    #[structopt(
    #[arg(
        long,
        requires_if("true", "http2"),
        parse(try_from_str),
        default_value = "false",
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        requires_if("true", "http2"),
        env = "SERVER_HTTPS_REDIRECT"
    )]
    #[cfg(feature = "http2")]
@@ -172,9 +178,9 @@ pub struct General {
    /// Redirect all requests with scheme "http" to "https" for the current server instance. It depends on "http2" to be enabled.
    pub https_redirect: bool,

    #[structopt(
    #[arg(
        long,
        requires_if("true", "https-redirect"),
        requires_if("true", "https_redirect"),
        default_value = "localhost",
        env = "SERVER_HTTPS_REDIRECT_HOST"
    )]
@@ -183,9 +189,9 @@ pub struct General {
    /// Canonical host name or IP of the HTTPS (HTTPS/2) server. It depends on "https_redirect" to be enabled.
    pub https_redirect_host: String,

    #[structopt(
    #[arg(
        long,
        requires_if("true", "https-redirect"),
        requires_if("true", "https_redirect"),
        default_value = "80",
        env = "SERVER_HTTPS_REDIRECT_FROM_PORT"
    )]
@@ -194,9 +200,9 @@ pub struct General {
    /// HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on "https_redirect" to be enabled.
    pub https_redirect_from_port: u16,

    #[structopt(
    #[arg(
        long,
        requires_if("true", "https-redirect"),
        requires_if("true", "https_redirect"),
        default_value = "localhost",
        env = "SERVER_HTTPS_REDIRECT_FROM_HOSTS"
    )]
@@ -207,114 +213,145 @@ pub struct General {

    #[cfg(feature = "compression")]
    #[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
    #[structopt(
    #[arg(
        long,
        short = 'x',
        parse(try_from_str),
        default_value = "true",
        env = "SERVER_COMPRESSION"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_COMPRESSION",
    )]
    /// Gzip, Deflate, Brotli or Zstd compression on demand determined by the Accept-Encoding header and applied to text-based web file types only.
    pub compression: bool,

    #[cfg(feature = "compression")]
    #[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
    #[structopt(
    #[arg(
        long,
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_COMPRESSION_STATIC"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_COMPRESSION_STATIC",
    )]
    /// Look up the pre-compressed file variant (`.gz`, `.br` or `.zst`) on disk of a requested file and serves it directly if available.
    /// The compression type is determined by the `Accept-Encoding` header.
    pub compression_static: bool,

    #[structopt(
    #[arg(
        long,
        short = 'z',
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_DIRECTORY_LISTING"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_DIRECTORY_LISTING",
    )]
    /// Enable directory listing for all requests ending with the slash character (‘/’).
    pub directory_listing: bool,

    #[structopt(
    #[arg(
        long,
        required_if_eq("directory-listing", "true"),
        requires_if("true", "directory_listing"),
        default_value = "6",
        env = "SERVER_DIRECTORY_LISTING_ORDER"
    )]
    /// Specify a default code number to order directory listing entries per `Name`, `Last modified` or `Size` attributes (columns). Code numbers supported: 0 (Name asc), 1 (Name desc), 2 (Last modified asc), 3 (Last modified desc), 4 (Size asc), 5 (Size desc). Default 6 (unordered)
    pub directory_listing_order: u8,

    #[structopt(
    #[arg(
        long,
        required_if_eq("directory-listing", "true"),
        value_enum,
        requires_if("true", "directory_listing"),
        default_value = "html",
        env = "SERVER_DIRECTORY_LISTING_FORMAT",
        case_insensitive = true
        ignore_case(true)
    )]
    /// Specify a content format for directory listing entries. Formats supported: "html" or "json". Default "html".
    pub directory_listing_format: DirListFmt,

    #[structopt(
    #[arg(
        long,
        parse(try_from_str),
        default_value_if("http2", Some("true"), Some("true")),
        default_value = "false",
        env = "SERVER_SECURITY_HEADERS"
        default_value_if("http2", "true", Some("true")),
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_SECURITY_HEADERS",
    )]
    /// Enable security headers by default when HTTP/2 feature is activated.
    /// Headers included: "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload" (2 years max-age),
    /// "X-Frame-Options: DENY", "X-XSS-Protection: 1; mode=block" and "Content-Security-Policy: frame-ancestors 'self'".
    pub security_headers: bool,

    #[structopt(
    #[arg(
        long,
        short = 'e',
        parse(try_from_str),
        default_value = "true",
        env = "SERVER_CACHE_CONTROL_HEADERS"
    )]
    #[arg(
        long,
        short = 'e',
        default_value = "true",
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_CACHE_CONTROL_HEADERS",
    )]
    /// Enable cache control headers for incoming requests based on a set of file types. The file type list can be found on `src/control_headers.rs` file.
    pub cache_control_headers: bool,

    /// It provides The "Basic" HTTP Authentication scheme using credentials as "user-id:password" pairs. Password must be encoded using the "BCrypt" password-hashing function.
    #[structopt(long, default_value = "", env = "SERVER_BASIC_AUTH")]
    #[arg(long, default_value = "", env = "SERVER_BASIC_AUTH")]
    pub basic_auth: String,

    #[structopt(long, short = 'q', default_value = "0", env = "SERVER_GRACE_PERIOD")]
    #[arg(long, short = 'q', default_value = "0", env = "SERVER_GRACE_PERIOD")]
    /// Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds.
    pub grace_period: u8,

    #[structopt(long, short = 'w', env = "SERVER_CONFIG_FILE")]
    #[arg(long, short = 'w', env = "SERVER_CONFIG_FILE")]
    /// Server TOML configuration file path.
    pub config_file: Option<PathBuf>,

    #[structopt(
    #[arg(
        long,
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_LOG_REMOTE_ADDRESS"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_LOG_REMOTE_ADDRESS",
    )]
    /// Log incoming requests information along with its remote address if available using the `info` log level.
    pub log_remote_address: bool,

    #[structopt(
    #[arg(
        long,
        parse(try_from_str),
        default_value = "true",
        env = "SERVER_REDIRECT_TRAILING_SLASH"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_REDIRECT_TRAILING_SLASH",
    )]
    /// Check for a trailing slash in the requested directory URI and redirect permanently (308) to the same path with a trailing slash suffix if it is missing.
    pub redirect_trailing_slash: bool,

    #[structopt(
    #[arg(
        long,
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_IGNORE_HIDDEN_FILES"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_IGNORE_HIDDEN_FILES",
    )]
    /// Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML index pages (directory listing).
    pub ignore_hidden_files: bool,
@@ -323,32 +360,35 @@ pub struct General {
    // Windows specific arguments and commands
    //
    #[cfg(windows)]
    #[structopt(
    #[arg(
        long,
        short = 's',
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_WINDOWS_SERVICE"
        default_missing_value("true"),
        num_args(0..=1),
        require_equals(true),
        action = clap::ArgAction::Set,
        env = "SERVER_WINDOWS_SERVICE",
    )]
    /// Tell the web server to run in a Windows Service context. Note that the `install` subcommand will enable this option automatically.
    pub windows_service: bool,

    // Windows commands
    #[cfg(windows)]
    #[structopt(subcommand)]
    #[command(subcommand)]
    /// Subcommands to install or uninstall the SWS Windows Service.
    pub commands: Option<Commands>,
}

#[cfg(windows)]
#[derive(Debug, StructOpt)]
#[derive(Debug, clap::Subcommand)]
/// Subcommands to install or uninstall the SWS Windows Service.
pub enum Commands {
    /// Install a Windows Service for the web server.
    #[structopt(name = "install")]
    #[command(name = "install")]
    Install {},

    /// Uninstall the current Windows Service.
    #[structopt(name = "uninstall")]
    #[command(name = "uninstall")]
    Uninstall {},
}
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index bf94bd8..746d9d7 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -6,7 +6,7 @@
//! Module that provides all settings of SWS.
//!

use clap::StructOpt;
use clap::Parser;
use globset::{Glob, GlobMatcher};
use headers::HeaderMap;
use hyper::StatusCode;
@@ -68,7 +68,7 @@ pub struct Settings {
impl Settings {
    /// Handles CLI and config file options and converging them into one.
    pub fn get() -> Result<Settings> {
        let opts = General::from_args();
        let opts = General::parse();

        // Define the general CLI/file options
        let mut host = opts.host;