index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-01-12 23:11:04.0 +00:00:00
committer GitHub <noreply@github.com> 2021-01-12 23:11:04.0 +00:00:00
commit
c73a30ba1ad4c7feffd0a1c6293ba3dd6559c2d7 [patch]
tree
a5541d6a73a587e4e9c2487cc73f0b567a1b6cab
parent
c4df0d3fdfd0726d9b400d1e6569c848897c10b0
parent
015691a804e483b313cc7e38ff2bcece4e0158a5
download
c73a30ba1ad4c7feffd0a1c6293ba3dd6559c2d7.tar.gz

Merge pull request #28 from joseluisq/v2

v2.0.0-beta.1

Diff

 .gitignore                |    1 +-
 Cargo.lock                | 1399 ++++++++++++++++++++++++++++++----------------
 Cargo.toml                |   27 +-
 README.md                 |  167 +-----
 docker/alpine/Dockerfile  |    4 +-
 docker/scratch/Dockerfile |    4 +-
 public/404.html           |   28 +-
 public/50x.html           |   28 +-
 public/index.html         |   14 +-
 sample.env                |    5 +-
 src/bin/server.rs         |  101 +++-
 src/config.rs             |   56 +--
 src/core/cache.rs         |   38 +-
 src/core/config.rs        |   26 +-
 src/core/helpers.rs       |   27 +-
 src/core/logger.rs        |   17 +-
 src/core/mod.rs           |   11 +-
 src/core/rejection.rs     |   22 +-
 src/core/result.rs        |    4 +-
 src/core/signals.rs       |   51 ++-
 src/error_page.rs         |   80 +---
 src/gzip.rs               |   37 +-
 src/helpers.rs            |   35 +-
 src/lib.rs                |    4 +-
 src/logger.rs             |   66 +--
 src/main.rs               |  320 +-----------
 src/signal_manager.rs     |   56 +--
 src/staticfiles.rs        |  100 +---
 28 files changed, 1264 insertions(+), 1464 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..0696392 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7,12 +7,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"

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

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

[[package]]
@@ -21,14 +27,37 @@ 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 = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
 "winapi 0.3.9",
]

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

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

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

