index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-01-05 0:53:10.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2021-01-05 0:53:10.0 +00:00:00
commit
343711b291add03bb727304fdada863b88deb13d [patch]
tree
4994e80c372a748526e233663d0a48e558f295a1
parent
c4df0d3fdfd0726d9b400d1e6569c848897c10b0
download
343711b291add03bb727304fdada863b88deb13d.tar.gz

refactor: code base for v2



Diff

 .gitignore                 |    1 +-
 Cargo.lock                 | 1096 ++++++++++++++++++++++++++++++++-------------
 Cargo.toml                 |   20 +-
 README.md                  |  159 +-------
 public/404.html            |   28 +-
 public/50x.html            |   28 +-
 public/index.html          |   14 +-
 sample.env                 |    4 +-
 src/bin/server.rs          |   36 +-
 src/config.rs              |   56 +--
 src/core/config.rs         |   21 +-
 src/core/helpers.rs        |   35 +-
 src/core/logger.rs         |   45 ++-
 src/core/mod.rs            |    7 +-
 src/core/rejection.rs      |   23 +-
 src/core/signal_manager.rs |   56 ++-
 src/error_page.rs          |   80 +---
 src/gzip.rs                |   37 +--
 src/helpers.rs             |   35 +-
 src/lib.rs                 |    3 +-
 src/logger.rs              |   66 +---
 src/main.rs                |  320 +-------------
 src/signal_manager.rs      |   56 +--
 src/staticfiles.rs         |  100 +----
 24 files changed, 1031 insertions(+), 1295 deletions(-)

