index : static-web-server.git

ascending towards madness

author Jose Quintana <1700322+joseluisq@users.noreply.github.com> 2023-03-31 0:04:39.0 +00:00:00
committer GitHub <noreply@github.com> 2023-03-31 0:04:39.0 +00:00:00
commit
70db3c9ad2df6908c5bb3b099565941b83c84e5c [patch]
tree
a5d37c61b969445a8314cf9035f16b842a659a08
parent
90ec4b64b548123fa00af391705123a0fd8fa766
download
70db3c9ad2df6908c5bb3b099565941b83c84e5c.tar.gz

feat: `static_web_server` crate (#190)

* feat: preliminary `static_web_server` crate setup
* chore: add API docs

resolves #188

Diff

 .github/workflows/release.crate.yml |  6 +--
 Cargo.toml                          | 22 ++++++++++-
 Makefile                            | 11 ++++++-
 src/basic_auth.rs                   |  3 ++-
 src/compression.rs                  | 10 +++--
 src/compression_static.rs           |  6 +++-
 src/control_headers.rs              |  4 +-
 src/cors.rs                         | 47 +++++++++---------------
 src/custom_headers.rs               |  3 ++-
 src/directory_listing.rs            |  5 +++-
 src/error.rs                        |  3 ++-
 src/error_page.rs                   |  3 ++-
 src/exts/http.rs                    |  4 ++-
 src/exts/path.rs                    |  1 +-
 src/fallback_page.rs                |  3 ++-
 src/handler.rs                      | 22 ++++++++++-
 src/helpers.rs                      | 19 +---------
 src/lib.rs                          | 76 ++++++++++++++++++++++++++++++++++++--
 src/logger.rs                       |  3 ++-
 src/redirects.rs                    |  3 ++-
 src/rewrites.rs                     |  3 ++-
 src/security_headers.rs             |  3 ++-
 src/server.rs                       | 15 +++++---
 src/service.rs                      |  6 +++-
 src/settings/cli.rs                 |  2 +-
 src/settings/file.rs                | 68 ++++++++++++++++++++++++++--------
 src/settings/mod.rs                 |  6 +++-
 src/signals.rs                      |  3 ++-
 src/static_files.rs                 | 22 ++++++++---
 src/tls.rs                          | 17 ++++++---
 src/transport.rs                    |  9 +++--
 src/winservice.rs                   |  3 +-
 32 files changed, 317 insertions(+), 94 deletions(-)

diff --git a/.github/workflows/release.crate.yml b/.github/workflows/release.crate.yml
index 1781b10..6010d91 100644
--- a/.github/workflows/release.crate.yml
+++ b/.github/workflows/release.crate.yml
@@ -9,7 +9,7 @@ on:
jobs:
  check-secret:
    runs-on: ubuntu-latest
    environment: crates-io-libsws
    environment: crates-io-static-web-server
    outputs:
      publish: ${{ steps.check.outputs.publish }}
    steps:
@@ -205,7 +205,7 @@ jobs:
    needs: test
    if: needs.check-secret.outputs.publish == 'true'
    runs-on: ubuntu-latest
    environment: crates-io-libsws
    environment: crates-io-static-web-server
    steps:
      - name: Checkout
        uses: actions/checkout@v3
@@ -217,6 +217,6 @@ jobs:

      - name: Publish workspace packages
        run: |
          cargo publish -p libsws
          cargo publish -p static-web-server
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }}
diff --git a/Cargo.toml b/Cargo.toml
index 8a9d33d..da76d60 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,26 +6,44 @@ license = "MIT OR Apache-2.0"
description = "A cross-platform, high-performance and asynchronous web server for static files-serving."
repository = "https://github.com/static-web-server/static-web-server"
readme = "README.md"
homepage = "https://static-web-server.net"
keywords = [
    "static-web-server",
    "file-server",
    "http-server",
    "docker-image",
    "musl-libc",
    "x86",
    "x86-64",
    "arm",
    "arm64",
    "linux",
    "darwin",
    "freebsd",
    "windows",
]
categories = ["network-programming", "web-programming::http-server"]
edition = "2021"
include = ["src/**/*", "Cargo.toml", "Cargo.lock"]
include = [
    "src/**/*.rs",
    "Cargo.toml",
    "README.md",
    "LICENSE-MIT",
    "LICENSE-APACHE"
]
autotests = true
autoexamples = true

[package.metadata.docs.rs]
all-features = true

[lib]
name = "static_web_server"
path = "src/lib.rs"

[[bin]]
name = "static-web-server"
path = "src/bin/server.rs"
doc = false

[features]
default = ["http2"]
diff --git a/Makefile b/Makefile
index 574b7da..ccfa8d8 100644
--- a/Makefile
+++ b/Makefile
@@ -247,6 +247,17 @@ docs-dev:
	@docker-compose -f docs/docker-compose.yml up --build
.PHONY: docs-dev

crate-docs:
	@cargo doc --no-deps
.PHONY: crate-docs