[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"

[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
version = "1.3.4"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"

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

[[package]]
name = "bytes"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"

[[package]]
name = "cc"
@@ -103,7 +201,7 @@ dependencies = [
 "num-integer",
 "num-traits",
 "time",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -112,7 +210,7 @@ version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
 "ansi_term",
 "ansi_term 0.11.0",
 "atty",
 "bitflags",
 "strsim",
@@ -122,51 +220,49 @@ dependencies = [
]

[[package]]
name = "cloudabi"
version = "0.0.3"
name = "cpuid-bool"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
 "bitflags",
]
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"

[[package]]
name = "core-foundation"
version = "0.9.1"
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 = "env_logger"
version = "0.7.1"
name = "dtoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
 "atty",
 "humantime",
 "log 0.4.11",
 "regex",
 "termcolor",
]
checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"

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

[[package]]
name = "flate2"
@@ -181,19 +277,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"
@@ -202,10 +299,104 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
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.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c70be434c505aee38639abccb918163b63158a4b4bb791b45b7023044bdc3c9c"
dependencies = [
 "futures-channel",
 "futures-core",
 "futures-io",
 "futures-sink",
 "futures-task",
 "futures-util",
]

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

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

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

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

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

[[package]]
name = "futures-util"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "036a2107cdeb57f6d7322f1b6c363dad67cd63ca3b7d1b925bdf75bd5d96cda9"
dependencies = [
 "futures-core",
 "futures-sink",
 "futures-task",
 "pin-project-lite 0.2.4",
 "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"
@@ -219,6 +410,68 @@ dependencies = [
]

[[package]]
name = "getrandom"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi 0.10.1+wasi-snapshot-preview1",
]

[[package]]
name = "h2"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
dependencies = [
 "bytes 0.5.6",
 "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 0.5.6",
 "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,55 +490,67 @@ dependencies = [
]

[[package]]
name = "httparse"
version = "1.3.4"
name = "http"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
dependencies = [
 "bytes 1.0.1",
 "fnv",
 "itoa",
]

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

[[package]]
name = "hyper"
version = "0.10.16"
name = "httparse"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
dependencies = [
 "base64",
 "httparse",
 "language-tags",
 "log 0.3.9",
 "mime",
 "num_cpus",
 "time",
 "traitobject",
 "typeable",
 "unicase",
 "url",
]
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"

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

[[package]]
name = "hyper"
version = "0.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d375598f442742b0e66208ee12501391f1c7ac0bafb90b4fe53018f81f06068"
checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf"
dependencies = [
 "antidote",
 "hyper",
 "native-tls",
 "bytes 0.5.6",
 "futures-channel",
 "futures-core",
 "futures-util",
 "h2",
 "http",
 "http-body",
 "httparse",
 "httpdate",
 "itoa",
 "pin-project 1.0.4",
 "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 +558,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",
 "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 0.5.6",
]

[[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 +613,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"
@@ -380,26 +630,26 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.81"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"

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

[[package]]
name = "log"
version = "0.4.11"
name = "matchers"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
dependencies = [
 "cfg-if 0.1.10",
 "regex-automata",
]

[[package]]
@@ -416,22 +666,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",
]

@@ -442,31 +687,100 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
dependencies = [
 "adler",
 "autocfg 1.0.1",
 "autocfg",
]

[[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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d050aeedc89243f5347c3e237e3e13dc76fbe4ae3742a57b94dc14f69acf76d4"
dependencies = [
 "buf_redux",
 "httparse",
 "log",
 "mime",
 "mime_guess",
 "quick-error",
 "rand 0.7.3",
 "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]]
@@ -488,7 +802,7 @@ version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
 "autocfg 1.0.1",
 "autocfg",
 "num-traits",
]

@@ -498,7 +812,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
 "autocfg 1.0.1",
 "autocfg",
]

[[package]]
@@ -512,107 +826,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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2"
dependencies = [
 "phf_generator",
 "phf_shared",
 "pin-project-internal 1.0.4",
]

[[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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71"
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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0"
dependencies = [
 "typemap",
]
checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"

[[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 +923,7 @@ dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "version_check 0.9.2",
 "version_check",
]

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

[[package]]
@@ -670,53 +963,11 @@ 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"
dependencies = [
 "autocfg 0.1.7",
 "libc",
 "rand_chacha 0.1.1",
 "rand_core 0.4.2",
 "rand_hc 0.1.0",
 "rand_isaac",
 "rand_jitter",
 "rand_os",
 "rand_pcg",
 "rand_xorshift",
 "winapi",
]

[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
 "getrandom",
 "getrandom 0.1.16",
 "libc",
 "rand_chacha 0.2.2",
 "rand_core 0.5.1",
@@ -724,13 +975,15 @@ dependencies = [
]

[[package]]
name = "rand_chacha"
version = "0.1.1"
name = "rand"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
checksum = "c24fcd450d3fa2b592732565aa4f17a27a61c65ece4726353e000939b0edee34"
dependencies = [
 "autocfg 0.1.7",
 "rand_core 0.3.1",
 "libc",
 "rand_chacha 0.3.0",
 "rand_core 0.6.1",
 "rand_hc 0.3.0",
]

[[package]]
@@ -740,23 +993,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
 "ppv-lite86",
 "rand_core 0.5.1",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
 "rand_core 0.4.2",
 "rand_core 0.5.1",
]

[[package]]
name = "rand_core"
version = "0.4.2"
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
 "ppv-lite86",
 "rand_core 0.6.1",
]

[[package]]
name = "rand_core"
@@ -764,16 +1012,16 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
 "getrandom",
 "getrandom 0.1.16",
]

[[package]]
name = "rand_hc"
version = "0.1.0"
name = "rand_core"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
dependencies = [
 "rand_core 0.3.1",
 "getrandom 0.2.1",
]

[[package]]
@@ -786,143 +1034,136 @@ dependencies = [
]

[[package]]
name = "rand_isaac"
version = "0.1.1"
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
 "rand_core 0.3.1",
 "rand_core 0.6.1",
]

[[package]]
name = "rand_jitter"
version = "0.1.4"
name = "redox_syscall"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
dependencies = [
 "libc",
 "rand_core 0.4.2",
 "winapi",
 "bitflags",
]

[[package]]
name = "rand_os"
version = "0.1.3"
name = "regex"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
 "cloudabi",
 "fuchsia-cprng",
 "libc",
 "rand_core 0.4.2",
 "rdrand",
 "winapi",
 "regex-syntax",
]

[[package]]
name = "rand_pcg"
version = "0.1.2"
name = "regex-automata"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
dependencies = [
 "autocfg 0.1.7",
 "rand_core 0.4.2",
 "byteorder",
 "regex-syntax",
]

[[package]]
name = "rand_xorshift"
version = "0.1.1"
name = "regex-syntax"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
 "rand_core 0.3.1",
]
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"

[[package]]
name = "rdrand"
version = "0.4.0"
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
 "rand_core 0.3.1",
 "winapi 0.3.9",
]