diff --git a/.gitignore b/.gitignore
index 820521f..7b070eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,5 +15,6 @@
**/*.env
release
.vscode
TODO

!sample.env
diff --git a/Cargo.lock b/Cargo.lock
index c601907..f142856 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -16,19 +16,48 @@ dependencies = [
]

[[package]]
name = "alloc-no-stdlib"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192ec435945d87bc2f70992b4d818154b5feede43c09fb7592146374eac90a6"

[[package]]
name = "alloc-stdlib"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2"
dependencies = [
 "alloc-no-stdlib",
]

[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
 "winapi",
 "winapi 0.3.9",
]

[[package]]
name = "antidote"
version = "1.0.0"
name = "anyhow"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86"

[[package]]
name = "async-compression"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72c1f1154e234325b50864a349b9c8e56939e266a4c307c0f159812df2f9537"
dependencies = [
 "brotli",
 "bytes",
 "flate2",
 "futures-core",
 "memchr",
 "pin-project-lite 0.2.0",
]

[[package]]
name = "atty"
@@ -38,7 +67,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -55,19 +84,82 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"

[[package]]
name = "base64"
version = "0.9.3"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
 "block-padding",
 "byte-tools",
 "byteorder",
 "generic-array 0.12.3",
]

[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
 "generic-array 0.14.4",
]

[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
 "byte-tools",
]

[[package]]
name = "brotli"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f29919120f08613aadcd4383764e00526fc9f18b6c0895814faeed0dd78613e"
dependencies = [
 "alloc-no-stdlib",
 "alloc-stdlib",
 "brotli-decompressor",
]

[[package]]
name = "brotli-decompressor"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1052e1c3b8d4d80eb84a8b94f0a1498797b5fb96314c001156a1c761940ef4ec"
dependencies = [
 "alloc-no-stdlib",
 "alloc-stdlib",
]

[[package]]
name = "buf_redux"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
dependencies = [
 "memchr",
 "safemem",
]

[[package]]
name = "bitflags"
version = "1.2.1"
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"

[[package]]
name = "byteorder"
@@ -76,6 +168,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"

[[package]]
name = "bytes"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"

[[package]]
name = "cc"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -103,7 +201,7 @@ dependencies = [
 "num-integer",
 "num-traits",
 "time",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -131,31 +229,45 @@ dependencies = [
]

[[package]]
name = "core-foundation"
version = "0.9.1"
name = "cpuid-bool"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"

[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
 "core-foundation-sys",
 "libc",
 "cfg-if 1.0.0",
]

[[package]]
name = "core-foundation-sys"
version = "0.8.2"
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
 "generic-array 0.12.3",
]

[[package]]
name = "crc32fast"
version = "1.2.1"
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
 "cfg-if 1.0.0",
 "generic-array 0.14.4",
]

[[package]]
name = "dtoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"

[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -163,12 +275,18 @@ checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
 "atty",
 "humantime",
 "log 0.4.11",
 "log",
 "regex",
 "termcolor",
]

[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"

[[package]]
name = "flate2"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -181,19 +299,20 @@ dependencies = [
]

[[package]]
name = "foreign-types"
version = "0.3.2"
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
 "foreign-types-shared",
]
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"

[[package]]
name = "foreign-types-shared"
version = "0.1.1"
name = "form_urlencoded"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
dependencies = [
 "matches",
 "percent-encoding",
]

[[package]]
name = "fs_extra"
@@ -208,6 +327,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"

[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
 "bitflags",
 "fuchsia-zircon-sys",
]

[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"

[[package]]
name = "futures"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0"
dependencies = [
 "futures-channel",
 "futures-core",
 "futures-io",
 "futures-sink",
 "futures-task",
 "futures-util",
]

[[package]]
name = "futures-channel"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64"
dependencies = [
 "futures-core",
 "futures-sink",
]

[[package]]
name = "futures-core"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"

[[package]]
name = "futures-io"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"

[[package]]
name = "futures-sink"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d"

[[package]]
name = "futures-task"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d"
dependencies = [
 "once_cell",
]

[[package]]
name = "futures-util"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2"
dependencies = [
 "futures-core",
 "futures-sink",
 "futures-task",
 "pin-project 1.0.2",
 "pin-utils",
 "slab",
]

[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
dependencies = [
 "typenum",
]

[[package]]
name = "generic-array"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
dependencies = [
 "typenum",
 "version_check",
]

[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -219,6 +438,57 @@ dependencies = [
]

[[package]]
name = "h2"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
dependencies = [
 "bytes",
 "fnv",
 "futures-core",
 "futures-sink",
 "futures-util",
 "http",
 "indexmap",
 "slab",
 "tokio",
 "tokio-util",
 "tracing",
 "tracing-futures",
]

[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"

[[package]]
name = "headers"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f"
dependencies = [
 "base64",
 "bitflags",
 "bytes",
 "headers-core",
 "http",
 "mime",
 "sha-1 0.8.2",
 "time",
]

[[package]]
name = "headers-core"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
dependencies = [
 "http",
]

[[package]]
name = "heck"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -237,12 +507,39 @@ dependencies = [
]

[[package]]
name = "http"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26"
dependencies = [
 "bytes",
 "fnv",
 "itoa",
]

[[package]]
name = "http-body"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
dependencies = [
 "bytes",
 "http",
]

[[package]]
name = "httparse"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"

[[package]]
name = "httpdate"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"

[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -253,39 +550,33 @@ dependencies = [

[[package]]
name = "hyper"
version = "0.10.16"
version = "0.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf"
dependencies = [
 "base64",
 "bytes",
 "futures-channel",
 "futures-core",
 "futures-util",
 "h2",
 "http",
 "http-body",
 "httparse",
 "language-tags",
 "log 0.3.9",
 "mime",
 "num_cpus",
 "time",
 "traitobject",
 "typeable",
 "unicase",
 "url",
]

[[package]]
name = "hyper-native-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d375598f442742b0e66208ee12501391f1c7ac0bafb90b4fe53018f81f06068"
dependencies = [
 "antidote",
 "hyper",
 "native-tls",
 "httpdate",
 "itoa",
 "pin-project 1.0.2",
 "socket2",
 "tokio",
 "tower-service",
 "tracing",
 "want",
]

[[package]]
name = "idna"
version = "0.1.5"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
dependencies = [
 "matches",
 "unicode-bidi",
@@ -293,57 +584,38 @@ dependencies = [
]

[[package]]
name = "iron"
version = "0.6.1"
name = "indexmap"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6d308ca2d884650a8bf9ed2ff4cb13fbb2207b71f64cda11dc9b892067295e8"
checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
dependencies = [
 "hyper",
 "log 0.3.9",
 "mime_guess",
 "modifier",
 "num_cpus",
 "plugin",
 "typemap",
 "url",
 "autocfg 1.0.1",
 "hashbrown",
]

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

[[package]]
name = "iron-test"
version = "0.6.0"
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1944bcf30f8b3f51ebf01e715517dd9755e9480934778d6de70179a41d283c1"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
 "hyper",
 "iron",
 "log 0.3.9",
 "url",
 "uuid",
 "libc",
]

[[package]]
name = "iron_staticfile_middleware"
version = "0.4.2"
source = "git+https://github.com/joseluisq/iron-staticfile-middleware.git?tag=v0.4.2#1681b273edd087ff470d77d2c3fa7d64fc547e6f"
dependencies = [
 "iron",
 "log 0.4.11",
 "mime",
 "mime_guess",
 "rustc-serialize",
 "time",
 "url",
]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"

[[package]]
name = "jemalloc-sys"
@@ -367,10 +639,14 @@ dependencies = [
]

[[package]]
name = "language-tags"
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
 "winapi 0.2.8",
 "winapi-build",
]

[[package]]
name = "lazy_static"
@@ -386,15 +662,6 @@ checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"

[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
dependencies = [
 "log 0.4.11",
]

[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
@@ -416,22 +683,17 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"

[[package]]
name = "mime"
version = "0.2.6"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
dependencies = [
 "log 0.3.9",
]
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"

[[package]]
name = "mime_guess"
version = "1.8.8"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
dependencies = [
 "mime",
 "phf",
 "phf_codegen",
 "unicase",
]

@@ -446,27 +708,96 @@ dependencies = [
]

[[package]]
name = "modifier"
version = "0.1.0"
name = "mio"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58"
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
dependencies = [
 "cfg-if 0.1.10",
 "fuchsia-zircon",
 "fuchsia-zircon-sys",
 "iovec",
 "kernel32-sys",
 "libc",
 "log",
 "miow 0.2.2",
 "net2",
 "slab",
 "winapi 0.2.8",
]

[[package]]
name = "native-tls"
version = "0.2.7"
name = "mio-named-pipes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
dependencies = [
 "lazy_static",
 "log",
 "mio",
 "miow 0.3.6",
 "winapi 0.3.9",
]

[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
 "iovec",
 "libc",
 "log 0.4.11",
 "openssl",
 "openssl-probe",
 "openssl-sys",
 "schannel",
 "security-framework",
 "security-framework-sys",
 "mio",
]

[[package]]
name = "miow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
 "kernel32-sys",
 "net2",
 "winapi 0.2.8",
 "ws2_32-sys",
]

[[package]]
name = "miow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
 "socket2",
 "winapi 0.3.9",
]

[[package]]
name = "multipart"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8209c33c951f07387a8497841122fc6f712165e3f9bda3e6be4645b58188f676"
dependencies = [
 "buf_redux",
 "httparse",
 "log",
 "mime",
 "mime_guess",
 "quick-error",
 "rand 0.6.5",
 "safemem",
 "tempfile",
 "twoway",
]

[[package]]
name = "net2"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
dependencies = [
 "cfg-if 0.1.10",
 "libc",
 "winapi 0.3.9",
]

[[package]]
@@ -512,107 +843,86 @@ dependencies = [
]

[[package]]
name = "openssl"
version = "0.10.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
dependencies = [
 "bitflags",
 "cfg-if 1.0.0",
 "foreign-types",
 "lazy_static",
 "libc",
 "openssl-sys",
]

[[package]]
name = "openssl-probe"
version = "0.1.2"
name = "once_cell"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"

[[package]]
name = "openssl-src"
version = "111.13.0+1.1.1i"
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045e4dc48af57aad93d665885789b43222ae26f4886494da12d1ed58d309dcb6"
dependencies = [
 "cc",
]
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"

[[package]]
name = "openssl-sys"
version = "0.9.60"
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
dependencies = [
 "autocfg 1.0.1",
 "cc",
 "libc",
 "openssl-src",
 "pkg-config",
 "vcpkg",
]
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"

[[package]]
name = "percent-encoding"
version = "1.0.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"

[[package]]
name = "phf"
version = "0.7.24"
name = "pin-project"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
dependencies = [
 "phf_shared",
 "pin-project-internal 0.4.27",
]

[[package]]
name = "phf_codegen"
version = "0.7.24"
name = "pin-project"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7"
dependencies = [
 "phf_generator",
 "phf_shared",
 "pin-project-internal 1.0.2",
]

[[package]]
name = "phf_generator"
version = "0.7.24"
name = "pin-project-internal"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
dependencies = [
 "phf_shared",
 "rand 0.6.5",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "phf_shared"
version = "0.7.24"
name = "pin-project-internal"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f"
dependencies = [
 "siphasher",
 "unicase",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "pkg-config"
version = "0.3.19"
name = "pin-project-lite"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"

[[package]]
name = "plugin"
version = "0.2.6"
name = "pin-project-lite"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0"
dependencies = [
 "typemap",
]
checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"

[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"

[[package]]
name = "ppv-lite86"
@@ -630,7 +940,7 @@ dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "version_check 0.9.2",
 "version_check",
]

[[package]]
@@ -641,7 +951,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
 "proc-macro2",
 "quote",
 "version_check 0.9.2",
 "version_check",
]

[[package]]
@@ -670,29 +980,6 @@ dependencies = [

[[package]]
name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
dependencies = [
 "libc",
 "rand 0.4.6",
]

[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
 "fuchsia-cprng",
 "libc",
 "rand_core 0.3.1",
 "rdrand",
 "winapi",
]

[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
@@ -707,7 +994,7 @@ dependencies = [
 "rand_os",
 "rand_pcg",
 "rand_xorshift",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -802,7 +1089,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
 "libc",
 "rand_core 0.4.2",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -816,7 +1103,7 @@ dependencies = [
 "libc",
 "rand_core 0.4.2",
 "rdrand",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -877,14 +1164,14 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
 "winapi",
 "winapi 0.3.9",
]

[[package]]
name = "rustc-serialize"
version = "0.3.24"
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"

[[package]]
name = "safemem"
@@ -893,36 +1180,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"

[[package]]
name = "schannel"
version = "0.1.19"
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"

[[package]]
name = "serde"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"

[[package]]
name = "serde_json"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
dependencies = [
 "lazy_static",
 "winapi",
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "security-framework"
version = "2.0.0"
name = "serde_urlencoded"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
dependencies = [
 "bitflags",
 "core-foundation",
 "core-foundation-sys",
 "libc",
 "security-framework-sys",
 "dtoa",
 "itoa",
 "serde",
 "url",
]

[[package]]
name = "security-framework-sys"
version = "2.0.0"
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
 "core-foundation-sys",
 "libc",
 "block-buffer 0.7.3",
 "digest 0.8.1",
 "fake-simd",
 "opaque-debug 0.2.3",
]

[[package]]
name = "sha-1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
dependencies = [
 "block-buffer 0.9.0",
 "cfg-if 1.0.0",
 "cpuid-bool",
 "digest 0.9.0",
 "opaque-debug 0.3.0",
]

[[package]]
@@ -936,31 +1250,45 @@ dependencies = [
]

[[package]]
name = "siphasher"
version = "0.2.3"
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
 "libc",
]

[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"

[[package]]
name = "socket2"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "winapi 0.3.9",
]

[[package]]
name = "static-web-server"
version = "1.12.0"
version = "0.0.0"
dependencies = [
 "anyhow",
 "chrono",
 "env_logger",
 "flate2",
 "hyper",
 "hyper-native-tls",
 "iron",
 "iron-cors",
 "iron-test",
 "iron_staticfile_middleware",
 "jemallocator",
 "log 0.4.11",
 "log",
 "nix",
 "openssl",
 "signal",
 "structopt",
 "tempdir",
 "tokio",
 "warp",
]

[[package]]
@@ -1005,16 +1333,6 @@ dependencies = [
]

[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
 "rand 0.4.6",
 "remove_dir_all",
]

[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1025,7 +1343,7 @@ dependencies = [
 "rand 0.7.3",
 "redox_syscall",
 "remove_dir_all",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -1063,7 +1381,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
 "libc",
 "wasi 0.10.0+wasi-snapshot-preview1",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -1082,33 +1400,151 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"

[[package]]
name = "traitobject"
version = "0.1.0"
name = "tokio"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
dependencies = [
 "bytes",
 "fnv",
 "futures-core",
 "iovec",
 "lazy_static",
 "libc",
 "memchr",
 "mio",
 "mio-named-pipes",
 "mio-uds",
 "num_cpus",
 "pin-project-lite 0.1.11",
 "signal-hook-registry",
 "slab",
 "tokio-macros",
 "winapi 0.3.9",
]

[[package]]
name = "typeable"
version = "0.1.2"
name = "tokio-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "typemap"
version = "0.3.3"
name = "tokio-tungstenite"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c"
dependencies = [
 "futures-util",
 "log",
 "pin-project 0.4.27",
 "tokio",
 "tungstenite",
]

[[package]]
name = "tokio-util"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
 "bytes",
 "futures-core",
 "futures-sink",
 "log",
 "pin-project-lite 0.1.11",
 "tokio",
]

[[package]]
name = "tower-service"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"

[[package]]
name = "tracing"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
dependencies = [
 "unsafe-any",
 "cfg-if 1.0.0",
 "log",
 "pin-project-lite 0.2.0",
 "tracing-core",
]

[[package]]
name = "tracing-core"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
dependencies = [
 "lazy_static",
]

[[package]]
name = "tracing-futures"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
dependencies = [
 "pin-project 0.4.27",
 "tracing",
]

[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"

[[package]]
name = "tungstenite"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
dependencies = [
 "base64",
 "byteorder",
 "bytes",
 "http",
 "httparse",
 "input_buffer",
 "log",
 "rand 0.7.3",
 "sha-1 0.9.2",
 "url",
 "utf-8",
]

[[package]]
name = "twoway"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
dependencies = [
 "memchr",
]

[[package]]
name = "typenum"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"

[[package]]
name = "unicase"
version = "1.4.2"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
 "version_check 0.1.5",
 "version_check",
]

[[package]]
@@ -1148,39 +1584,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"

[[package]]
name = "unsafe-any"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
dependencies = [
 "traitobject",
]

[[package]]
name = "url"
version = "1.7.2"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
dependencies = [
 "form_urlencoded",
 "idna",
 "matches",
 "percent-encoding",
]

[[package]]
name = "uuid"
version = "0.5.1"
name = "urlencoding"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
dependencies = [
 "rand 0.3.23",
]
checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593"

[[package]]
name = "vcpkg"
version = "0.2.11"
name = "utf-8"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"

[[package]]
name = "vec_map"
@@ -1190,12 +1615,6 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"

[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"

[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
@@ -1207,6 +1626,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
 "log",
 "try-lock",
]

[[package]]
name = "warp"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f41be6df54c97904af01aa23e613d4521eed7ab23537cede692d4058f6449407"
dependencies = [
 "async-compression",
 "bytes",
 "futures",
 "headers",
 "http",
 "hyper",
 "log",
 "mime",
 "mime_guess",
 "multipart",
 "pin-project 0.4.27",
 "scoped-tls",
 "serde",
 "serde_json",
 "serde_urlencoded",
 "tokio",
 "tokio-tungstenite",
 "tower-service",
 "tracing",
 "tracing-futures",
 "urlencoding",
]

[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1220,6 +1678,12 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"

[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"

[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
@@ -1229,6 +1693,12 @@ dependencies = [
]

[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1240,7 +1710,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -1248,3 +1718,13 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
 "winapi 0.2.8",
 "winapi-build",
]
diff --git a/Cargo.toml b/Cargo.toml
index 23eb974..d4bc078 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,47 +1,39 @@
[package]
name = "static-web-server"
version = "1.12.0"
version = "0.0.0"
authors = ["Jose Quintana <https://git.io/joseluisq>"]
license = "MIT OR Apache-2.0"
description = "A blazing fast static files-serving web server powered by Rust Iron."
repository = "https://github.com/joseluisq/static-web-server"
readme = "README.md"
keywords = [
    "docker-image",
    "static-web-server",
    "file-server",
    "http-server",
    "linux",
    "darwin",
    "musl-libc",
    "x86-64"
    "x86-64",
    "docker-image"
]
categories = ["network-programming", "web-programming::http-server"]
edition = "2018"
include = ["src/**/*", "Cargo.toml", "Cargo.lock"]