crate-docs-dev:
	@cargo doc --no-deps
	@echo "Crate documentation: http://localhost:8787/static_web_server"
	@static-web-server -p 8787 -d target/doc/ \
		& watchman-make -p 'src/**/*.rs' --run 'cargo doc'
.PHONY: crate-docs-dev

docs-deploy:
	@git stash
	@rm -rf /tmp/docs
diff --git a/src/basic_auth.rs b/src/basic_auth.rs
index 34c13b6..9daada8 100644
--- a/src/basic_auth.rs
+++ b/src/basic_auth.rs
@@ -1,3 +1,6 @@
//! Basic HTTP Authorization Schema module.
//!

use bcrypt::verify as bcrypt_verify;
use headers::{authorization::Basic, Authorization, HeaderMapExt};
use hyper::StatusCode;
diff --git a/src/compression.rs b/src/compression.rs
index 349f567..a39b1c1 100644
--- a/src/compression.rs
+++ b/src/compression.rs
@@ -1,5 +1,7 @@
// Compression handler that compress the body of a response.
// -> Part of the file is borrowed from https://github.com/seanmonstar/warp/pull/513
//! Auto-compression module to compress responses body.
//!

// Part of the file is borrowed from <https://github.com/seanmonstar/warp/pull/513>*

use async_compression::tokio::bufread::{BrotliEncoder, DeflateEncoder, GzipEncoder};
use bytes::Bytes;
@@ -158,8 +160,8 @@ pub fn create_encoding_header(existing: Option<HeaderValue>, coding: ContentCodi
    coding.into()
}

/// A wrapper around any type that implements [`Stream`](futures::Stream) to be
/// compatible with async_compression's Stream based encoders.
/// A wrapper around any type that implements [`Stream`](futures_util::Stream) to be
/// compatible with async_compression's `Stream` based encoders.
#[pin_project]
#[derive(Debug)]
pub struct CompressableBody<S, E>
diff --git a/src/compression_static.rs b/src/compression_static.rs
index e4dfb8d..85578b2 100644
--- a/src/compression_static.rs
+++ b/src/compression_static.rs
@@ -1,3 +1,6 @@
//! Compression static module to serve compressed files directly from the file system.
//!

use headers::{ContentCoding, HeaderMap, HeaderValue};
use std::{
    ffi::OsStr,
@@ -9,8 +12,11 @@ use crate::{compression, static_files::file_metadata};

/// It defines the pre-compressed file variant metadata of a particular file path.
pub struct CompressedFileVariant<'a> {
    /// Current file path.
    pub file_path: PathBuf,
    /// The metadata of the current file.
    pub metadata: Metadata,
    /// The file extension.
    pub extension: &'a str,
}

diff --git a/src/control_headers.rs b/src/control_headers.rs
index 9b1adef..aadf506 100644
--- a/src/control_headers.rs
+++ b/src/control_headers.rs
@@ -1,4 +1,6 @@
// An arbitrary `Cache-Control` headers functionality for incoming requests based on a set of file types.
//! It provides an arbitrary `Cache-Control` headers functionality
//! for incoming requests based on a set of file types.
//!

use headers::{CacheControl, HeaderMapExt};
use hyper::{Body, Response};
diff --git a/src/cors.rs b/src/cors.rs
index c326e3f..77d2ab0 100644
--- a/src/cors.rs
+++ b/src/cors.rs
@@ -1,5 +1,7 @@
// CORS handler for incoming requests.
// -> Part of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/filters/cors.rs
//! CORS module to handle incoming requests.
//!

// Part of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/filters/cors.rs