[[package]]
name = "redox_syscall"
version = "0.1.57"
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"

[[package]]
name = "regex"
version = "1.4.2"
name = "safemem"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-syntax",
 "thread_local",
]
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"

[[package]]
name = "regex-syntax"
version = "0.6.21"
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"

[[package]]
name = "remove_dir_all"
version = "0.5.3"
name = "serde"
version = "1.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
 "winapi",
]
checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3"

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

[[package]]
name = "safemem"
version = "0.3.3"
name = "serde_urlencoded"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
dependencies = [
 "dtoa",
 "itoa",
 "serde",
 "url",
]

[[package]]
name = "schannel"
version = "0.1.19"
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
 "lazy_static",
 "winapi",
 "block-buffer 0.7.3",
 "digest 0.8.1",
 "fake-simd",
 "opaque-debug 0.2.3",
]

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

[[package]]
name = "security-framework-sys"
version = "2.0.0"
name = "sharded-slab"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3"
dependencies = [
 "core-foundation-sys",
 "libc",
 "lazy_static",
]

[[package]]
@@ -936,31 +1177,50 @@ 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 = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"

[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"

[[package]]
name = "socket2"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
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 = [
 "chrono",
 "env_logger",
 "flate2",
 "hyper",
 "hyper-native-tls",
 "iron",
 "iron-cors",
 "iron-test",
 "iron_staticfile_middleware",
 "anyhow",
 "jemallocator",
 "log 0.4.11",
 "nix",
 "openssl",
 "signal",
 "structopt",
 "tempdir",
 "tokio",
 "tracing",
 "tracing-subscriber",
 "warp",
]

[[package]]
@@ -995,9 +1255,9 @@ dependencies = [

[[package]]
name = "syn"
version = "1.0.57"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6"
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
dependencies = [
 "proc-macro2",
 "quote",
@@ -1005,36 +1265,17 @@ 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"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
 "cfg-if 0.1.10",
 "cfg-if 1.0.0",
 "libc",
 "rand 0.7.3",
 "rand 0.8.1",
 "redox_syscall",
 "remove_dir_all",
 "winapi",
]

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

[[package]]
@@ -1048,22 +1289,21 @@ dependencies = [

[[package]]
name = "thread_local"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
dependencies = [
 "lazy_static",
]

[[package]]
name = "time"
version = "0.1.44"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
 "libc",
 "wasi 0.10.0+wasi-snapshot-preview1",
 "winapi",
 "winapi 0.3.9",
]

[[package]]
@@ -1082,33 +1322,206 @@ 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 = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
dependencies = [
 "bytes 0.5.6",
 "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 = "tokio-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
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 0.5.6",
 "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 = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"

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

[[package]]
name = "tracing-attributes"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[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 = "tracing-log"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
checksum = "5e0f8c7178e13481ff6765bd169b33e8d554c5d2bbede5e32c356194be02b9b9"
dependencies = [
 "lazy_static",
 "log",
 "tracing-core",
]

[[package]]
name = "typeable"
name = "tracing-serde"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
dependencies = [
 "serde",
 "tracing-core",
]

[[package]]
name = "typemap"
version = "0.3.3"
name = "tracing-subscriber"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8f0c8f4c594e4fc9debc1990deab13238077271ba84dd853d54902ee3401"
dependencies = [
 "ansi_term 0.12.1",
 "chrono",
 "lazy_static",
 "matchers",
 "regex",
 "serde",
 "serde_json",
 "sharded-slab",
 "smallvec",
 "thread_local",
 "tracing",
 "tracing-core",
 "tracing-log",
 "tracing-serde",
]

[[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 = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
dependencies = [
 "unsafe-any",
 "base64",
 "byteorder",
 "bytes 0.5.6",
 "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 +1561,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 +1592,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 +1603,44 @@ 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 = "git+https://github.com/joseluisq/warp.git?branch=0.2.x#5bda31d6bba12c9c3889de1a5bb9dd540a269187"
dependencies = [
 "async-compression",
 "bytes 0.5.6",
 "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"
@@ -1214,9 +1648,15 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
version = "0.10.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9"

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

[[package]]
name = "winapi"
@@ -1229,22 +1669,29 @@ dependencies = [
]

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

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

[[package]]
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..3bd1c63 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,47 +1,38 @@
[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."
description = "A blazing fast static files-serving web server."
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"
log = "0.4"
chrono = "0.4"
env_logger = "0.7"
tokio = { version = "0.2", features = ["full"] }
warp = { git = "https://github.com/joseluisq/warp.git", branch = "0.2.x", features = ["compression"] }
anyhow = "1.0"
tracing = "0.1"
tracing-subscriber = "0.2"
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..11b2a71 100644
--- a/README.md
+++ b/README.md
@@ -1,163 +1,30 @@
# 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 the WIP v2 release under **active** development.
For stable release v1 and contributions please refer to branch [1.x]https://github.com/joseluisq/static-web-server/tree/1.x.

> A blazing fast static files-serving web server. :zap:

**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.
- Memory safety and very reduced CPU and RAM overhead.
- Blazing fast static files-serving powered by [Warp]https://github.com/seanmonstar/warp/ `v0.2` ([Hyper]https://github.com/hyperium/hyper/ `v0.13`), [Tokio]https://github.com/tokio-rs/tokio `v0.2` and a set of [awesome crates]./Cargo.toml.
- Suitable for lightweight [GNU/Linux Docker containers]https://hub.docker.com/r/joseluisq/static-web-server/tags. It's a fully __5MB__ 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.
- Opt-in GZip, Deflate and 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.
- [Cache Control]https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control headers for assets.
- [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.
- [Termination signal]https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html handling.
- [HTTP/2]https://tools.ietf.org/html/rfc7540 + TLS support.
- Default and custom error pages.
- [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.
- Configurable using CLI arguments or environment variables.
- 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
```
- MacOs binary support thanks to [Rust Linux / Darwin Builder]https://github.com/joseluisq/rust-linux-darwin-builder.

## Contributions

diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile
index 28a7497..c9286bb 100644
--- a/docker/alpine/Dockerfile
+++ b/docker/alpine/Dockerfile
@@ -4,7 +4,7 @@ ARG SERVER_VERSION=0.0.0
ENV SERVER_VERSION=${SERVER_VERSION}

LABEL version="${SERVER_VERSION}" \
    description="A blazing fast static files-serving web server powered by Rust Iron." \
    description="A blazing fast static files-serving web server." \
    maintainer="Jose Quintana <joseluisq.net>"

RUN apk --no-cache add ca-certificates tzdata
@@ -28,6 +28,6 @@ CMD ["static-web-server"]
LABEL org.opencontainers.image.vendor="Jose Quintana" \
    org.opencontainers.image.url="https://github.com/joseluisq/static-web-server" \
    org.opencontainers.image.title="Static Web Server" \
    org.opencontainers.image.description="A blazing fast static files-serving web server powered by Rust Iron." \
    org.opencontainers.image.description="A blazing fast static files-serving web server." \
    org.opencontainers.image.version="${SERVER_VERSION}" \
    org.opencontainers.image.documentation="https://github.com/joseluisq/static-web-server"
diff --git a/docker/scratch/Dockerfile b/docker/scratch/Dockerfile
index 2a12fd9..b27f2f3 100644
--- a/docker/scratch/Dockerfile
+++ b/docker/scratch/Dockerfile
@@ -4,7 +4,7 @@ ARG SERVER_VERSION=0.0.0
ENV SERVER_VERSION=${SERVER_VERSION}

LABEL version="${SERVER_VERSION}" \
    description="A blazing fast static files-serving web server powered by Rust Iron." \
    description="A blazing fast static files-serving web server." \
    maintainer="Jose Quintana <joseluisq.net>"

COPY ./bin/static-web-server /
@@ -18,6 +18,6 @@ ENTRYPOINT ["/static-web-server"]
LABEL org.opencontainers.image.vendor="Jose Quintana" \
    org.opencontainers.image.url="https://github.com/joseluisq/static-web-server" \
    org.opencontainers.image.title="Static Web Server" \
    org.opencontainers.image.description="A blazing fast static files-serving web server powered by Rust Iron." \
    org.opencontainers.image.description="A blazing fast static files-serving web server." \
    org.opencontainers.image.version="${SERVER_VERSION}" \
    org.opencontainers.image.documentation="https://github.com/joseluisq/static-web-server"
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..357a9ca 100644
--- a/sample.env
+++ b/sample.env
@@ -2,8 +2,5 @@
E_ARGS = "--port=8787"

# Server configuration

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

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

extern crate static_web_server;

use structopt::StructOpt;
use tracing::warn;
use warp::Filter;

use self::static_web_server::core::*;

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

    let public_head = warp::head().and(
        warp::fs::dir(opts.root.clone())
            .map(cache::control_headers)
            .with(warp::trace::request())
            .recover(rejection::handle_rejection),
    );

    let public_get_default = warp::get().and(
        warp::fs::dir(opts.root.clone())
            .map(cache::control_headers)
            .with(warp::trace::request())
            .recover(rejection::handle_rejection),
    );

    let host = opts.host.parse::<std::net::IpAddr>()?;
    let port = opts.port;

    let accept_encoding = |v: &'static str| warp::header::contains("accept-encoding", v);

    match opts.compression.as_ref() {
        "brotli" => tokio::task::spawn(
            warp::serve(
                public_head.or(warp::get()
                    .and(accept_encoding("br"))
                    .and(
                        warp::fs::dir(opts.root.clone())
                            .map(cache::control_headers)
                            .with(warp::trace::request())
                            .with(warp::compression::brotli(true))
                            .recover(rejection::handle_rejection),
                    )
                    .or(public_get_default)),
            )
            .run((host, port)),
        ),
        "deflate" => tokio::task::spawn(
            warp::serve(
                public_head.or(warp::get()
                    .and(accept_encoding("deflate"))
                    .and(
                        warp::fs::dir(opts.root.clone())
                            .map(cache::control_headers)
                            .with(warp::trace::request())
                            .with(warp::compression::deflate(true))
                            .recover(rejection::handle_rejection),
                    )
                    .or(public_get_default)),
            )
            .run((host, port)),
        ),
        "gzip" => tokio::task::spawn(
            warp::serve(
                public_head.or(warp::get()
                    .and(accept_encoding("gzip"))
                    .and(
                        warp::fs::dir(opts.root.clone())
                            .map(cache::control_headers)
                            .with(warp::trace::request())
                            .with(warp::compression::gzip(true))
                            .recover(rejection::handle_rejection),
                    )
                    .or(public_get_default)),
            )
            .run((host, port)),
        ),
        _ => tokio::task::spawn(warp::serve(public_head.or(public_get_default)).run((host, port))),
    };

    signals::wait(|sig: signals::Signal| {
        let code = signals::as_int(sig);
        warn!("Signal {} caught. Server execution exited.", code);
        std::process::exit(code)
    });

    Ok(())
}

#[tokio::main(max_threads = 10_000)]
async fn main() -> Result {
    server(config::Options::from_args()).await?;

    Ok(())
}
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/cache.rs b/src/core/cache.rs
new file mode 100644
index 0000000..9512619
--- /dev/null
+++ b/src/core/cache.rs
@@ -0,0 +1,38 @@
const CACHE_EXT_ONE_HOUR: [&'static str; 4] = ["atom", "json", "rss", "xml"];
const CACHE_EXT_ONE_YEAR: [&'static str; 30] = [
    "bmp", "bz2", "css", "map", "doc", "gif", "gz", "htc", "ico", "jpg", "mp3", "mp4", "ogg",
    "ogv", "pdf", "png", "rar", "tar", "tgz", "wav", "weba", "webm", "webp", "woff", "zip", "jpeg",
    "js", "mjs", "rtf", "woff2",
];

/// It applies the corresponding Cache-Control headers based on a set of file types.
pub fn control_headers(res: warp::fs::File) -> warp::reply::WithHeader<warp::fs::File> {
    // Default max-age value in seconds (one day)
    let mut max_age = 60 * 60 * 24 as u64;

    if let Some(ext) = res.path().extension() {
        if let Some(ext) = ext.to_str() {
            if CACHE_EXT_ONE_HOUR.iter().any(|n| *n == ext) {
                max_age = 60 * 60;
            } else if CACHE_EXT_ONE_YEAR.iter().any(|x| *x == ext) {
                max_age = 60 * 60 * 24 * 365;
            }
        }
    }

    // HTML file types and others
    warp::reply::with_header(
        res,
        "cache-control",
        [
            "public, max-age=".to_string(),
            duration(max_age).to_string(),
        ]
        .concat(),
    )
}

/// It caps a duration value at ~136 years.
fn duration(n: u64) -> u32 {
    std::cmp::min(n.clone(), u32::MAX as u64) as u32
}
diff --git a/src/core/config.rs b/src/core/config.rs
new file mode 100644
index 0000000..43a5914
--- /dev/null
+++ b/src/core/config.rs
@@ -0,0 +1,26 @@
use structopt::StructOpt;

/// Static 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 = "c", default_value = "gzip", env = "SERVER_COMPRESSION")]
    /// Compression body support for web text-based file types. Values: "gzip", "deflate" or "brotli".
    /// Use an empty value to skip compression.
    pub compression: 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..96f2eff
--- /dev/null
+++ b/src/core/helpers.rs
@@ -0,0 +1,27 @@
use std::path::{Path, PathBuf};

use super::Result;

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

/// 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_string()),
        _ => bail!("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..759dd4f
--- /dev/null
+++ b/src/core/logger.rs
@@ -0,0 +1,17 @@
use tracing::Level;
use tracing_subscriber::fmt::format::FmtSpan;

use super::Result;

/// Initialize logging builder with its levels.
pub fn init(level: &str) -> Result {
    let level = level.parse::<Level>()?;
    match tracing_subscriber::fmt()
        .with_max_level(level)
        .with_span_events(FmtSpan::CLOSE)
        .try_init()
    {
        Err(err) => Err(anyhow!(err)),
        _ => Ok(()),
    }
}
diff --git a/src/core/mod.rs b/src/core/mod.rs
new file mode 100644
index 0000000..188dde3
--- /dev/null
+++ b/src/core/mod.rs
@@ -0,0 +1,11 @@
pub mod cache;
pub mod config;
pub mod helpers;
pub mod logger;
pub mod rejection;
pub mod signals;

#[macro_use]
pub mod result;

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

// It receives a `Rejection` and tries to return a HTML error reply.
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 content = format!(
        "<html><head><title>{}</title></head><body><center><h1>{}</h1></center></body></html>",
        code, code
    );
    Ok(warp::reply::with_status(warp::reply::html(content), code))
}
diff --git a/src/core/result.rs b/src/core/result.rs
new file mode 100644
index 0000000..a183171
--- /dev/null
+++ b/src/core/result.rs
@@ -0,0 +1,4 @@
/// Convenient result return type alias of `anyhow::Result`
pub type Result<T = ()> = anyhow::Result<T, anyhow::Error>;

pub use anyhow::Context;
diff --git a/src/core/signals.rs b/src/core/signals.rs
new file mode 100644
index 0000000..6947b11
--- /dev/null
+++ b/src/core/signals.rs
@@ -0,0 +1,51 @@
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;

pub use signal::Signal;

/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc
pub fn wait<F>(func: F)
where
    F: Fn(signal::Signal),
{
    let sig_trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]);
    for sig in sig_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 => func(sig),
        }
    }
}

/// It casts a given `signal::Signal` to `i32`.
pub fn as_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..2cf9f77
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,4 @@
#[macro_use]
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
    }
}