[dependencies]
iron = "0.6"
tokio = { version = "0.2", features = ["full"] }
warp = { version = "0.2", features = ["compression"] }
log = "0.4"
chrono = "0.4"
anyhow = "1.0"
env_logger = "0.7"
structopt = "0.3"
flate2 = "1.0"
iron_staticfile_middleware = { git = "https://github.com/joseluisq/iron-staticfile-middleware.git", tag = "v0.4.2" }
hyper-native-tls = "0.3"
nix = "0.14"
signal = "0.7"
iron-cors = "0.8"

[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator]
version = "0.3"

[dev-dependencies]
openssl = { version = "0.10", features = ["vendored"] }
hyper = "0.10"
iron-test = "0.6"
tempdir = "0.3"

[profile.release]
lto = "fat"
codegen-units = 1
diff --git a/README.md b/README.md
index bb1056b..debe834 100644
--- a/README.md
+++ b/README.md
@@ -1,163 +1,8 @@
# Static Web Server [![Docker Image Version (tag latest semver)]https://img.shields.io/docker/v/joseluisq/static-web-server/1]https://hub.docker.com/r/joseluisq/static-web-server/ [![Build Status]https://travis-ci.com/joseluisq/static-web-server.svg?branch=master]https://travis-ci.com/joseluisq/static-web-server [![Docker Image Size (tag)]https://img.shields.io/docker/image-size/joseluisq/static-web-server/1]https://hub.docker.com/r/joseluisq/static-web-server/tags [![Docker Image]https://img.shields.io/docker/pulls/joseluisq/static-web-server.svg]https://hub.docker.com/r/joseluisq/static-web-server/