use headers::{
    AccessControlAllowHeaders, AccessControlAllowMethods, AccessControlExposeHeaders, HeaderMapExt,
@@ -139,13 +141,6 @@ impl Cors {
        self
    }

    /// Sets the `Access-Control-Max-Age` header.
    /// TODO: we could enable this in the future.
    pub fn max_age(mut self, seconds: impl Seconds) -> Self {
        self.max_age = Some(seconds.seconds());
        self
    }

    /// Adds multiple headers to the list of allowed request headers.
    ///
    /// **Note**: These should match the values the browser sends via `Access-Control-Request-Headers`, e.g.`content-type`.
@@ -211,6 +206,7 @@ impl Default for Cors {
}

#[derive(Clone, Debug)]
/// CORS is configurated.
pub struct Configured {
    cors: Cors,
    allowed_headers: AccessControlAllowHeaders,
@@ -219,16 +215,24 @@ pub struct Configured {
}

#[derive(Debug)]
/// Validated CORS request.
pub enum Validated {
    /// Validated as preflight.
    Preflight(HeaderValue),
    /// Validated as simple.
    Simple(HeaderValue),
    /// Validated as not cors.
    NotCors,
}

#[derive(Debug)]
/// Forbidden errors.
pub enum Forbidden {
    /// Forbidden error origin.
    Origin,
    /// Forbidden error method.
    Method,
    /// Forbidden error header.
    Header,
}

@@ -239,6 +243,7 @@ impl Default for Forbidden {
}

impl Configured {
    /// Check for the incoming CORS request.
    pub fn check_request(
        &self,
        method: &http::Method,
@@ -299,19 +304,19 @@ impl Configured {
        }
    }

    pub fn is_method_allowed(&self, header: &HeaderValue) -> bool {
    fn is_method_allowed(&self, header: &HeaderValue) -> bool {
        http::Method::from_bytes(header.as_bytes())
            .map(|method| self.cors.allowed_methods.contains(&method))
            .unwrap_or(false)
    }

    pub fn is_header_allowed(&self, header: &str) -> bool {
    fn is_header_allowed(&self, header: &str) -> bool {
        HeaderName::from_bytes(header.as_bytes())
            .map(|header| self.cors.allowed_headers.contains(&header))
            .unwrap_or(false)
    }

    pub fn is_origin_allowed(&self, origin: &HeaderValue) -> bool {
    fn is_origin_allowed(&self, origin: &HeaderValue) -> bool {
        if let Some(ref allowed) = self.cors.origins {
            allowed.contains(origin)
        } else {
@@ -330,23 +335,9 @@ impl Configured {
    }
}

pub trait Seconds {
    fn seconds(self) -> u64;
}

impl Seconds for u32 {
    fn seconds(self) -> u64 {
        self.into()
    }
}

impl Seconds for ::std::time::Duration {
    fn seconds(self) -> u64 {
        self.as_secs()
    }
}

/// Cast values into the origin header.
pub trait IntoOrigin {
    /// Cast actual value into an origin header.
    fn into_origin(self) -> Origin;
}

diff --git a/src/custom_headers.rs b/src/custom_headers.rs
index 1cb4209..e972a57 100644
--- a/src/custom_headers.rs
+++ b/src/custom_headers.rs
@@ -1,3 +1,6 @@
//! Module to append custom HTTP headers via TOML config file.
//!

use hyper::{Body, Response};

use crate::settings::Headers;
diff --git a/src/directory_listing.rs b/src/directory_listing.rs
index dab3399..7388697 100644
--- a/src/directory_listing.rs
+++ b/src/directory_listing.rs
@@ -1,3 +1,8 @@
//! It provides directory listig and auto-index support.
//!

#![allow(missing_docs)]

use chrono::{DateTime, Local, NaiveDateTime, Utc};
use futures_util::future::Either;
use futures_util::{future, FutureExt};
diff --git a/src/error.rs b/src/error.rs
index 83512fa..3902eb6 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,3 +1,6 @@
//! Useful error type re-exports based on [anyhow][mod@anyhow].
//!

/// Just a `anyhow::Result` type alias.
pub type Result<T = (), E = anyhow::Error> = anyhow::Result<T, E>;

diff --git a/src/error_page.rs b/src/error_page.rs
index fd2f783..6beb947 100644
--- a/src/error_page.rs
+++ b/src/error_page.rs
@@ -1,3 +1,6 @@
//! Error page module to compose an HTML page response.
//!

use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt};
use hyper::{Body, Method, Response, StatusCode, Uri};
use mime_guess::mime;
diff --git a/src/exts/http.rs b/src/exts/http.rs
index 1322d53..9ea932b 100644
--- a/src/exts/http.rs
+++ b/src/exts/http.rs
@@ -7,9 +7,13 @@ pub const HTTP_SUPPORTED_METHODS: &[Method; 3] = &[Method::OPTIONS, Method::HEAD

/// SWS HTTP Method extensions trait.
pub trait MethodExt {
    /// If method is allowed.
    fn is_allowed(&self) -> bool;
    /// If method is `GET`.
    fn is_get(&self) -> bool;
    /// If method is `HEAD`.
    fn is_head(&self) -> bool;
    /// If method is `OPTIONS`.
    fn is_options(&self) -> bool;
}

diff --git a/src/exts/path.rs b/src/exts/path.rs
index 30628c9..ce17e49 100644
--- a/src/exts/path.rs
+++ b/src/exts/path.rs
@@ -4,6 +4,7 @@ use std::path::{Component, Path};

/// SWS Path extensions trait.
pub trait PathExt {
    /// If file path is hidden.
    fn is_hidden(&self) -> bool;
}

diff --git a/src/fallback_page.rs b/src/fallback_page.rs
index 67c5ab6..9b1370e 100644
--- a/src/fallback_page.rs
+++ b/src/fallback_page.rs
@@ -1,3 +1,6 @@
//! Fallback page module useful for a custom page default.
//!

use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt};
use hyper::{Body, Response, StatusCode};
use mime_guess::mime;
diff --git a/src/handler.rs b/src/handler.rs
index b7410b4..bc8831d 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -1,3 +1,6 @@
//! Request handler module intended to manage incoming HTTP requests.
//!

use headers::HeaderValue;
use hyper::{header::WWW_AUTHENTICATE, Body, Request, Response, StatusCode};
use std::{future::Future, net::IpAddr, net::SocketAddr, path::PathBuf, sync::Arc};
@@ -16,29 +19,46 @@ use crate::{
/// It defines options for a request handler.
pub struct RequestHandlerOpts {
    // General options
    /// Root directory of static files.
    pub root_dir: PathBuf,
    /// Compression feature.
    pub compression: bool,
    /// Compression static feature.
    pub compression_static: bool,
    /// Directory listing feature.
    pub dir_listing: bool,
    /// Directory listing order feature.
    pub dir_listing_order: u8,
    /// Directory listing format feature.
    pub dir_listing_format: DirListFmt,
    /// CORS feature.
    pub cors: Option<cors::Configured>,
    /// Security headers feature.
    pub security_headers: bool,
    /// Cache control headers feature.
    pub cache_control_headers: bool,
    /// Page for 404 errors.
    pub page404: Vec<u8>,
    /// Page for 50x errors.
    pub page50x: Vec<u8>,
    /// Page fallback feature.
    pub page_fallback: Vec<u8>,
    /// Basic auth feature.
    pub basic_auth: String,
    /// Log remote address feature.
    pub log_remote_address: bool,
    /// Redirect trailing slash feature.
    pub redirect_trailing_slash: bool,
    /// Ignore hidden files feature.
    pub ignore_hidden_files: bool,

    // Advanced options
    /// Advanced options from the config file.
    pub advanced_opts: Option<Advanced>,
}

/// It defines the main request handler used by the Hyper service request.
pub struct RequestHandler {
    /// Request handler options.
    pub opts: Arc<RequestHandlerOpts>,
}

diff --git a/src/helpers.rs b/src/helpers.rs
index 5862ba4..866e200 100644
--- a/src/helpers.rs
+++ b/src/helpers.rs
@@ -15,21 +15,6 @@ where
    }
}

/// Get the directory name of a valid directory path.
pub fn get_dirname<P: AsRef<Path>>(path: P) -> Result<String>
where
    PathBuf: From<P>,
{
    let path = get_valid_dirpath(path)?;
    match path.iter().last() {
        Some(v) => Ok(v.to_str().unwrap().to_owned()),
        _ => bail!(
            "directory name for path {} was not determined",
            path.display()
        ),
    }
}

/// Read the entire contents of a file into a bytes vector.
pub fn read_bytes(path: &Path) -> Result<Vec<u8>> {
    fs::read(path).with_context(|| format!("failed to read file `{}`", path.display()))
@@ -75,13 +60,13 @@ pub fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {

#[cfg(unix)]
/// In Unix-like systems it just casts the `PathBuf` into an string.
pub fn adjust_canonicalization(p: PathBuf) -> String {
pub fn adjust_canonicalization(p: &Path) -> String {
    p.display().to_string()
}

#[cfg(windows)]
/// In Windows systems it adjusts the `PathBuf` stripping its `\\?\` prefix.
pub fn adjust_canonicalization(p: PathBuf) -> String {
pub fn adjust_canonicalization(p: &Path) -> String {
    const VERBATIM_PREFIX: &str = r#"\\?\"#;
    let p = p.to_str().unwrap_or_default();
    let p = if p.starts_with(VERBATIM_PREFIX) {
diff --git a/src/lib.rs b/src/lib.rs
index f395485..ba63e99 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,14 +1,81 @@
//! # Static Web Server (SWS)
//!
//! SWS is a cross-platform, high-performance and asynchronous web server for static files-serving.
//!
//! **Considerations:**
//!
//! This crate was published to make it possible for users to embed SWS with ease instead of working around it via forks.
//! For example, allowing users to turn off default features like `tls` and so on.
//!
//! **However**, because the library is highly coupled with the SWS binary project at this point,
//! users might be limited by the exposed APIs, implementations, missing functionality, or dependencies.
//!
//! In the future, we will eventually plan to make SWS library independent from the binary project.
//!
//! That said, if fine-grained control and flexibility are needed then you could want to look at other alternatives like HTTP libraries or frameworks.
//!
//! **Pre-compile binaries:**
//!
//! This is the official SWS crate.
//! If you are looking for platform pre-compile binaries then
//! take a look at [static-web-server.net/download-and-install](https://static-web-server.net/download-and-install).
//!
//! ## Overview
//!
//! **Static Web Server** (or **`SWS`** abbreviated) is a very small and fast production-ready web server suitable to serve static web files or assets.
//!
//! It is focused on **lightness and easy-to-use** principles while keeping [high performance and safety](https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html) powered by [The Rust Programming Language](https://rust-lang.org).
//!
//! Written on top of [Hyper](https://github.com/hyperium/hyper) and [Tokio](https://github.com/tokio-rs/tokio) runtime. It provides [concurrent and asynchronous networking abilities](https://rust-lang.github.io/async-book/01_getting_started/02_why_async.html) as well as the latest HTTP/1 - HTTP/2 implementations.
//!
//! It's cross-platform and available for `Linux`, `macOS`, `Windows` and `FreeBSD` (`x86`/`x86_64`,  `ARM`/`ARM64`) as well as `Docker`.
//!
//! ![static-web-server](https://user-images.githubusercontent.com/1700322/152613820-658f025c-d0a4-46b3-aa6d-bdc7f638ce77.png)
//!
//! ## Features
//!
//! - Built with [Rust](https://rust-lang.org) which is focused on [safety, speed and concurrency](https://kornel.ski/rust-c-speed).
//! - Memory safe and very reduced CPU and RAM overhead.
//! - Blazing fast static files-serving and asynchronous powered by latest [Hyper](https://github.com/hyperium/hyper/), [Tokio](https://github.com/tokio-rs/tokio) and a set of [awesome crates](https://github.com/static-web-server/static-web-server/blob/master/Cargo.toml).
//! - Single __4MB__ (uncompressed) and fully static binary with no dependencies ([Musl libc](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/musl-support-for-fully-static-binaries.html)). Suitable for running on [any Linux distro](https://en.wikipedia.org/wiki/Linux_distribution) or [Docker container](https://hub.docker.com/r/joseluisq/static-web-server/tags).
//! - Optional GZip, Deflate or Brotli compression for text-based web files only.
//! - Compression on-demand via [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header.
//! - [Partial Content Delivery](https://en.wikipedia.org/wiki/Byte_serving) support for byte-serving of large files.
//! - Optional [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) headers for assets.
//! - [Termination signal](https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html) handling with [graceful shutdown](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-terminating-with-grace) ability and grace period.
//! - [HTTP/2](https://tools.ietf.org/html/rfc7540) and TLS support.
//! - [Security headers](https://web.dev/security-headers/) for HTTP/2 by default.
//! - [HEAD](https://tools.ietf.org/html/rfc7231#section-4.3.2) responses.
//! - Lightweight and configurable logging via [tracing](https://github.com/tokio-rs/tracing) crate.
//! - Customizable number of worker threads.
//! - Optional directory listing.
//! - [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) support.
//! - Basic HTTP Authentication.
//! - Customizable HTTP response headers for specific file requests via glob patterns.
//! - Fallback pages for 404 errors, useful for Single-page applications.
//! - Run the server as a [Windows Service](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc783643(v=ws.10)).
//! - Configurable using CLI arguments, environment variables or a TOML file.
//! - Default and custom error pages.
//! - Custom URL rewrites and redirects via glob patterns.
//! - Support for serving pre-compressed (Gzip/Brotli) files.
//! - First-class [Docker](https://docs.docker.com/get-started/overview/) support. [Scratch](https://hub.docker.com/_/scratch) and latest [Alpine Linux](https://hub.docker.com/_/alpine) Docker images.
//! - Ability to accept a socket listener as a file descriptor for use in sandboxing and on-demand applications (E.g [systemd](http://0pointer.de/blog/projects/socket-activation.html)).
//! - Cross-platform. Pre-compiled binaries for Linux, macOS, Windows and FreeBSD (`x86`,`x86_64`,`ARM`,`ARM64`).
//!

#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(dead_code)]

// Extern crates
#[macro_use]
extern crate anyhow;

#[macro_use]
extern crate serde;

// Public modules
pub mod basic_auth;
pub mod compression;
pub mod compression_static;
@@ -20,7 +87,6 @@ pub mod error_page;
pub mod exts;
pub mod fallback_page;
pub mod handler;
pub mod helpers;
pub mod logger;
pub mod redirects;
pub mod rewrites;
@@ -34,13 +100,15 @@ pub mod static_files;
#[cfg(feature = "tls")]
pub mod tls;
pub mod transport;

#[cfg(windows)]
pub mod winservice;

#[macro_use]
pub mod error;

// Private modules
mod helpers;

// Re-exports
pub use error::*;
pub use server::Server;
pub use settings::Settings;
diff --git a/src/logger.rs b/src/logger.rs
index ac666bf..c27786b 100644
--- a/src/logger.rs
+++ b/src/logger.rs
@@ -1,3 +1,6 @@
//! Provides logging initialization for the web server.
//!

use tracing::Level;
use tracing_subscriber::fmt::format::FmtSpan;

diff --git a/src/redirects.rs b/src/redirects.rs
index d51f591..bf88616 100644
--- a/src/redirects.rs
+++ b/src/redirects.rs
@@ -1,3 +1,6 @@
//! Redirection module to handle config redirect URLs with pattern matching support.
//!

use hyper::StatusCode;

use crate::settings::Redirects;
diff --git a/src/rewrites.rs b/src/rewrites.rs
index b007b36..1b4a320 100644
--- a/src/rewrites.rs
+++ b/src/rewrites.rs
@@ -1,3 +1,6 @@
//! Module that allows to rewrite request URLs with pattern matching support.
//!

use crate::settings::Rewrites;

/// It returns a rewrite's destination path if the current request uri
diff --git a/src/security_headers.rs b/src/security_headers.rs
index 73ff7bb..eb35376 100644
--- a/src/security_headers.rs
+++ b/src/security_headers.rs
@@ -1,3 +1,6 @@
//! The module provides several HTTP security headers support.
//!

use http::header::{
    CONTENT_SECURITY_POLICY, STRICT_TRANSPORT_SECURITY, X_CONTENT_TYPE_OPTIONS, X_FRAME_OPTIONS,
    X_XSS_PROTECTION,
diff --git a/src/server.rs b/src/server.rs
index ef19deb..79f71e8 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,3 +1,6 @@
//! Server module intended to construct a multi-thread HTTP or HTTP/2 web server.
//!

#[allow(unused_imports)]
use hyper::server::conn::AddrIncoming;
#[allow(unused_imports)]
@@ -43,7 +46,8 @@ impl Server {
        })
    }

    /// Build and run the multi-thread `Server` as standalone.
    /// Run the multi-thread `Server` as standalone.
    /// It is a top-level function of [run_server_on_rt](#method.run_server_on_rt).
    pub fn run_standalone(self) -> Result {
        // Logging system initialization
        logger::init(&self.opts.general.log_level)?;
@@ -51,7 +55,8 @@ impl Server {
        self.run_server_on_rt(None, || {})
    }

    /// Build and run the multi-thread `Server` which will be used by a Windows service.
    /// Run the multi-thread `Server` which will be used by a Windows service.
    /// It is a top-level function of [run_server_on_rt](#method.run_server_on_rt).
    #[cfg(windows)]
    pub fn run_as_service<F>(self, cancel: Option<Receiver<()>>, cancel_fn: F) -> Result
    where
@@ -60,8 +65,8 @@ impl Server {
        self.run_server_on_rt(cancel, cancel_fn)
    }

    /// Build and run the multi-thread `Server` on Tokio runtime.
    fn run_server_on_rt<F>(self, cancel_recv: Option<Receiver<()>>, cancel_fn: F) -> Result
    /// Build and run the multi-thread `Server` on the Tokio runtime.
    pub fn run_server_on_rt<F>(self, cancel_recv: Option<Receiver<()>>, cancel_fn: F) -> Result
    where
        F: FnOnce(),
    {
@@ -100,7 +105,7 @@ impl Server {

        // Config file option
        if let Some(config_file) = general.config_file {
            let config_file = helpers::adjust_canonicalization(config_file);
            let config_file = helpers::adjust_canonicalization(&config_file);
            tracing::info!("config file: {}", config_file);
        }

diff --git a/src/service.rs b/src/service.rs
index 315fd16..9161c98 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -1,3 +1,6 @@
//! The module provides a custom [Hyper service](hyper::service::Service).
//!

use hyper::{service::Service, Body, Request, Response};
use std::convert::Infallible;
use std::future::{ready, Future, Ready};
@@ -14,6 +17,7 @@ pub struct RouterService {
}

impl RouterService {
    /// Creates a new router service.
    pub fn new(handler: RequestHandler) -> Self {
        Self {
            builder: RequestServiceBuilder::new(handler),
@@ -63,12 +67,14 @@ pub struct RequestServiceBuilder {
}

impl RequestServiceBuilder {
    /// Initializes a new request service buider.
    pub fn new(handler: RequestHandler) -> Self {
        Self {
            handler: Arc::new(handler),
        }
    }

    /// Build a new request service.
    pub fn build(&self, remote_addr: Option<SocketAddr>) -> RequestService {
        RequestService {
            handler: self.handler.clone(),
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index 166c9c5..d21c52e 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -281,11 +281,13 @@ pub struct General {
    // Windows commands
    #[cfg(windows)]
    #[structopt(subcommand)]
    /// Subcommands to install or uninstall the SWS Windows Service.
    pub commands: Option<Commands>,
}

#[cfg(windows)]
#[derive(Debug, StructOpt)]
/// Subcommands to install or uninstall the SWS Windows Service.
pub enum Commands {
    /// Install a Windows Service for the web server.
    #[structopt(name = "install")]
diff --git a/src/settings/file.rs b/src/settings/file.rs
index 6b8c4cc..06685f2 100644
--- a/src/settings/file.rs
+++ b/src/settings/file.rs
@@ -11,15 +11,22 @@ use crate::{helpers, Context, Result};

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
/// Log level variants.
pub enum LogLevel {
    /// Error log level.
    Error,
    /// Warn log level.
    Warn,
    /// Info log level.
    Info,
    /// Debug log level.
    Debug,
    /// Trace log level.
    Trace,
}

impl LogLevel {
    /// Get log level name.
    pub fn name(&self) -> &'static str {
        match self {
            LogLevel::Error => "error",
@@ -33,14 +40,18 @@ impl LogLevel {

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
/// Represents an HTTP headers map.
pub struct Headers {
    /// Header source.
    pub source: String,
    #[serde(rename(deserialize = "headers"), with = "http_serde::header_map")]
    /// headers list.
    pub headers: HeaderMap,
}

#[derive(Debug, Serialize_repr, Deserialize_repr, Clone)]
#[repr(u16)]
/// Represents redirects types.
pub enum RedirectsKind {
    /// Moved Permanently
    Permanent = 301,
@@ -50,16 +61,23 @@ pub enum RedirectsKind {

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
/// Represents redirects types.
pub struct Redirects {
    /// Source of the redirect.
    pub source: String,
    /// Redirect destination.
    pub destination: String,
    /// Redirect type.
    pub kind: RedirectsKind,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
/// Represents rewrites types.
pub struct Rewrites {
    /// Source of the rewrite.
    pub source: String,
    /// Rewrite destination.
    pub destination: String,
}

@@ -67,11 +85,11 @@ pub struct Rewrites {
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Advanced {
    // Headers
    /// Headers
    pub headers: Option<Vec<Headers>>,
    // Rewrites
    /// Rewrites
    pub rewrites: Option<Vec<Rewrites>>,
    // Redirects
    /// Redirects
    pub redirects: Option<Vec<Redirects>>,
}

@@ -80,70 +98,86 @@ pub struct Advanced {
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct General {
    // Address & Root dir
    /// Server address.
    pub host: Option<String>,
    /// Server port.
    pub port: Option<u16>,
    /// Root directory path.
    pub root: Option<PathBuf>,

    // Logging
    /// Logging.
    pub log_level: Option<LogLevel>,

    // Cache Control headers
    /// Cache Control headers.
    pub cache_control_headers: Option<bool>,

    // Compression
    /// Compression.
    pub compression: Option<bool>,

    // Check for a pre-compressed file on disk
    /// Check for a pre-compressed file on disk.
    pub compression_static: Option<bool>,

    // Error pages
    /// Error 404 pages.
    pub page404: Option<PathBuf>,
    /// Error 50x pages.
    pub page50x: Option<PathBuf>,

    // HTTP/2 + TLS
    /// HTTP/2 + TLS.
    #[cfg(feature = "http2")]
    pub http2: Option<bool>,
    /// Http2 tls certificate feature.
    #[cfg(feature = "http2")]
    pub http2_tls_cert: Option<PathBuf>,
    /// Http2 tls key feature.
    #[cfg(feature = "http2")]
    pub http2_tls_key: Option<PathBuf>,

    // Security headers
    /// Security headers.
    pub security_headers: Option<bool>,

    // CORS
    /// Cors allow origins feature.
    pub cors_allow_origins: Option<String>,
    /// Cors allow headers feature.
    pub cors_allow_headers: Option<String>,
    /// Cors expose headers feature.
    pub cors_expose_headers: Option<String>,

    // Directory listing
    /// Directory listing feature.
    pub directory_listing: Option<bool>,
    /// Directory listing order feature.
    pub directory_listing_order: Option<u8>,
    /// Directory listing format feature.
    pub directory_listing_format: Option<DirListFmt>,

    // Basich Authentication
    /// Basich Authentication feature.
    pub basic_auth: Option<String>,

    // File descriptor binding
    /// File descriptor binding feature.
    pub fd: Option<usize>,

    // Worker threads
    /// Worker threads.
    pub threads_multiplier: Option<usize>,

    /// Max blocking threads feature.
    pub max_blocking_threads: Option<usize>,

    /// Grace period feature.
    pub grace_period: Option<u8>,

    /// Page fallback feature.
    pub page_fallback: Option<PathBuf>,

    /// Log remote address feature.
    pub log_remote_address: Option<bool>,

    /// Redirect trailing slash feature.
    pub redirect_trailing_slash: Option<bool>,

    /// Ignore hidden files feature.
    pub ignore_hidden_files: Option<bool>,

    #[cfg(windows)]
    /// windows service feature.
    pub windows_service: Option<bool>,
}

@@ -151,7 +185,9 @@ pub struct General {
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Settings {
    /// General settings.
    pub general: Option<General>,
    /// Advanced settings.
    pub advanced: Option<Advanced>,
}

diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index 8bd577f..7aa3c05 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -1,3 +1,6 @@
//! Module that provides all settings of SWS.
//!

use globset::{Glob, GlobMatcher};
use headers::HeaderMap;
use hyper::StatusCode;
@@ -41,8 +44,11 @@ pub struct Redirects {

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

diff --git a/src/signals.rs b/src/signals.rs
index d3d6d81..f28c8ba 100644
--- a/src/signals.rs
+++ b/src/signals.rs
@@ -1,3 +1,6 @@
//! The module provides signals support like `SIGTERM`, `SIGINT` and `SIGQUIT`.
//!

use tokio::time::{sleep, Duration};

#[cfg(unix)]
diff --git a/src/static_files.rs b/src/static_files.rs
index bdeaf3f..0431938 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -1,7 +1,8 @@
//! Static File handler
//! The static file module which powers the web server.
//!
//! Part of the file is borrowed and adapted at a convenience from
//! https://github.com/seanmonstar/warp/blob/master/src/filters/fs.rs

// Part of the file is borrowed and adapted at a convenience from
// https://github.com/seanmonstar/warp/blob/master/src/filters/fs.rs

use bytes::{Bytes, BytesMut};
use futures_util::future::{Either, Future};
@@ -27,20 +28,31 @@ use crate::{compression_static, directory_listing, Result};

/// Defines all options needed by the static-files handler.
pub struct HandleOpts<'a> {
    /// Request method.
    pub method: &'a Method,
    /// Request headers.
    pub headers: &'a HeaderMap<HeaderValue>,
    /// Request base path.
    pub base_path: &'a PathBuf,
    /// Request base path.
    pub uri_path: &'a str,
    /// Request URI query.
    pub uri_query: Option<&'a str>,
    /// Directory listing feature.
    pub dir_listing: bool,
    /// Directory listing order feature.
    pub dir_listing_order: u8,
    /// Directory listing format feature.
    pub dir_listing_format: &'a DirListFmt,
    /// Redirect trailing slash feature.
    pub redirect_trailing_slash: bool,
    /// Compression static feature.
    pub compression_static: bool,
    /// Ignore hidden files feature.
    pub ignore_hidden_files: bool,
}

/// Entry point to handle incoming requests which map to specific files
/// The server entry point to handle incoming requests which map to specific files
/// on file system and return a file response.
pub async fn handle<'a>(opts: &HandleOpts<'a>) -> Result<(Response<Body>, bool), StatusCode> {
    let method = opts.method;
@@ -472,7 +484,7 @@ const READ_BUF_SIZE: usize = 4_096;
const READ_BUF_SIZE: usize = 8_192;

#[derive(Debug)]
pub struct FileStream<T> {
struct FileStream<T> {
    reader: T,
}

diff --git a/src/tls.rs b/src/tls.rs
index aaebec9..6a20032 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -1,5 +1,7 @@
// Handles requests over TLS
// -> Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/tls.rs
//! The module handles requests over TLS via [Rustls](tokio_rustls::rustls).
//!

// Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/tls.rs

use futures_util::ready;
use hyper::server::accept::Accept;
@@ -23,6 +25,7 @@ use crate::transport::Transport;
/// Represents errors that can occur building the TlsConfig
#[derive(Debug)]
pub enum TlsConfigError {
    /// Error type for I/O operations
    Io(io::Error),
    /// An Error parsing the Certificate
    CertParseError,
@@ -168,6 +171,7 @@ impl TlsConfigBuilder {
        self
    }

    /// Builds TLS configuration.
    pub fn build(mut self) -> Result<ServerConfig, TlsConfigError> {
        let mut cert_rdr = BufReader::new(self.cert);
        let cert = rustls_pemfile::certs(&mut cert_rdr)
@@ -284,9 +288,10 @@ enum State {
    Streaming(tokio_rustls::server::TlsStream<AddrStream>),
}

// tokio_rustls::server::TlsStream doesn't expose constructor methods,
// so we have to TlsAcceptor::accept and handshake to have access to it
// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first
/// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first.
///
/// tokio_rustls::server::TlsStream doesn't expose constructor methods,
/// so we have to TlsAcceptor::accept and handshake to have access to it.
pub struct TlsStream {
    state: State,
    remote_addr: SocketAddr,
@@ -359,12 +364,14 @@ impl AsyncWrite for TlsStream {
    }
}

/// Type to intercept Tls incoming connections.
pub struct TlsAcceptor {
    config: Arc<ServerConfig>,
    incoming: AddrIncoming,
}

impl TlsAcceptor {
    /// Creates a new Tls interceptor.
    pub fn new(config: ServerConfig, incoming: AddrIncoming) -> TlsAcceptor {
        TlsAcceptor {
            config: Arc::new(config),
diff --git a/src/transport.rs b/src/transport.rs
index 7f52ed7..e372e89 100644
--- a/src/transport.rs
+++ b/src/transport.rs
@@ -1,5 +1,7 @@
// Handles requests over TLS
// -> Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/transport.rs
//! Async transport module.
//!

// Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/transport.rs

use std::io;
use std::net::SocketAddr;
@@ -9,7 +11,9 @@ use std::task::{Context, Poll};
use hyper::server::conn::AddrStream;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

/// Transport trait that supports the remote (peer) address.
pub trait Transport: AsyncRead + AsyncWrite {
    /// Returns the remote (peer) address of this connection.
    fn remote_addr(&self) -> Option<SocketAddr>;
}

@@ -19,6 +23,7 @@ impl Transport for AddrStream {
    }
}

/// Type to support `Transport`, `AsyncRead` and `AsyncWrite`.
pub struct LiftIo<T>(pub T);

impl<T: AsyncRead + Unpin> AsyncRead for LiftIo<T> {
diff --git a/src/winservice.rs b/src/winservice.rs
index 652fcdc..e197986 100644
--- a/src/winservice.rs
+++ b/src/winservice.rs
@@ -1,4 +1,5 @@
//! Module that lets SWS to run in a "Windows Service" context
//!

use std::ffi::OsString;
use std::thread;
@@ -181,7 +182,7 @@ pub fn install_service(config_file: Option<PathBuf>) -> Result {

    // Append a `--config-file` path to the binary arguments if present
    if let Some(f) = config_file {
        let f = helpers::adjust_canonicalization(f);
        let f = helpers::adjust_canonicalization(&f);
        if !f.is_empty() {
            service_binary_arguments.push(OsString::from(["--config-file=", &f].concat()));
        }