> A blazing fast static files-serving web server powered by [Rust Iron]https://github.com/iron/iron. :zap:
**STATUS:** This is a WIP v2 release under **active** development. For stable releases and contributions please refer to [master]https://github.com/joseluisq/static-web-server branch.

**Static Web Server** is a very small and fast production-ready web server to serving static web files or assets.

## Features

- Built with [Rust]https://rust-lang.org which is focused on [safety, speed, and concurrency]https://kornel.ski/rust-c-speed.
- Memory safety and reduced overhead of CPU and RAM resources.
- Blazing fast static files-serving thanks to [Rust Iron]https://github.com/iron/iron and [Hyper]https://github.com/hyperium/hyper.
- Suitable for lightweight [GNU/Linux Docker containers]https://hub.docker.com/r/joseluisq/static-web-server/tags. It's a fully __4MB__ static binary thanks to [Rust and Musl libc]https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/musl-support-for-fully-static-binaries.html (`x86_64-unknown-linux-musl`).
- Gzip 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.
- [Cache control headers]https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control for assets.
- [CORS]https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS support.
- [HEAD]https://tools.ietf.org/html/rfc7231#section-4.3.2 responses support.
- [TLS]https://www.openssl.org/ support via [Rust Native TLS]https://docs.rs/native-tls/0.2.3/native_tls/ crate.
- Lightweight and configurable logging.
- 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 available.
- Server configurable via environment variables or CLI arguments.
- MacOs binary support (`x86_64-apple-darwin`) thanks to [Rust Linux / Darwin Builder]https://github.com/joseluisq/rust-linux-darwin-builder.
- Additional HTTP redirect server for redirecting HTTP traffic to HTTPS site.

## Releases

Available for download/install via following methods:

- **Docker Image** on [hub.docker.com/r/joseluisq/static-web-server/]https://hub.docker.com/r/joseluisq/static-web-server/
- **Release binaries** for `GNU/Linux` and `MacOS` x86_64 on [github.com/joseluisq/static-web-server/releases]https://github.com/joseluisq/static-web-server/releases.

## Usage

Server can be configured either via environment variables or their equivalent command-line arguments.

### Environment Variables

| Variable | Description | Default |
| --- | --- | --- |
| `SERVER_NAME` | Name for server. | Default `my-static-server`. |
| `SERVER_HOST` | Host address (E.g 127.0.0.1). | Default `[::]`. |
| `SERVER_PORT` | Host port. | Default `80`. |
| `SERVER_ROOT` | Root directory path of static files. | Default `./public`. |
| `SERVER_ASSETS` | Assets directory path for add cache headers functionality. | Default `./public/assets`. |
| `SERVER_LOG_LEVEL` | Specify a logging level in lower case (see [log::LevelFilter]https://docs.rs/log/0.4.10/log/enum.LevelFilter.html). | Default `error` |
| `SERVER_ERROR_PAGE_404` | 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. Default `./public/404.html`.
| `SERVER_ERROR_PAGE_50X` | 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. Default `./public/50x.html` |
| `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_TLS_REDIRECT_FROM` | Host port for redirecting HTTP requests to HTTPS. This option enables the HTTP redirect feature | Default empty (disabled) |
| `SERVER_TLS_REDIRECT_HOST` | Host name of HTTPS site for redirecting HTTP requests to. | Default host address |
| `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

CLI arguments listed with `static-web-server -h`.

```
static-web-server 1.10.0
A blazing fast static files-serving web server powered by Rust Iron

USAGE:
    static-web-server [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
        --assets <assets>
            Assets directory path for add cache headers functionality [env: SERVER_ASSETS=]  [default: ./public/assets]

        --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]
        --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]
        --root <root>
            Root directory path of static files [env: SERVER_ROOT=]  [default: ./public]

        --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: ]
        --tls-pkcs12-passwd <tls-pkcs12-passwd>
            A specified password to decrypt the private key [env: SERVER_TLS_PKCS12_PASSWD=]  [default: ]

        --tls-redirect-from <tls-redirect-from>
            Host port for redirecting HTTP requests to HTTPS. This option enables the HTTP redirect feature [env:
            SERVER_TLS_REDIRECT_FROM=]
        --tls-redirect-host <tls-redirect-host>
            Host name of HTTPS site for redirecting HTTP requests to. Defaults to host address [env:
            SERVER_TLS_REDIRECT_HOST=]
```

## TLS/SSL

TLS/SSL support is provided by [Rust Native TLS]https://docs.rs/native-tls/0.2.3/native_tls/struct.Identity.html#method.from_pkcs12 crate which supports [PKCS #12 cryptographic identity]https://en.wikipedia.org/wiki/PKCS_12.
An identity is an [X509 certificate]https://en.wikipedia.org/wiki/X.509 certificate along with its corresponding private key and chain of certificates to a trusted root.

For instance, identity files (`.p12` or `.pfx`) can be generated using the [OpenSSL SSL/TLS Toolkit]https://www.openssl.org/docs/manmaster/man1/pkcs12.html:

Generate a self-signed certificate (optional):

```sh
openssl req -x509 -newkey rsa:4096 -nodes -keyout local.key -out local.crt -days 3650
```

Generate a PKCS #12 indentity file (using an existing certificate and private key):

```sh
openssl pkcs12 -export -out identity.p12 -inkey local.key -in local.crt -password pass:my_password
```

## Docker stack

Example using [Traefik Proxy]https://traefik.io/:

```yaml
version: "3.3"

services:
  web:
    image: joseluisq/static-web-server:1
    environment:
        - SERVER_NAME=my-server
        - SERVER_HOST=127.0.0.1
        - SERVER_PORT=80
        - SERVER_ROOT=/public
        # NOTE:
        #   For the server, assets directory is not relative to root.
        #   That's why, it's necessary to be explicit (prefer absolute paths).
        #   See release v1.8.0+ for more details.
        - SERVER_ASSETS=/public/assets
    volumes:
        - ./some-dir-path:/public
    labels:
        - "traefik.enable=true"
        - "traefik.frontend.entryPoints=https"
        - "traefik.backend=localhost_dev"
        - "traefik.frontend.rule=Host:localhost.dev"
        - "traefik.port=80"
    networks:
        - traefik_net

networks:
    traefik_net:
        external: true
```
> A blazing fast static files-serving web server. :zap:

## Contributions

diff --git a/public/404.html b/public/404.html
index 192df19..d339d77 100644
--- a/public/404.html
+++ b/public/404.html
@@ -1,33 +1,9 @@
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>404 Not Found</title>
    <style>
        body {
            font-family: sans-serif;
            color: black;
            text-align: center;
            font-size: 1.25rem;
        }

        h1 a {
            text-decoration: none;
            color: black;
        }

        a {
            color: #0366d6;
        }
    </style>
<title>404 Content was not found</title>
</head>

<body>
    <h1>404</h1>
    <p>Content was not found.</p>
<center><h1>404 Content was not found</h1></center>
</body>

</html>
diff --git a/public/50x.html b/public/50x.html
index ea050e6..9a20203 100644
--- a/public/50x.html
+++ b/public/50x.html
@@ -1,33 +1,9 @@
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>50x Service Unavailable</title>
    <style>
        body {
            font-family: sans-serif;
            color: black;
            text-align: center;
            font-size: 1.25rem;
        }

        h1 a {
            text-decoration: none;
            color: black;
        }

        a {
            color: #0366d6;
        }
    </style>
<title>50x Service Unavailable</title>
</head>

<body>
    <h1>50x</h1>
    <p>Service is temporarily unavailable.</p>
<center><h1>50x Service Unavailable</h1></center>
</body>

</html>
diff --git a/public/index.html b/public/index.html
index 41c3206..dd87f3e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,21 +1,15 @@
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Static Web Server - A blazing fast static files-serving web server powered by Rust Iron.</title>
<html>
    <head>
    <title>Static Web Server</title>
    <link rel="stylesheet" href="/assets/main.css">
    <link rel="shortcut icon" href="/assets/favicon.ico">
</head>

<body>
    <h1>Static Web Server</h1>
    <p>A blazing fast static files-serving web server powered by <a href="https://github.com/iron/iron"
            target="_blank">Rust Iron</a>.</p>
    <p>A blazing fast static files-serving web server powered by Rust.</p>
    <p><a href="https://github.com/joseluisq/static-web-server/" target="_blank">View on GitHub</a></p>
    <script src="/assets/main.js"></script>
</body>

</html>
diff --git a/sample.env b/sample.env
index 5b981e7..d79fcba 100644
--- a/sample.env
+++ b/sample.env
@@ -2,8 +2,4 @@
E_ARGS = "--port=8787"

# Server configuration

SERVER_LOG_LEVEL = "trace"
SERVER_ROOT = "./public"
SERVER_ASSETS = "./public/assets"
SERVER_CORS_ALLOW_ORIGINS = "*"
diff --git a/src/bin/server.rs b/src/bin/server.rs
new file mode 100644
index 0000000..9e6211e
--- /dev/null
+++ b/src/bin/server.rs
@@ -0,0 +1,36 @@
#![deny(warnings)]

#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

#[macro_use]
extern crate static_web_server;

use self::static_web_server::{core::config, core::logger, core::rejection};
use structopt::StructOpt;
use warp::Filter;

/// It creates a new server instance with given options.
async fn server(opts: config::Options) {
    logger::init(&opts.log_level);

    let filters = warp::get()
        .and(warp::fs::dir(opts.root))
        .with(warp::compression::gzip())
        .recover(rejection::handle_rejection);

    note!("server is listening on {}:{}", &opts.host, &opts.port);

    let host = opts
        .host
        .parse::<std::net::IpAddr>()
        .expect("not valid IP address");

    warp::serve(filters).run((host, opts.port)).await
}

#[tokio::main(max_threads = 10_000)]
async fn main() {
    server(config::Options::from_args()).await
}
diff --git a/src/config.rs b/src/config.rs
deleted file mode 100644
index a3ce7b6..0000000
--- a/src/config.rs
+++ /dev/null
@@ -1,56 +0,0 @@
use structopt::StructOpt;

/// A blazing fast static files-serving web server powered by Rust Iron
#[derive(Debug, StructOpt)]
pub struct Options {
    #[structopt(long, default_value = "my-static-server", env = "SERVER_NAME")]
    /// Name for server
    pub name: String,
    #[structopt(long, default_value = "[::]", env = "SERVER_HOST")]
    /// Host address (E.g 127.0.0.1)
    pub host: String,
    #[structopt(long, default_value = "80", env = "SERVER_PORT")]
    /// Host port
    pub port: u16,
    #[structopt(long, default_value = "./public", env = "SERVER_ROOT")]
    /// Root directory path of static files
    pub root: String,
    #[structopt(long, default_value = "./public/assets", env = "SERVER_ASSETS")]
    /// Assets directory path for add cache headers functionality
    pub assets: String,
    #[structopt(
        long,
        default_value = "./public/50x.html",
        env = "SERVER_ERROR_PAGE_50X"
    )]
    /// 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.
    pub page50x: String,
    #[structopt(
        long,
        default_value = "./public/404.html",
        env = "SERVER_ERROR_PAGE_404"
    )]
    /// 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.
    pub page404: String,
    #[structopt(long, env = "SERVER_TLS")]
    /// Enables TLS/SSL support.
    pub tls: bool,
    #[structopt(long, default_value = "", env = "SERVER_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.
    pub tls_pkcs12: String,
    #[structopt(long, default_value = "", env = "SERVER_TLS_PKCS12_PASSWD")]
    /// A specified password to decrypt the private key.
    pub tls_pkcs12_passwd: String,
    #[structopt(long, env = "SERVER_TLS_REDIRECT_FROM")]
    /// Host port for redirecting HTTP requests to HTTPS. This option enables the HTTP redirect feature.
    pub tls_redirect_from: Option<u16>,
    #[structopt(long, env = "SERVER_TLS_REDIRECT_HOST")]
    /// Host name of HTTPS site for redirecting HTTP requests to. Defaults to host address.
    pub tls_redirect_host: Option<String>,
    #[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/core/config.rs b/src/core/config.rs
new file mode 100644
index 0000000..ddd1d83
--- /dev/null
+++ b/src/core/config.rs
@@ -0,0 +1,21 @@
use structopt::StructOpt;

/// Warp Web Server
#[derive(Debug, StructOpt)]
pub struct Options {
    #[structopt(long, short = "s", 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")]
    /// Host port
    pub port: u16,

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

    #[structopt(long, short = "l", default_value = "error", env = "SERVER_LOG_LEVEL")]
    /// Specify a logging level in lower case.
    pub log_level: String,
}
diff --git a/src/core/helpers.rs b/src/core/helpers.rs
new file mode 100644
index 0000000..d251a0c
--- /dev/null
+++ b/src/core/helpers.rs
@@ -0,0 +1,35 @@
use std::error;
use std::path::{Path, PathBuf};

/// Validate and return a directory path.
pub fn get_valid_dirpath<P: AsRef<Path>>(path: P) -> Result<PathBuf, Box<dyn error::Error>>
where
    PathBuf: From<P>,
{
    match PathBuf::from(path) {
        v if !v.exists() => Result::Err(From::from(format!("path \"{:?}\" was not found", &v))),
        v if !v.is_dir() => {
            Result::Err(From::from(format!("path \"{:?}\" is not a directory", &v)))
        }
        v => Result::Ok(v),
    }
}

/// Get the directory name of a valid directory path.
pub fn get_dirname<P: AsRef<Path>>(path: P) -> Result<String, Box<dyn error::Error>>
where
    PathBuf: From<P>,
{
    let path = match get_valid_dirpath(path) {
        Err(e) => return Result::Err(e),
        Ok(v) => v,
    };

    match path.iter().last() {
        Some(v) => Result::Ok(v.to_str().unwrap().to_string()),
        _ => Result::Err(From::from(format!(
            "directory name for path \"{:?}\" was not determined",
            path,
        ))),
    }
}
diff --git a/src/core/logger.rs b/src/core/logger.rs
new file mode 100644
index 0000000..c25df23
--- /dev/null
+++ b/src/core/logger.rs
@@ -0,0 +1,45 @@
use env_logger::Builder;
use log::LevelFilter;
use std::io::Write;

/// Initialize logging builder with its levels
pub fn init(level: &str) {
    let log_level = match level {
        "off" => LevelFilter::Off,
        "error" => LevelFilter::Error,
        "warn" => LevelFilter::Warn,
        "info" => LevelFilter::Info,
        "debug" => LevelFilter::Debug,
        "trace" => LevelFilter::Trace,
        _ => {
            println!("Log level \"{}\" is not supported", level);
            std::process::exit(1);
        }
    };

    Builder::new()
        .filter_level(log_level)
        .format(|buf, record| {
            writeln!(
                buf,
                "{} [{}] - {}",
                chrono::Local::now().format("%Y-%m-%dT%H:%M:%S"),
                record.level(),
                record.args()
            )
        })
        .init();
}

// `note` is a custom log level macro which is not affected by env log level.
// It takes precedence over other log levels
#[macro_export]
macro_rules! note {
    ($($arg:tt)*) => ({
        println!(
            "{} [NOTE] - {}",
            chrono::Local::now().format("%Y-%m-%dT%H:%M:%S"),
            format!($($arg)*)
        );
    })
}
diff --git a/src/core/mod.rs b/src/core/mod.rs
new file mode 100644
index 0000000..5d48af2
--- /dev/null
+++ b/src/core/mod.rs
@@ -0,0 +1,7 @@
#[macro_use]
pub mod logger;

pub mod config;
pub mod helpers;
pub mod rejection;
pub mod signal_manager;
diff --git a/src/core/rejection.rs b/src/core/rejection.rs
new file mode 100644
index 0000000..f798e87
--- /dev/null
+++ b/src/core/rejection.rs
@@ -0,0 +1,23 @@
use anyhow::Result;
use std::convert::Infallible;
use warp::http::StatusCode;
use warp::{Rejection, Reply};

// This function receives a `Rejection` and tries to return a custom
// value, otherwise simply passes the rejection along.
pub async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
    let code = if err.is_not_found() {
        StatusCode::NOT_FOUND
    } else if let Some(_) = err.find::<warp::filters::body::BodyDeserializeError>() {
        StatusCode::BAD_REQUEST
    } else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
        StatusCode::METHOD_NOT_ALLOWED
    } else {
        StatusCode::INTERNAL_SERVER_ERROR
    };
    let body = format!(
        "<html><head><title>{}</title></head><body><center><h1>{}</h1></center></body></html>",
        code, code
    );
    Ok(warp::reply::with_status(warp::reply::html(body), code))
}
diff --git a/src/core/signal_manager.rs b/src/core/signal_manager.rs
new file mode 100644
index 0000000..06be40c
--- /dev/null
+++ b/src/core/signal_manager.rs
@@ -0,0 +1,56 @@
use nix::errno::Errno;
use nix::libc::c_int;
use nix::sys::signal::{SIGCHLD, SIGINT, SIGTERM};
use nix::sys::wait::WaitStatus::{Exited, Signaled, StillAlive};
use nix::sys::wait::{waitpid, WaitPidFlag};
use nix::Error;
use signal::trap::Trap;

/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc
pub fn wait_for_signal<F>(f: F)
where
    F: Fn(signal::Signal),
{
    let signal_trap = Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]);

    for sig in signal_trap {
        match sig {
            SIGCHLD => {
                // Current std::process::Command ip does not have a way to find
                // process id, so we just wait until we have no children
                loop {
                    match waitpid(None, Some(WaitPidFlag::WNOHANG)) {
                        Ok(Exited(pid, status)) => {
                            println!("{} exited with status {}", pid, status);
                            continue;
                        }
                        Ok(Signaled(pid, sig, _)) => {
                            println!("{} killed by {}", pid, sig as c_int);
                            continue;
                        }
                        Ok(StillAlive) => {
                            break;
                        }
                        Ok(status) => {
                            println!("Temporary status {:?}", status);
                            continue;
                        }
                        Err(Error::Sys(Errno::ECHILD)) => {
                            return;
                        }
                        Err(e) => {
                            panic!("Error {:?}", e);
                        }
                    }
                }
            }

            sig => f(sig),
        }
    }
}

/// It casts a `signal::Signal` to `i32`
pub fn signal_to_int(sig: signal::Signal) -> i32 {
    sig as c_int
}
diff --git a/src/error_page.rs b/src/error_page.rs
deleted file mode 100644
index 1b15877..0000000
--- a/src/error_page.rs
+++ /dev/null
@@ -1,80 +0,0 @@
use iron::mime;
use iron::prelude::*;
use iron::status;
use iron::AfterMiddleware;
use std::fs;
use std::path::Path;

const PAGE_404: &str = "<h2>404</h2><p>Content could not found</p>";
const PAGE_50X: &str =
    "<h2>50x</h2><p>SERVICE is temporarily unavailable due an unexpected error</p>";

/// Custom Error pages middleware for Iron
pub struct ErrorPage {
    /// HTML file content for 404 errors.
    pub page404: String,
    /// HTML file content for 50x errors.
    pub page50x: String,
}

impl ErrorPage {
    /// Create a new instance of `ErrorPage` middleware with a given html pages.
    pub fn new<P: AsRef<Path>>(page_404_path: P, page_50x_path: P) -> ErrorPage {
        let page404 = if Path::new(&page_404_path.as_ref()).exists() {
            fs::read_to_string(page_404_path).unwrap()
        } else {
            String::from(PAGE_404)
        };

        let page50x = if Path::new(&page_50x_path.as_ref()).exists() {
            fs::read_to_string(page_50x_path).unwrap()
        } else {
            String::from(PAGE_50X)
        };

        ErrorPage { page404, page50x }
    }
}

impl AfterMiddleware for ErrorPage {
    fn after(&self, req: &mut Request, resp: Response) -> IronResult<Response> {
        let mut no_status_error = false;

        let content_type = "text/html"
            .parse::<mime::Mime>()
            .expect("Unable to create a default content type header");

        let mut resp = match resp.status {
            Some(status::NotFound) => {
                Response::with((content_type, status::NotFound, self.page404.as_str()))
            }
            Some(status::InternalServerError) => Response::with((
                content_type,
                status::InternalServerError,
                self.page50x.as_str(),
            )),
            Some(status::BadGateway) => {
                Response::with((content_type, status::BadGateway, self.page50x.as_str()))
            }
            Some(status::ServiceUnavailable) => Response::with((
                content_type,
                status::ServiceUnavailable,
                self.page50x.as_str(),
            )),
            Some(status::GatewayTimeout) => {
                Response::with((content_type, status::GatewayTimeout, self.page50x.as_str()))
            }
            _ => {
                no_status_error = true;
                resp
            }
        };

        // Empty response body only on HEAD requests and status error (404,50x)
        if req.method == iron::method::Head && !no_status_error {
            resp.set_mut(vec![]);
        }

        Ok(resp)
    }
}
diff --git a/src/gzip.rs b/src/gzip.rs
deleted file mode 100644
index 737c72a..0000000
--- a/src/gzip.rs
+++ /dev/null
@@ -1,37 +0,0 @@
use flate2::write::GzEncoder;
use flate2::Compression;
use iron::headers::{AcceptEncoding, ContentEncoding, ContentType, Encoding};
use iron::prelude::*;
use iron::AfterMiddleware;
use iron_staticfile_middleware::helpers;

pub struct GzipMiddleware;

impl AfterMiddleware for GzipMiddleware {
    fn after(&self, req: &mut Request, mut resp: Response) -> IronResult<Response> {
        // Skip Gzip response on HEAD requests
        if req.method == iron::method::Head {
            return Ok(resp);
        }

        // Enable Gzip compression only for known text-based file types
        let enable_gz = helpers::is_text_mime_type(resp.headers.get::<ContentType>());
        let accept_gz = helpers::accept_gzip(req.headers.get::<AcceptEncoding>());

        if enable_gz && accept_gz {
            let compressed_bytes = resp.body.as_mut().map(|b| {
                let mut encoder = GzEncoder::new(vec![], Compression::fast());
                {
                    let _ = b.write_body(&mut encoder);
                }
                encoder.finish().unwrap()
            });
            if let Some(b) = compressed_bytes {
                resp.headers.set(ContentEncoding(vec![Encoding::Gzip]));
                resp.set_mut(b);
            }
        }

        Ok(resp)
    }
}
diff --git a/src/helpers.rs b/src/helpers.rs
deleted file mode 100644
index d251a0c..0000000
--- a/src/helpers.rs
+++ /dev/null
@@ -1,35 +0,0 @@
use std::error;
use std::path::{Path, PathBuf};

/// Validate and return a directory path.
pub fn get_valid_dirpath<P: AsRef<Path>>(path: P) -> Result<PathBuf, Box<dyn error::Error>>
where
    PathBuf: From<P>,
{
    match PathBuf::from(path) {
        v if !v.exists() => Result::Err(From::from(format!("path \"{:?}\" was not found", &v))),
        v if !v.is_dir() => {
            Result::Err(From::from(format!("path \"{:?}\" is not a directory", &v)))
        }
        v => Result::Ok(v),
    }
}

/// Get the directory name of a valid directory path.
pub fn get_dirname<P: AsRef<Path>>(path: P) -> Result<String, Box<dyn error::Error>>
where
    PathBuf: From<P>,
{
    let path = match get_valid_dirpath(path) {
        Err(e) => return Result::Err(e),
        Ok(v) => v,
    };

    match path.iter().last() {
        Some(v) => Result::Ok(v.to_str().unwrap().to_string()),
        _ => Result::Err(From::from(format!(
            "directory name for path \"{:?}\" was not determined",
            path,
        ))),
    }
}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..6e03bf4
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,3 @@
extern crate anyhow;

pub mod core;
diff --git a/src/logger.rs b/src/logger.rs
deleted file mode 100644
index fb6e3dc..0000000
--- a/src/logger.rs
+++ /dev/null
@@ -1,66 +0,0 @@
use chrono::Local;
use env_logger::Builder;
use iron::prelude::*;
use iron::AfterMiddleware;
use log::LevelFilter;
use std::io::Write;

/// Initialize logging builder and format
pub fn init(log_level_str: &str) {
    let log_level = match log_level_str {
        "off" => LevelFilter::Off,
        "error" => LevelFilter::Error,
        "warn" => LevelFilter::Warn,
        "info" => LevelFilter::Info,
        "debug" => LevelFilter::Debug,
        "trace" => LevelFilter::Trace,
        _ => {
            println!("Log level \"{}\" is not supported", log_level_str);
            std::process::exit(1);
        }
    };

    Builder::new()
        .filter_level(log_level)
        .format(|buf, record| {
            writeln!(
                buf,
                "{} [{}] - {}",
                Local::now().format("%Y-%m-%dT%H:%M:%S"),
                record.level(),
                record.args()
            )
        })
        .init();
}

/// Print specific log info for the server which doesn't depend on any level
pub fn log_server(msg: &str) {
    println!(
        "{} [SERVER] - {}",
        Local::now().format("%Y-%m-%dT%H:%M:%S"),
        &msg
    );
}

pub struct Logger;

impl AfterMiddleware for Logger {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        info!(
            "Request [{}] {} - {}",
            req.method,
            res.status
                .into_iter()
                .map(|i| i.to_string())
                .collect::<String>(),
            req.url
                .path()
                .into_iter()
                .map(|i| format!("/{}", i))
                .collect::<String>()
        );

        Ok(res)
    }
}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 50cfd90..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,320 +0,0 @@
#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

#[macro_use]
extern crate log;

use crate::config::Options;
use hyper_native_tls::NativeTlsServer;
use iron::{prelude::*, Listening};
use iron_staticfile_middleware::HttpToHttpsRedirect;
use staticfiles::*;
use structopt::StructOpt;

mod config;
mod error_page;
mod gzip;
mod helpers;
mod logger;
mod signal_manager;
mod staticfiles;

/// Struct for holding a reference to a running iron server instance
#[derive(Debug)]
struct RunningServer {
    listening: Listening,
    server_type: String,
}

fn on_server_running(server_name: &str, running_servers: &Vec<RunningServer>) {
    // Notify when server is running
    running_servers.iter().for_each(|server| {
        logger::log_server(&format!(
            "{} Server \"{}\" is listening on {}",
            server.server_type, server_name, server.listening.socket
        ))
    });

    // Wait for incoming signals (E.g Ctrl+C (SIGINT), SIGTERM, etc
    signal_manager::wait_for_signal(|sig: signal::Signal| {
        let code = signal_manager::signal_to_int(sig);

        println!();
        warn!("SIGINT {} caught. HTTP Server execution exited.", code);
        std::process::exit(code)
    })
}

fn main() {
    let opts = Options::from_args();

    logger::init(&opts.log_level);

    let addr = &format!("{}{}{}", opts.host, ":", opts.port);

    // Configure & launch the HTTP server

    let files = StaticFiles::new(StaticFilesOptions {
        root_dir: opts.root,
        assets_dir: opts.assets,
        page_50x_path: opts.page50x,
        page_404_path: opts.page404,
        cors_allow_origins: opts.cors_allow_origins,
    });

    let mut running_servers = Vec::new();
    if opts.tls {
        // Launch static HTTPS server
        let ssl = NativeTlsServer::new(opts.tls_pkcs12, &opts.tls_pkcs12_passwd).unwrap();

        match Iron::new(files.handle()).https(addr, ssl) {
            Result::Ok(listening) => running_servers.push(RunningServer {
                listening,
                server_type: "Static HTTPS".to_string(),
            }),
            Result::Err(err) => panic!("{:?}", err),
        }

        // Launch redirect HTTP server (if requested)
        if let Some(port_redirect) = opts.tls_redirect_from {
            let addr_redirect = &format!("{}{}{}", opts.host, ":", port_redirect);
            let host_redirect = match opts.tls_redirect_host.as_ref() {
                Some(host) => host,
                None => &opts.host,
            };
            let handler =
                Chain::new(HttpToHttpsRedirect::new(&host_redirect, opts.port).permanent());
            match Iron::new(handler).http(addr_redirect) {
                Result::Ok(listening) => running_servers.push(RunningServer {
                    listening,
                    server_type: "Redirect HTTP".to_string(),
                }),
                Result::Err(err) => panic!("{:?}", err),
            }
        }
    } else {
        // Launch static HTTP server
        match Iron::new(files.handle()).http(addr) {
            Result::Ok(listening) => running_servers.push(RunningServer {
                listening,
                server_type: "Static HTTP".to_string(),
            }),
            Result::Err(err) => panic!("{:?}", err),
        }
    }
    on_server_running(&opts.name, &running_servers);
}

#[cfg(test)]
mod test {
    extern crate hyper;
    extern crate iron_test;
    extern crate tempdir;

    use super::*;

    use std::fs::{DirBuilder, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    use self::hyper::header::Headers;
    use self::iron_test::{request, response};
    use self::tempdir::TempDir;
    use iron::headers::{ContentLength, ContentType};
    use iron::status;

    struct TestFilesystemSetup(TempDir);

    impl TestFilesystemSetup {
        fn new() -> Self {
            TestFilesystemSetup(TempDir::new("test").expect("Could not create test directory"))
        }

        fn path(&self) -> &Path {
            self.0.path()
        }

        fn dir(&self, name: &str) -> PathBuf {
            let p = self.path().join(name);
            DirBuilder::new()
                .recursive(true)
                .create(&p)
                .expect("Could not create directory");
            p
        }

        fn file(&self, name: &str, body: Vec<u8>) -> PathBuf {
            let p = self.path().join(name);

            let mut file = File::create(&p).expect("Could not create file");
            file.write_all(&body).expect("Could not write to file");

            p
        }
    }

    #[test]
    fn staticfile_allow_request_methods() {
        let opts = Options::from_args();

        let files = StaticFiles::new(StaticFilesOptions {
            root_dir: opts.root,
            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())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::Ok));

        let response = request::get("http://127.0.0.1/", Headers::new(), &files.handle())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::Ok));
    }

    #[test]
    fn staticfile_empty_body_on_head_request() {
        let opts = Options::from_args();

        let files = StaticFiles::new(StaticFilesOptions {
            root_dir: opts.root,
            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())
            .expect("Response was a http error");

        assert_eq!(res.status, Some(status::Ok));

        let result_body = response::extract_body_to_bytes(res);
        assert_eq!(result_body, vec!());
    }

    #[test]
    fn staticfile_valid_content_length_on_head_request() {
        let root = TestFilesystemSetup::new();
        root.dir("root");
        root.file("index.html", b"<html><h2>hello</h2></html>".to_vec());

        let assets = TestFilesystemSetup::new();
        assets.dir("assets");

        let opts = Options::from_args();

        let files = StaticFiles::new(StaticFilesOptions {
            root_dir: root.path().to_str().unwrap().to_string(),
            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())
            .expect("Response was a http error");

        assert_eq!(res.status, Some(status::Ok));

        let content_length = res.headers.get::<ContentLength>().unwrap();

        assert_eq!(content_length.0, 27);
    }

    #[test]
    fn staticfile_zero_content_length_on_404_head_request() {
        let opts = Options::from_args();

        let files = StaticFiles::new(StaticFilesOptions {
            root_dir: opts.root,
            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())
            .expect("Response was a http error");

        assert_eq!(res.status, Some(status::NotFound));

        let content_length = res.headers.get::<ContentLength>().unwrap();

        assert_eq!(content_length.0, 0);
    }

    #[test]
    fn staticfile_disallow_request_methods() {
        let opts = Options::from_args();

        let files = StaticFiles::new(StaticFilesOptions {
            root_dir: opts.root,
            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())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::MethodNotAllowed));

        let response = request::delete("http://127.0.0.1/", Headers::new(), &files.handle())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::MethodNotAllowed));

        let response = request::put("http://127.0.0.1/", Headers::new(), "", &files.handle())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::MethodNotAllowed));

        let response = request::patch("http://127.0.0.1/", Headers::new(), "", &files.handle())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::MethodNotAllowed));

        let response = request::options("http://127.0.0.1/", Headers::new(), &files.handle())
            .expect("Response was a http error");

        assert_eq!(response.status, Some(status::MethodNotAllowed));
    }

    #[test]
    fn staticfile_valid_content_type_for_404() {
        let root = TestFilesystemSetup::new();
        root.dir("root");

        let assets = TestFilesystemSetup::new();
        assets.dir("assets");

        let opts = Options::from_args();

        let files = StaticFiles::new(StaticFilesOptions {
            root_dir: root.path().to_str().unwrap().to_string(),
            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/unknown", Headers::new(), &files.handle())
            .expect("Response was a http error");

        assert_eq!(res.status, Some(status::NotFound));

        let content_type = res.headers.get::<ContentType>().unwrap();

        assert_eq!(
            content_type.0,
            "text/html".parse::<iron::mime::Mime>().unwrap()
        );
    }
}
diff --git a/src/signal_manager.rs b/src/signal_manager.rs
deleted file mode 100644
index 06be40c..0000000
--- a/src/signal_manager.rs
+++ /dev/null
@@ -1,56 +0,0 @@
use nix::errno::Errno;
use nix::libc::c_int;
use nix::sys::signal::{SIGCHLD, SIGINT, SIGTERM};
use nix::sys::wait::WaitStatus::{Exited, Signaled, StillAlive};
use nix::sys::wait::{waitpid, WaitPidFlag};
use nix::Error;
use signal::trap::Trap;

/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc
pub fn wait_for_signal<F>(f: F)
where
    F: Fn(signal::Signal),
{
    let signal_trap = Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]);

    for sig in signal_trap {
        match sig {
            SIGCHLD => {
                // Current std::process::Command ip does not have a way to find
                // process id, so we just wait until we have no children
                loop {
                    match waitpid(None, Some(WaitPidFlag::WNOHANG)) {
                        Ok(Exited(pid, status)) => {
                            println!("{} exited with status {}", pid, status);
                            continue;
                        }
                        Ok(Signaled(pid, sig, _)) => {
                            println!("{} killed by {}", pid, sig as c_int);
                            continue;
                        }
                        Ok(StillAlive) => {
                            break;
                        }
                        Ok(status) => {
                            println!("Temporary status {:?}", status);
                            continue;
                        }
                        Err(Error::Sys(Errno::ECHILD)) => {
                            return;
                        }
                        Err(e) => {
                            panic!("Error {:?}", e);
                        }
                    }
                }
            }

            sig => f(sig),
        }
    }
}

/// It casts a `signal::Signal` to `i32`
pub fn signal_to_int(sig: signal::Signal) -> i32 {
    sig as c_int
}
diff --git a/src/staticfiles.rs b/src/staticfiles.rs
deleted file mode 100644
index 7649ac8..0000000
--- a/src/staticfiles.rs
+++ /dev/null
@@ -1,100 +0,0 @@
use crate::error_page::ErrorPage;
use crate::gzip::GzipMiddleware;
use crate::helpers;
use crate::logger::{log_server, Logger};

use iron::mime;
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.
pub struct StaticFiles {
    opts: StaticFilesOptions,
}

pub struct StaticFilesOptions {
    pub root_dir: String,
    pub assets_dir: String,
    pub page_50x_path: String,
    pub page_404_path: String,
    pub cors_allow_origins: String,
}

impl StaticFiles {
    /// Create a new instance of `StaticFiles` with given options.
    pub fn new(opts: StaticFilesOptions) -> StaticFiles {
        StaticFiles { opts }
    }

    /// Handle static files for current `StaticFiles` middleware.
    pub fn handle(&self) -> Chain {
        // Check the root directory
        let root_dir = &match helpers::get_valid_dirpath(&self.opts.root_dir) {
            Err(e) => {
                error!("{}", e);
                std::process::exit(1)
            }
            Ok(v) => v,
        };

        // Check the assets directory
        let assets_dir = &match helpers::get_valid_dirpath(&self.opts.assets_dir) {
            Err(e) => {
                error!("{}", e);
                std::process::exit(1)
            }
            Ok(v) => v,
        };

        // Get the assets directory name
        let assets_dirname = &match helpers::get_dirname(assets_dir) {
            Err(e) => {
                error!("{}", e);
                std::process::exit(1)
            }
            Ok(v) => v,
        };

        // Define middleware chain
        let mut chain = Chain::new(
            Staticfile::new(root_dir, assets_dir).expect("Directory to serve files was not found"),
        );
        let one_day = Duration::new(60 * 60 * 24, 0);
        let one_year = Duration::new(60 * 60 * 24 * 365, 0);
        let default_content_type = "text/html"
            .parse::<mime::Mime>()
            .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));
        chain.link_after(GzipMiddleware);
        chain.link_after(Logger);
        chain.link_after(ErrorPage::new(
            &self.opts.page_404_path,
            &self.opts.page_50x_path,
        ));
        chain
    }
}