From 618342699bcb7a956419eb53403608f0381bb1eb Mon Sep 17 00:00:00 2001
From: Pavel Kirilin <win10@list.ru>
Date: Wed, 22 Dec 2021 20:00:38 +0400
Subject: [PATCH] Added Notifications. Description: * Added notification
 system; * Updated Actix-Web up to 4.0.0; * Changed log provider; * Added Http
 hooks; * Switched async runtime reactor to tokio.

Signed-off-by: Pavel Kirilin <win10@list.ru>
---
 .pre-commit-config.yaml                       |   45 +
 Cargo.lock                                    | 1376 +++++++----------
 Cargo.toml                                    |   23 +-
 README.md                                     |    4 +-
 imgs/rustus_startup_logo.txt                  |    7 +
 src/config.rs                                 |  149 +-
 src/errors.rs                                 |   15 +-
 .../models/available_info_storages.rs         |   33 +-
 src/info_storages/models/file_info.rs         |    4 +-
 src/main.rs                                   |   92 +-
 src/notifiers/http_notifier.rs                |   48 +
 src/notifiers/mod.rs                          |    9 +-
 src/notifiers/models/hooks.rs                 |   20 +
 src/notifiers/models/message.rs               |    7 -
 src/notifiers/models/message_format.rs        |  162 ++
 src/notifiers/models/mod.rs                   |    5 +-
 src/notifiers/models/notification_manager.rs  |   44 +
 src/notifiers/models/notifier.rs              |   10 +
 src/notifiers/notifier.rs                     |    6 -
 src/protocol/core/mod.rs                      |    2 +-
 src/protocol/core/routes.rs                   |   52 +-
 src/protocol/creation/routes.rs               |   51 +-
 src/protocol/extensions.rs                    |   21 +
 src/protocol/mod.rs                           |    8 +-
 src/protocol/termination/routes.rs            |   22 +-
 src/storages/file_storage.rs                  |   49 +-
 src/storages/models/available_stores.rs       |   24 +-
 src/storages/models/storage.rs                |    6 +-
 src/utils/enums.rs                            |   28 +
 src/utils/mod.rs                              |    1 +
 30 files changed, 1285 insertions(+), 1038 deletions(-)
 create mode 100644 .pre-commit-config.yaml
 create mode 100644 imgs/rustus_startup_logo.txt
 create mode 100644 src/notifiers/http_notifier.rs
 create mode 100644 src/notifiers/models/hooks.rs
 delete mode 100644 src/notifiers/models/message.rs
 create mode 100644 src/notifiers/models/message_format.rs
 create mode 100644 src/notifiers/models/notification_manager.rs
 create mode 100644 src/notifiers/models/notifier.rs
 delete mode 100644 src/notifiers/notifier.rs
 create mode 100644 src/protocol/extensions.rs
 create mode 100644 src/utils/enums.rs

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..4a87a93
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,45 @@
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v2.1.0
+    hooks:
+      - id: trailing-whitespace
+  - repo: local
+    hooks:
+
+      - id: build
+        types:
+          - rust
+        name: cargo build
+        language: system
+        entry: cargo
+        pass_filenames: false
+        args:
+          - build
+          - --features=all
+
+      - id: fmt
+        types:
+          - rust
+        name: cargo fmt
+        language: system
+        entry: cargo
+        pass_filenames: false
+        args:
+          - fmt
+
+      - id: clippy
+        types:
+          - rust
+        name: cargo clippy
+        language: system
+        pass_filenames: false
+        entry: cargo
+        args:
+          - clippy
+          - --features=all
+          - --all
+          - --
+          - -W
+          - clippy::all
+          - -W
+          - clippy::pedantic
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 7f8b165..3fe75eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,111 +4,87 @@ version = 3
 
 [[package]]
 name = "actix-codec"
-version = "0.3.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570"
+checksum = "13895df506faee81e423febbae3a33b27fca71831b96bb3d60adf16ebcfea952"
 dependencies = [
  "bitflags",
- "bytes 0.5.6",
+ "bytes",
  "futures-core",
  "futures-sink",
  "log",
- "pin-project 0.4.28",
- "tokio 0.2.25",
- "tokio-util 0.3.1",
-]
-
-[[package]]
-name = "actix-connect"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc"
-dependencies = [
- "actix-codec",
- "actix-rt",
- "actix-service",
- "actix-utils",
- "derive_more",
- "either",
- "futures-util",
- "http",
- "log",
- "trust-dns-proto",
- "trust-dns-resolver",
+ "memchr",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
 ]
 
 [[package]]
 name = "actix-files"
-version = "0.5.0"
+version = "0.6.0-beta.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c51e8a9146c12fce92a6e4c24b8c4d9b05268130bfd8d61bc587e822c32ce689"
+checksum = "84080179cb878e592ebba0e5194a0eb890dfb37b4f956710e51db6d4d8e18abb"
 dependencies = [
+ "actix-http",
  "actix-service",
+ "actix-utils",
  "actix-web",
+ "askama_escape",
  "bitflags",
- "bytes 0.5.6",
+ "bytes",
  "derive_more",
  "futures-core",
- "futures-util",
+ "http-range",
  "log",
  "mime",
  "mime_guess",
  "percent-encoding",
- "v_htmlescape",
+ "pin-project-lite",
 ]
 
 [[package]]
 name = "actix-http"
-version = "2.2.1"
+version = "3.0.0-beta.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cb8958da437716f3f31b0e76f8daf36554128517d7df37ceba7df00f09622ee"
+checksum = "6294c508c1413346857838356f53f45dbfd257ea31dca19470d9ce78750a7d37"
 dependencies = [
  "actix-codec",
- "actix-connect",
  "actix-rt",
  "actix-service",
- "actix-threadpool",
  "actix-utils",
+ "ahash",
  "base64",
  "bitflags",
  "brotli2",
- "bytes 0.5.6",
- "cookie",
- "copyless",
+ "bytes",
+ "bytestring",
  "derive_more",
- "either",
  "encoding_rs",
  "flate2",
- "futures-channel",
  "futures-core",
- "futures-util",
- "fxhash",
+ "futures-task",
  "h2",
  "http",
  "httparse",
- "indexmap",
+ "httpdate",
  "itoa 0.4.8",
  "language-tags",
- "lazy_static",
+ "local-channel",
  "log",
  "mime",
  "percent-encoding",
- "pin-project 1.0.8",
- "rand 0.7.3",
- "regex",
- "serde",
- "serde_json",
- "serde_urlencoded",
+ "pin-project-lite",
+ "rand",
  "sha-1 0.9.8",
- "slab",
- "time 0.2.27",
+ "smallvec",
+ "zstd",
 ]
 
 [[package]]
 name = "actix-macros"
-version = "0.1.3"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655"
+checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
 dependencies = [
  "quote",
  "syn",
@@ -116,11 +92,12 @@ dependencies = [
 
 [[package]]
 name = "actix-router"
-version = "0.2.7"
+version = "0.5.0-beta.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c"
+checksum = "ddd9f117b910fbcce6e9f45092ffd4ff017785a346d09e2d4fd049f4e20384f4"
 dependencies = [
  "bytestring",
+ "firestorm",
  "http",
  "log",
  "regex",
@@ -129,115 +106,59 @@ dependencies = [
 
 [[package]]
 name = "actix-rt"
-version = "1.1.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227"
+checksum = "05c2f80ce8d0c990941c7a7a931f69fd0701b76d521f8d36298edf59cd3fbf1f"
 dependencies = [
  "actix-macros",
- "actix-threadpool",
- "copyless",
- "futures-channel",
- "futures-util",
- "smallvec",
- "tokio 0.2.25",
+ "futures-core",
+ "tokio",
 ]
 
 [[package]]
 name = "actix-server"
-version = "1.0.4"
+version = "2.0.0-rc.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e"
+checksum = "78c9b22794b8af1c2e02434873ef858f2a7db40dbbf861ce77a04cd81ac6b767"
 dependencies = [
- "actix-codec",
  "actix-rt",
  "actix-service",
  "actix-utils",
- "futures-channel",
+ "futures-core",
  "futures-util",
  "log",
- "mio 0.6.23",
- "mio-uds",
+ "mio 0.8.0",
  "num_cpus",
- "slab",
- "socket2 0.3.19",
+ "socket2",
+ "tokio",
 ]
 
 [[package]]
 name = "actix-service"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb"
-dependencies = [
- "futures-util",
- "pin-project 0.4.28",
-]
-
-[[package]]
-name = "actix-testing"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c"
-dependencies = [
- "actix-macros",
- "actix-rt",
- "actix-server",
- "actix-service",
- "log",
- "socket2 0.3.19",
-]
-
-[[package]]
-name = "actix-threadpool"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30"
-dependencies = [
- "derive_more",
- "futures-channel",
- "lazy_static",
- "log",
- "num_cpus",
- "parking_lot",
- "threadpool",
-]
-
-[[package]]
-name = "actix-tls"
-version = "2.0.0"
+version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb"
+checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a"
 dependencies = [
- "actix-codec",
- "actix-service",
- "actix-utils",
- "futures-util",
+ "futures-core",
+ "paste",
+ "pin-project-lite",
 ]
 
 [[package]]
 name = "actix-utils"
-version = "2.0.0"
+version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a"
+checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94"
 dependencies = [
- "actix-codec",
- "actix-rt",
- "actix-service",
- "bitflags",
- "bytes 0.5.6",
- "either",
- "futures-channel",
- "futures-sink",
- "futures-util",
- "log",
- "pin-project 0.4.28",
- "slab",
+ "local-waker",
+ "pin-project-lite",
 ]
 
 [[package]]
 name = "actix-web"
-version = "3.3.2"
+version = "4.0.0-beta.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e641d4a172e7faa0862241a20ff4f1f5ab0ab7c279f00c2d4587b77483477b86"
+checksum = "4609cf57246040316642d4dc4c03d7f3d4a083a892122829dbd9e6ec8db7cd67"
 dependencies = [
  "actix-codec",
  "actix-http",
@@ -246,38 +167,40 @@ dependencies = [
  "actix-rt",
  "actix-server",
  "actix-service",
- "actix-testing",
- "actix-threadpool",
- "actix-tls",
  "actix-utils",
  "actix-web-codegen",
- "awc",
- "bytes 0.5.6",
+ "ahash",
+ "bytes",
+ "cfg-if",
+ "cookie",
  "derive_more",
  "encoding_rs",
- "futures-channel",
  "futures-core",
  "futures-util",
- "fxhash",
+ "itoa 0.4.8",
+ "language-tags",
  "log",
  "mime",
- "pin-project 1.0.8",
+ "once_cell",
+ "paste",
+ "pin-project-lite",
  "regex",
  "serde",
  "serde_json",
  "serde_urlencoded",
- "socket2 0.3.19",
- "time 0.2.27",
- "tinyvec",
+ "smallvec",
+ "socket2",
+ "time 0.3.5",
  "url",
 ]
 
 [[package]]
 name = "actix-web-codegen"
-version = "0.4.0"
+version = "0.5.0-beta.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb"
+checksum = "30a90b7f6c2fde9a1fe3df4da758c2c3c9d620dfa3eae4da0b6925dc0a13444a"
 dependencies = [
+ "actix-router",
  "proc-macro2",
  "quote",
  "syn",
@@ -295,9 +218,9 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 dependencies = [
- "getrandom 0.2.3",
+ "getrandom",
  "once_cell",
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -315,7 +238,7 @@ version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -324,6 +247,12 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
+[[package]]
+name = "askama_escape"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a1bb320f97e6edf9f756bf015900038e43c7700e059688e5724a928c8f3b8d5"
+
 [[package]]
 name = "async-channel"
 version = "1.6.1"
@@ -363,6 +292,7 @@ dependencies = [
  "futures-lite",
  "num_cpus",
  "once_cell",
+ "tokio",
 ]
 
 [[package]]
@@ -379,9 +309,9 @@ dependencies = [
  "parking",
  "polling",
  "slab",
- "socket2 0.4.2",
+ "socket2",
  "waker-fn",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -402,34 +332,6 @@ dependencies = [
  "event-listener",
 ]
 
-[[package]]
-name = "async-process"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6"
-dependencies = [
- "async-io",
- "blocking",
- "cfg-if 1.0.0",
- "event-listener",
- "futures-lite",
- "libc",
- "once_cell",
- "signal-hook",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "async-rustls"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378"
-dependencies = [
- "futures-lite",
- "rustls",
- "webpki",
-]
-
 [[package]]
 name = "async-std"
 version = "1.10.0"
@@ -440,7 +342,6 @@ dependencies = [
  "async-global-executor",
  "async-io",
  "async-lock",
- "async-process",
  "crossbeam-utils",
  "futures-channel",
  "futures-core",
@@ -452,7 +353,7 @@ dependencies = [
  "memchr",
  "num_cpus",
  "once_cell",
- "pin-project-lite 0.2.7",
+ "pin-project-lite",
  "pin-utils",
  "slab",
  "wasm-bindgen-futures",
@@ -498,7 +399,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
  "hermit-abi",
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -513,30 +414,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
-[[package]]
-name = "awc"
-version = "2.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691"
-dependencies = [
- "actix-codec",
- "actix-http",
- "actix-rt",
- "actix-service",
- "base64",
- "bytes 0.5.6",
- "cfg-if 1.0.0",
- "derive_more",
- "futures-core",
- "log",
- "mime",
- "percent-encoding",
- "rand 0.7.3",
- "serde",
- "serde_json",
- "serde_urlencoded",
-]
-
 [[package]]
 name = "base-x"
 version = "0.2.8"
@@ -640,15 +517,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "buf-min"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa17aa1cf56bdd6bb30518767d00e58019d326f3f05d8c3e0730b549d332ea83"
-dependencies = [
- "bytes 0.5.6",
-]
-
 [[package]]
 name = "bumpalo"
 version = "3.8.0"
@@ -667,12 +535,6 @@ version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
-[[package]]
-name = "bytes"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
-
 [[package]]
 name = "bytes"
 version = "1.1.0"
@@ -685,7 +547,7 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
 ]
 
 [[package]]
@@ -699,12 +561,9 @@ name = "cc"
 version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+dependencies = [
+ "jobserver",
+]
 
 [[package]]
 name = "cfg-if"
@@ -723,7 +582,7 @@ dependencies = [
  "num-traits",
  "serde",
  "time 0.1.44",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -741,18 +600,29 @@ dependencies = [
  "vec_map",
 ]
 
+[[package]]
+name = "colored"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
+dependencies = [
+ "atty",
+ "lazy_static",
+ "winapi",
+]
+
 [[package]]
 name = "combine"
 version = "4.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "futures-core",
  "memchr",
- "pin-project-lite 0.2.7",
- "tokio 1.15.0",
- "tokio-util 0.6.9",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
 ]
 
 [[package]]
@@ -778,20 +648,30 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
 
 [[package]]
 name = "cookie"
-version = "0.14.4"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951"
+checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d"
 dependencies = [
  "percent-encoding",
  "time 0.2.27",
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
-name = "copyless"
-version = "0.1.5"
+name = "core-foundation"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 
 [[package]]
 name = "cpufeatures"
@@ -823,7 +703,7 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
 ]
 
 [[package]]
@@ -832,7 +712,7 @@ version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crossbeam-utils",
 ]
 
@@ -842,7 +722,7 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crossbeam-utils",
 ]
 
@@ -852,7 +732,7 @@ version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "lazy_static",
 ]
 
@@ -882,7 +762,7 @@ version = "4.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "num_cpus",
 ]
 
@@ -934,7 +814,7 @@ checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 dependencies = [
  "libc",
  "redox_users",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -964,19 +844,7 @@ version = "0.8.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
 dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "enum-as-inner"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
+ "cfg-if",
 ]
 
 [[package]]
@@ -1000,13 +868,29 @@ dependencies = [
  "instant",
 ]
 
+[[package]]
+name = "fern"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065"
+dependencies = [
+ "colored",
+ "log",
+]
+
+[[package]]
+name = "firestorm"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499"
+
 [[package]]
 name = "flate2"
 version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "crc32fast",
  "libc",
  "miniz_oxide",
@@ -1019,36 +903,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
-name = "form_urlencoded"
-version = "1.0.1"
+name = "foreign-types"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 dependencies = [
- "matches",
- "percent-encoding",
+ "foreign-types-shared",
 ]
 
 [[package]]
-name = "fuchsia-zircon"
-version = "0.3.3"
+name = "foreign-types-shared"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
-dependencies = [
- "bitflags",
- "fuchsia-zircon-sys",
-]
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
 [[package]]
-name = "fuchsia-zircon-sys"
-version = "0.3.3"
+name = "form_urlencoded"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+dependencies = [
+ "matches",
+ "percent-encoding",
+]
 
 [[package]]
 name = "futures"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
+checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1061,9 +944,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
+checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1071,15 +954,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
+checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
+checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1099,9 +982,9 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
+checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
 
 [[package]]
 name = "futures-lite"
@@ -1114,18 +997,16 @@ dependencies = [
  "futures-io",
  "memchr",
  "parking",
- "pin-project-lite 0.2.7",
+ "pin-project-lite",
  "waker-fn",
 ]
 
 [[package]]
 name = "futures-macro"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
+checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
 dependencies = [
- "autocfg 1.0.1",
- "proc-macro-hack",
  "proc-macro2",
  "quote",
  "syn",
@@ -1133,15 +1014,15 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
+checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
 
 [[package]]
 name = "futures-task"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
+checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
 
 [[package]]
 name = "futures-timer"
@@ -1151,11 +1032,10 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
 
 [[package]]
 name = "futures-util"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
+checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
 dependencies = [
- "autocfg 1.0.1",
  "futures-channel",
  "futures-core",
  "futures-io",
@@ -1163,22 +1043,11 @@ dependencies = [
  "futures-sink",
  "futures-task",
  "memchr",
- "pin-project-lite 0.2.7",
+ "pin-project-lite",
  "pin-utils",
- "proc-macro-hack",
- "proc-macro-nested",
  "slab",
 ]
 
-[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
 [[package]]
 name = "generic-array"
 version = "0.12.4"
@@ -1195,18 +1064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
 dependencies = [
  "typenum",
- "version_check 0.9.3",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
+ "version_check",
 ]
 
 [[package]]
@@ -1215,9 +1073,9 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
+ "wasi",
 ]
 
 [[package]]
@@ -1235,11 +1093,11 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.2.7"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
+checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd"
 dependencies = [
- "bytes 0.5.6",
+ "bytes",
  "fnv",
  "futures-core",
  "futures-sink",
@@ -1247,10 +1105,9 @@ dependencies = [
  "http",
  "indexmap",
  "slab",
- "tokio 0.2.25",
- "tokio-util 0.3.1",
+ "tokio",
+ "tokio-util",
  "tracing",
- "tracing-futures",
 ]
 
 [[package]]
@@ -1305,17 +1162,6 @@ dependencies = [
  "digest 0.9.0",
 ]
 
-[[package]]
-name = "hostname"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
-dependencies = [
- "libc",
- "match_cfg",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "html_parser"
 version = "0.6.2"
@@ -1337,17 +1183,77 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "fnv",
  "itoa 0.4.8",
 ]
 
+[[package]]
+name = "http-body"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-range"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eee9694f83d9b7c09682fdb32213682939507884e5bcf227be9aff5d644b90dc"
+
 [[package]]
 name = "httparse"
 version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
 
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "hyper"
+version = "0.14.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa 0.4.8",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
+dependencies = [
+ "bytes",
+ "hyper",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+]
+
 [[package]]
 name = "idna"
 version = "0.2.3"
@@ -1375,29 +1281,14 @@ version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "iovec"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
-dependencies = [
- "libc",
+ "cfg-if",
 ]
 
 [[package]]
-name = "ipconfig"
-version = "0.2.2"
+name = "ipnet"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
-dependencies = [
- "socket2 0.3.19",
- "widestring",
- "winapi 0.3.9",
- "winreg",
-]
+checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
 
 [[package]]
 name = "ipnetwork"
@@ -1430,22 +1321,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
 
 [[package]]
-name = "js-sys"
-version = "0.3.55"
+name = "jobserver"
+version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
+checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
 dependencies = [
- "wasm-bindgen",
+ "libc",
 ]
 
 [[package]]
-name = "kernel32-sys"
-version = "0.2.2"
+name = "js-sys"
+version = "0.3.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
 dependencies = [
- "winapi 0.2.8",
- "winapi-build",
+ "wasm-bindgen",
 ]
 
 [[package]]
@@ -1459,9 +1349,9 @@ dependencies = [
 
 [[package]]
 name = "language-tags"
-version = "0.2.2"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
+checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
 
 [[package]]
 name = "lazy_static"
@@ -1496,10 +1386,22 @@ dependencies = [
 ]
 
 [[package]]
-name = "linked-hash-map"
-version = "0.5.4"
+name = "local-channel"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "local-waker",
+]
+
+[[package]]
+name = "local-waker"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "902eb695eb0591864543cbfbf6d742510642a605a61fc5e97fe6ceb5a30ac4fb"
 
 [[package]]
 name = "lock_api"
@@ -1516,31 +1418,16 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "value-bag",
 ]
 
-[[package]]
-name = "lru-cache"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
-dependencies = [
- "linked-hash-map",
-]
-
 [[package]]
 name = "maplit"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 
-[[package]]
-name = "match_cfg"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
-
 [[package]]
 name = "matches"
 version = "0.1.9"
@@ -1596,25 +1483,6 @@ dependencies = [
  "autocfg 1.0.1",
 ]
 
-[[package]]
-name = "mio"
-version = "0.6.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-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 = "mio"
 version = "0.7.14"
@@ -1623,32 +1491,22 @@ checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
 dependencies = [
  "libc",
  "log",
- "miow 0.3.7",
+ "miow",
  "ntapi",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
-name = "mio-uds"
-version = "0.6.8"
+name = "mio"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
+checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
 dependencies = [
- "iovec",
  "libc",
- "mio 0.6.23",
-]
-
-[[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",
+ "log",
+ "miow",
+ "ntapi",
+ "winapi",
 ]
 
 [[package]]
@@ -1657,7 +1515,7 @@ version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -1666,14 +1524,13 @@ version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f76d2f2e2dcbb00a8d3b2b09f026a74a82693ea52cd071647aa6cfa7f1ff37e"
 dependencies = [
- "async-std",
  "async-trait",
  "futures-channel",
  "futures-core",
  "futures-timer",
  "futures-util",
  "log",
- "tokio 1.15.0",
+ "tokio",
 ]
 
 [[package]]
@@ -1687,24 +1544,21 @@ dependencies = [
 ]
 
 [[package]]
-name = "net2"
-version = "0.2.37"
+name = "native-tls"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
 dependencies = [
- "cfg-if 0.1.10",
+ "lazy_static",
  "libc",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "nom"
-version = "4.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
-dependencies = [
- "memchr",
- "version_check 0.1.5",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
 ]
 
 [[package]]
@@ -1715,7 +1569,7 @@ checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
 dependencies = [
  "memchr",
  "minimal-lexical",
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -1724,7 +1578,7 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -1762,7 +1616,7 @@ dependencies = [
  "num-integer",
  "num-iter",
  "num-traits",
- "rand 0.8.4",
+ "rand",
  "smallvec",
  "zeroize",
 ]
@@ -1826,6 +1680,39 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
+[[package]]
+name = "openssl"
+version = "0.10.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
+dependencies = [
+ "autocfg 1.0.1",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "parking"
 version = "2.0.0"
@@ -1849,14 +1736,20 @@ version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "instant",
  "libc",
- "redox_syscall 0.2.10",
+ "redox_syscall",
  "smallvec",
- "winapi 0.3.9",
+ "winapi",
 ]
 
+[[package]]
+name = "paste"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
+
 [[package]]
 name = "pem"
 version = "0.8.3"
@@ -1917,52 +1810,6 @@ dependencies = [
  "sha-1 0.8.2",
 ]
 
-[[package]]
-name = "pin-project"
-version = "0.4.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f"
-dependencies = [
- "pin-project-internal 0.4.28",
-]
-
-[[package]]
-name = "pin-project"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08"
-dependencies = [
- "pin-project-internal 1.0.8",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "0.4.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777"
-
 [[package]]
 name = "pin-project-lite"
 version = "0.2.7"
@@ -1987,11 +1834,11 @@ version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "log",
  "wepoll-ffi",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2010,7 +1857,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "syn",
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -2021,7 +1868,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 dependencies = [
  "proc-macro2",
  "quote",
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -2030,12 +1877,6 @@ version = "0.5.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 
-[[package]]
-name = "proc-macro-nested"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
-
 [[package]]
 name = "proc-macro2"
 version = "1.0.34"
@@ -2057,32 +1898,13 @@ dependencies = [
  "serde_json",
 ]
 
-[[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
 [[package]]
 name = "quote"
 version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
 dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "rand"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
-dependencies = [
- "getrandom 0.1.16",
- "libc",
- "rand_chacha 0.2.2",
- "rand_core 0.5.1",
- "rand_hc 0.2.0",
+ "proc-macro2",
 ]
 
 [[package]]
@@ -2092,19 +1914,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
 dependencies = [
  "libc",
- "rand_chacha 0.3.1",
- "rand_core 0.6.3",
- "rand_hc 0.3.1",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.5.1",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
 ]
 
 [[package]]
@@ -2114,16 +1926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.3",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-dependencies = [
- "getrandom 0.1.16",
+ "rand_core",
 ]
 
 [[package]]
@@ -2132,16 +1935,7 @@ version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
 dependencies = [
- "getrandom 0.2.3",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
-dependencies = [
- "rand_core 0.5.1",
+ "getrandom",
 ]
 
 [[package]]
@@ -2150,7 +1944,7 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
 dependencies = [
- "rand_core 0.6.3",
+ "rand_core",
 ]
 
 [[package]]
@@ -2167,7 +1961,7 @@ dependencies = [
  "lazy_static",
  "log",
  "once_cell",
- "rand 0.8.4",
+ "rand",
  "rbatis-core",
  "rbatis-macro-driver",
  "rbatis_sql",
@@ -2254,7 +2048,7 @@ dependencies = [
  "hex",
  "indexmap",
  "lazy_static",
- "rand 0.8.4",
+ "rand",
  "serde",
  "serde_bytes",
  "serde_json",
@@ -2267,27 +2061,20 @@ version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1a6ddfecac9391fed21cce10e83c65fa4abafd77df05c98b1c647c65374ce9b3"
 dependencies = [
- "async-std",
  "async-trait",
- "bytes 1.1.0",
+ "bytes",
  "combine",
  "dtoa",
  "futures-util",
  "itoa 0.4.8",
  "percent-encoding",
- "pin-project-lite 0.2.7",
+ "pin-project-lite",
  "sha1",
- "tokio 1.15.0",
- "tokio-util 0.6.9",
+ "tokio",
+ "tokio-util",
  "url",
 ]
 
-[[package]]
-name = "redox_syscall"
-version = "0.1.57"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
-
 [[package]]
 name = "redox_syscall"
 version = "0.2.10"
@@ -2303,8 +2090,8 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 dependencies = [
- "getrandom 0.2.3",
- "redox_syscall 0.2.10",
+ "getrandom",
+ "redox_syscall",
 ]
 
 [[package]]
@@ -2325,13 +2112,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
-name = "resolv-conf"
-version = "0.7.0"
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.11.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
+checksum = "7c4e0a76dc12a116108933f6301b95e83634e0c47b0afbed6abbaa0601e99258"
 dependencies = [
- "hostname",
- "quick-error",
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-tls",
+ "ipnet",
+ "js-sys",
+ "lazy_static",
+ "log",
+ "mime",
+ "native-tls",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "tokio",
+ "tokio-native-tls",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
 ]
 
 [[package]]
@@ -2357,7 +2178,7 @@ dependencies = [
  "spin",
  "untrusted",
  "web-sys",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2374,7 +2195,7 @@ dependencies = [
  "num-iter",
  "num-traits",
  "pem",
- "rand 0.8.4",
+ "rand",
  "simple_asn1",
  "subtle",
  "zeroize",
@@ -2433,18 +2254,21 @@ dependencies = [
  "base64",
  "chrono",
  "derive_more",
+ "fern",
+ "futures",
  "lazy_static",
  "log",
  "mobc-redis",
  "rbatis",
  "rbson",
+ "reqwest",
  "serde",
  "serde_json",
- "simple-logging",
  "strfmt",
  "structopt",
  "strum",
  "thiserror",
+ "tokio",
  "url",
  "uuid 1.0.0-alpha.1",
 ]
@@ -2461,6 +2285,16 @@ version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
 
+[[package]]
+name = "schannel"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
+dependencies = [
+ "lazy_static",
+ "winapi",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.1.0"
@@ -2477,6 +2311,29 @@ dependencies = [
  "untrusted",
 ]
 
+[[package]]
+name = "security-framework"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "semver"
 version = "0.9.0"
@@ -2570,7 +2427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
 dependencies = [
  "block-buffer 0.9.0",
- "cfg-if 1.0.0",
+ "cfg-if",
  "cpufeatures",
  "digest 0.9.0",
  "opaque-debug 0.3.0",
@@ -2589,22 +2446,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
 dependencies = [
  "block-buffer 0.9.0",
- "cfg-if 1.0.0",
+ "cfg-if",
  "cpufeatures",
  "digest 0.9.0",
  "opaque-debug 0.3.0",
 ]
 
-[[package]]
-name = "signal-hook"
-version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
-dependencies = [
- "libc",
- "signal-hook-registry",
-]
-
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.0"
@@ -2614,17 +2461,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "simple-logging"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542"
-dependencies = [
- "lazy_static",
- "log",
- "thread-id",
-]
-
 [[package]]
 name = "simple_asn1"
 version = "0.5.4"
@@ -2649,17 +2485,6 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
 
-[[package]]
-name = "socket2"
-version = "0.3.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "socket2"
 version = "0.4.2"
@@ -2667,7 +2492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2683,7 +2508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
 dependencies = [
  "itertools",
- "nom 7.1.0",
+ "nom",
  "unicode_categories",
 ]
 
@@ -2700,7 +2525,7 @@ dependencies = [
  "bit-vec",
  "bitflags",
  "byteorder",
- "bytes 1.1.0",
+ "bytes",
  "chrono",
  "crc",
  "crossbeam-channel",
@@ -2729,7 +2554,7 @@ dependencies = [
  "once_cell",
  "parking_lot",
  "percent-encoding",
- "rand 0.8.4",
+ "rand",
  "regex",
  "rsa",
  "rust_decimal",
@@ -2744,6 +2569,7 @@ dependencies = [
  "stringprep",
  "thiserror",
  "time 0.2.27",
+ "tokio-stream",
  "url",
  "uuid 0.8.2",
  "webpki",
@@ -2757,8 +2583,10 @@ version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0d1bd069de53442e7a320f525a6d4deb8bb0621ac7a55f7eccbc2b58b57f43d0"
 dependencies = [
- "async-rustls",
- "async-std",
+ "actix-rt",
+ "once_cell",
+ "tokio",
+ "tokio-rustls",
 ]
 
 [[package]]
@@ -2767,7 +2595,7 @@ version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
 dependencies = [
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -2916,6 +2744,20 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
 [[package]]
 name = "textwrap"
 version = "0.11.0"
@@ -2945,26 +2787,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "thread-id"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
-dependencies = [
- "libc",
- "redox_syscall 0.1.57",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "threadpool"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
-dependencies = [
- "num_cpus",
-]
-
 [[package]]
 name = "time"
 version = "0.1.44"
@@ -2972,8 +2794,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
 dependencies = [
  "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi 0.3.9",
+ "wasi",
+ "winapi",
 ]
 
 [[package]]
@@ -2988,8 +2810,18 @@ dependencies = [
  "standback",
  "stdweb",
  "time-macros",
- "version_check 0.9.3",
- "winapi 0.3.9",
+ "version_check",
+ "winapi",
+]
+
+[[package]]
+name = "time"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
+dependencies = [
+ "itoa 0.4.8",
+ "libc",
 ]
 
 [[package]]
@@ -3032,51 +2864,52 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "0.2.25"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
+checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
 dependencies = [
- "bytes 0.5.6",
- "futures-core",
- "iovec",
- "lazy_static",
+ "bytes",
  "libc",
  "memchr",
- "mio 0.6.23",
- "mio-uds",
- "pin-project-lite 0.1.12",
+ "mio 0.7.14",
+ "num_cpus",
+ "once_cell",
+ "parking_lot",
+ "pin-project-lite",
  "signal-hook-registry",
- "slab",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
-name = "tokio"
-version = "1.15.0"
+name = "tokio-native-tls"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
+checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
 dependencies = [
- "bytes 1.1.0",
- "libc",
- "memchr",
- "mio 0.7.14",
- "num_cpus",
- "pin-project-lite 0.2.7",
- "winapi 0.3.9",
+ "native-tls",
+ "tokio",
 ]
 
 [[package]]
-name = "tokio-util"
-version = "0.3.1"
+name = "tokio-rustls"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
+dependencies = [
+ "rustls",
+ "tokio",
+ "webpki",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
+checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
 dependencies = [
- "bytes 0.5.6",
  "futures-core",
- "futures-sink",
- "log",
- "pin-project-lite 0.1.12",
- "tokio 0.2.25",
+ "pin-project-lite",
+ "tokio",
 ]
 
 [[package]]
@@ -3085,23 +2918,28 @@ version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "futures-core",
  "futures-sink",
  "log",
- "pin-project-lite 0.2.7",
- "tokio 1.15.0",
+ "pin-project-lite",
+ "tokio",
 ]
 
+[[package]]
+name = "tower-service"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
+
 [[package]]
 name = "tracing"
 version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
 dependencies = [
- "cfg-if 1.0.0",
- "log",
- "pin-project-lite 0.2.7",
+ "cfg-if",
+ "pin-project-lite",
  "tracing-core",
 ]
 
@@ -3115,53 +2953,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "tracing-futures"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
-dependencies = [
- "pin-project 1.0.8",
- "tracing",
-]
-
-[[package]]
-name = "trust-dns-proto"
-version = "0.19.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9"
-dependencies = [
- "async-trait",
- "cfg-if 1.0.0",
- "enum-as-inner",
- "futures",
- "idna",
- "lazy_static",
- "log",
- "rand 0.7.3",
- "smallvec",
- "thiserror",
- "tokio 0.2.25",
- "url",
-]
-
-[[package]]
-name = "trust-dns-resolver"
-version = "0.19.7"
+name = "try-lock"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb"
-dependencies = [
- "cfg-if 0.1.10",
- "futures",
- "ipconfig",
- "lazy_static",
- "log",
- "lru-cache",
- "resolv-conf",
- "smallvec",
- "thiserror",
- "tokio 0.2.25",
- "trust-dns-proto",
-]
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
 [[package]]
 name = "typenum"
@@ -3181,7 +2976,7 @@ version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
 dependencies = [
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -3247,7 +3042,7 @@ version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
 dependencies = [
- "getrandom 0.2.3",
+ "getrandom",
  "serde",
 ]
 
@@ -3257,39 +3052,7 @@ version = "1.0.0-alpha.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bb3ab47baa004111b323696c6eaa2752e7356f7f77cf6b6dc7a2087368ce1ca4"
 dependencies = [
- "getrandom 0.2.3",
-]
-
-[[package]]
-name = "v_escape"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3e0ab5fab1db278a9413d2ea794cb66f471f898c5b020c3c394f6447625d9d4"
-dependencies = [
- "buf-min",
- "v_escape_derive",
-]
-
-[[package]]
-name = "v_escape_derive"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29769400af8b264944b851c961a4a6930e76604f59b1fcd51246bab6a296c8c"
-dependencies = [
- "nom 4.2.3",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "v_htmlescape"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f9a8af610ad6f7fc9989c9d2590d9764bc61f294884e9ee93baa58795174572"
-dependencies = [
- "cfg-if 1.0.0",
- "v_escape",
+ "getrandom",
 ]
 
 [[package]]
@@ -3299,7 +3062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
 dependencies = [
  "ctor",
- "version_check 0.9.3",
+ "version_check",
 ]
 
 [[package]]
@@ -3314,12 +3077,6 @@ version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 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.3"
@@ -3333,10 +3090,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
 
 [[package]]
-name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
+name = "want"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
 
 [[package]]
 name = "wasi"
@@ -3350,7 +3111,7 @@ version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "wasm-bindgen-macro",
 ]
 
@@ -3375,7 +3136,7 @@ version = "0.4.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "js-sys",
  "wasm-bindgen",
  "web-sys",
@@ -3458,18 +3219,6 @@ dependencies = [
  "web-sys",
 ]
 
-[[package]]
-name = "widestring"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
-
-[[package]]
-name = "winapi"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -3480,12 +3229,6 @@ dependencies = [
  "winapi-x86_64-pc-windows-gnu",
 ]
 
-[[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-
 [[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
@@ -3500,21 +3243,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "winreg"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
-dependencies = [
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "ws2_32-sys"
-version = "0.2.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
 dependencies = [
- "winapi 0.2.8",
- "winapi-build",
+ "winapi",
 ]
 
 [[package]]
@@ -3543,3 +3276,32 @@ dependencies = [
  "syn",
  "synstructure",
 ]
+
+[[package]]
+name = "zstd"
+version = "0.9.0+zstd.1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07749a5dc2cb6b36661290245e350f15ec3bbb304e493db54a1d354480522ccd"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "4.1.1+zstd.1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c91c90f2c593b003603e5e0493c837088df4469da25aafff8bce42ba48caf079"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "1.6.1+zstd.1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "615120c7a2431d16cf1cf979e7fc31ba7a5b5e5707b29c8a99e5dbf8a8392a33"
+dependencies = [
+ "cc",
+ "libc",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 3528b0d..1ea4fde 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,31 +9,36 @@ description = "TUS protocol implementation written in Rust."
 name = "rustus"
 
 [dependencies]
-actix-web = "^3.3.2"
-actix-files = "^0.5.0"
+actix-web = { version = "^4.0.0-beta.15" }
+actix-files = { version = "0.6.0-beta.10" }
+tokio = { version = "1.4.0", features = ["time"] }
 derive_more = { version = "^0.99.9", default-features = false, features = ["display", "from", "from_str"] }
 thiserror = "^1.0"
 async-trait = "0.1.52"
 structopt = { version = "^0.3" }
 uuid = { version = "^1.0.0-alpha.1", features = ["v4"] }
-async-std = "^1.10.0"
+async-std = { version = "^1.10.0", features = ["tokio1"] }
 serde = "1"
 chrono = { version = "^0.4.19", features = ["serde"] }
 serde_json = "1"
 log = "^0.4.14"
 url = "2.2.2"
 base64 = "^0.13.0"
-simple-logging = { version = "^2.0.2" }
+fern = { version = "0.6.0", features = ["colored"] }
 strfmt = "^0.1.6"
 lazy_static = "1.4.0"
 strum = { version = "0.23", features = ["derive"] }
+futures = { version = "^0.3.19", features = ["executor"] }
+reqwest = { version = "^0.11.8", features = ["json"], optional = true }
+# Deps for info storages.
 rbson = { version = "2.0", optional = true }
-rbatis = { version = "^3.0", default-features = false, features = ["runtime-async-std-rustls", "all-database"], optional = true }
-mobc-redis = { version = "0.7.0", features = ["async-std-comp"], optional = true }
-
+rbatis = { version = "^3.0", default-features = false, features = ["runtime-actix-rustls", "all-database"], optional = true }
+mobc-redis = { version = "^0.7.0", optional = true }
 
 [features]
 default = []
-all = ["redis_info_storage", "db_info_storage"]
+all = ["redis_info_storage", "db_info_storage", "http_notifier"]
 redis_info_storage = ["mobc-redis"]
-db_info_storage = ["rbatis", "rbson"]
\ No newline at end of file
+db_info_storage = ["rbatis", "rbson"]
+
+http_notifier = ["reqwest"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 9539832..c4c0003 100644
--- a/README.md
+++ b/README.md
@@ -69,9 +69,9 @@ All options are listed in `rustus --help`.
 * [x] Database support for info storage;
 * [x] Redis support for info storage;
 * [ ] S3 as data storage store support;
-* [ ] Notification interface;
+* [x] Notification interface;
 * [ ] Executable files notifications;
-* [ ] Notifications via http hooks;
+* [x] Notifications via http hooks;
 * [ ] Notifications via RabbitMQ;
 * [ ] Rustus helm chart;
 * [ ] Cloud native rustus operator.
diff --git a/imgs/rustus_startup_logo.txt b/imgs/rustus_startup_logo.txt
new file mode 100644
index 0000000..381d6f4
--- /dev/null
+++ b/imgs/rustus_startup_logo.txt
@@ -0,0 +1,7 @@
+     ____             __
+    / __ \__  _______/ /___  _______
+   / /_/ / / / / ___/ __/ / / / ___/
+  / _, _/ /_/ (__  ) /_/ /_/ (__  )
+ /_/ |_|\__,_/____/\__/\__,_/____/
+    Makes file uploads easier.
+
diff --git a/src/config.rs b/src/config.rs
index 2a0a73d..8a1a6f8 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -3,19 +3,33 @@ use std::env;
 use std::path::PathBuf;
 
 use chrono::{Datelike, Timelike};
+use lazy_static::lazy_static;
 use structopt::StructOpt;
 
-use crate::errors::RustusError;
 use crate::info_storages::AvailableInfoStores;
+use crate::notifiers::{Format, Hook};
+use crate::protocol::extensions::Extensions;
+
 use crate::storages::AvailableStores;
 
+lazy_static! {
+    /// Freezing ENVS on startup.
+    static ref ENV_MAP: HashMap<String, String> = {
+        let mut m = HashMap::new();
+        for (key, value) in env::vars() {
+            m.insert(format!("env[{}]", key), value);
+        }
+        m
+    };
+}
+
 #[derive(StructOpt, Debug, Clone)]
 pub struct StorageOptions {
     /// Rustus storage type.
     ///
     /// Storages are used to store
     /// uploads.
-    #[structopt(long, short, default_value = "file_storage", env = "RUSTUS_STORAGE")]
+    #[structopt(long, short, default_value = "file-storage", env = "RUSTUS_STORAGE")]
     pub storage: AvailableStores,
 
     /// Rustus data directory
@@ -25,7 +39,7 @@ pub struct StorageOptions {
     #[structopt(long, default_value = "./data")]
     pub data_dir: PathBuf,
 
-    #[structopt(long, short = "dstruct", default_value = "")]
+    #[structopt(long, default_value = "")]
     pub dis_structure: String,
 }
 
@@ -42,7 +56,7 @@ pub struct InfoStoreOptions {
     #[structopt(
         long,
         short,
-        default_value = "file_info_storage",
+        default_value = "file-info-storage",
         env = "RUSTUS_INFO_STORAGE"
     )]
     pub info_storage: AvailableInfoStores,
@@ -54,18 +68,51 @@ pub struct InfoStoreOptions {
     #[structopt(long, default_value = "./data", env = "RUSTUS_INFO_DIR")]
     pub info_dir: PathBuf,
 
+    /// Connection string for remote info storages.
+    ///
+    /// This connection string is used for storages
+    /// which require connection. Examples of such storages
+    /// are `Postgres`, `MySQL` or `Redis`.
+    ///
+    /// Value must include all connection details.
     #[cfg(any(feature = "redis_info_storage", feature = "db_info_storage"))]
     #[structopt(
         long,
-        required_if("info-storage", "db_info_storage"),
-        required_if("info-storage", "redis_info_storage"),
+        required_if("info-storage", "db-info-storage"),
+        required_if("info-storage", "redis-info-storage"),
         env = "RUSTUS_INFO_DB_DSN"
     )]
     pub info_db_dsn: Option<String>,
 }
 
+#[derive(StructOpt, Debug, Clone)]
+pub struct NotificationsOptions {
+    /// Notifications format.
+    ///
+    /// This format will be used in all
+    /// messages about hooks.
+    #[structopt(long, default_value = "default", env = "RUSTUS_NOTIFICATION_FORMAT")]
+    pub notification_format: Format,
+
+    /// Enabled hooks for notifications.
+    #[structopt(
+        long,
+        default_value = "pre-create,post-create,post-receive,post-terminate,post-finish",
+        env = "RUSTUS_HOOKS",
+        use_delimiter = true
+    )]
+    pub hooks: Vec<Hook>,
+
+    /// List of URLS to send webhooks to.
+    ///
+    /// This list will be notified
+    #[cfg(feature = "http_notifier")]
+    #[structopt(long, env = "RUSTUS_HOOKS_HTTP_URLS", use_delimiter = true)]
+    pub hooks_http_urls: Vec<String>,
+}
+
 #[derive(Debug, StructOpt, Clone)]
-#[structopt(name = "rustus")]
+#[structopt(name = "Rustus")]
 /// Tus protocol implementation.
 ///
 /// This program is a web-server that
@@ -74,7 +121,7 @@ pub struct InfoStoreOptions {
 /// You can read more about protocol
 /// [here](https://tus.io/).
 pub struct RustusConf {
-    /// Rustus host
+    /// Rustus server host
     #[structopt(short, long, default_value = "0.0.0.0", env = "RUSTUS_HOST")]
     pub host: String,
 
@@ -86,6 +133,9 @@ pub struct RustusConf {
     #[structopt(long, default_value = "/files", env = "RUSTUS_URL")]
     pub url: String,
 
+    /// Maximum payload size.
+    ///
+    /// This limit used to reduce amount of consumed memory.
     #[structopt(
         long,
         short = "mbs",
@@ -94,10 +144,6 @@ pub struct RustusConf {
     )]
     pub max_body_size: usize,
 
-    /// Enabled hooks for http events
-    #[structopt(long, default_value = "pre-create,post-finish", env = "RUSTUS_HOOKS")]
-    pub enabled_hooks: String,
-
     /// Rustus maximum log level
     #[structopt(long, default_value = "INFO", env = "RUSTUS_LOG_LEVEL")]
     pub log_level: log::LevelFilter,
@@ -110,57 +156,19 @@ pub struct RustusConf {
     #[structopt(
         long,
         default_value = "getting,creation,termination,creation-with-upload,creation-defer-length",
-        env = "RUSTUS_EXTENSIONS"
+        env = "RUSTUS_TUS_EXTENSIONS",
+        use_delimiter = true
     )]
-    pub extensions: String,
+    pub tus_extensions: Vec<Extensions>,
 
     #[structopt(flatten)]
     pub storage_opts: StorageOptions,
 
     #[structopt(flatten)]
     pub info_storage_opts: InfoStoreOptions,
-}
-
-/// Enum of available Protocol Extensions
-#[derive(PartialEq, PartialOrd, Ord, Eq)]
-pub enum ProtocolExtensions {
-    CreationDeferLength,
-    CreationWithUpload,
-    Creation,
-    Termination,
-    Getting,
-}
-
-impl TryFrom<String> for ProtocolExtensions {
-    type Error = RustusError;
 
-    /// Parse string to protocol extension.
-    ///
-    /// This function raises an error if unknown protocol was passed.
-    fn try_from(value: String) -> Result<Self, Self::Error> {
-        match value.as_str() {
-            "creation" => Ok(ProtocolExtensions::Creation),
-            "creation-with-upload" => Ok(ProtocolExtensions::CreationWithUpload),
-            "creation-defer-length" => Ok(ProtocolExtensions::CreationDeferLength),
-            "termination" => Ok(ProtocolExtensions::Termination),
-            "getting" => Ok(ProtocolExtensions::Getting),
-            _ => Err(RustusError::UnknownExtension(value.clone())),
-        }
-    }
-}
-
-impl From<ProtocolExtensions> for String {
-    /// Mapping protocol extensions to their
-    /// original names.
-    fn from(ext: ProtocolExtensions) -> Self {
-        match ext {
-            ProtocolExtensions::Creation => "creation".into(),
-            ProtocolExtensions::CreationWithUpload => "creation-with-upload".into(),
-            ProtocolExtensions::Termination => "termination".into(),
-            ProtocolExtensions::Getting => "getting".into(),
-            ProtocolExtensions::CreationDeferLength => "creation-defer-length".into(),
-        }
-    }
+    #[structopt(flatten)]
+    pub notification_opts: NotificationsOptions,
 }
 
 impl RustusConf {
@@ -193,17 +201,20 @@ impl RustusConf {
         )
     }
 
+    /// Check if hook is enabled by user.
+    pub fn hook_is_active(&self, hook: Hook) -> bool {
+        self.notification_opts.hooks.contains(&hook)
+    }
+
+    /// Generate directory name with user template.
     pub fn dir_struct(&self) -> String {
         let now = chrono::Utc::now();
-        let mut vars: HashMap<String, String> = HashMap::new();
+        let mut vars: HashMap<String, String> = ENV_MAP.clone();
         vars.insert("day".into(), now.day().to_string());
         vars.insert("month".into(), now.month().to_string());
         vars.insert("year".into(), now.year().to_string());
         vars.insert("hour".into(), now.hour().to_string());
         vars.insert("minute".into(), now.minute().to_string());
-        for (key, value) in env::vars() {
-            vars.insert(format!("env[{}]", key), value);
-        }
         strfmt::strfmt(self.storage_opts.dis_structure.as_str(), &vars)
             .unwrap_or_else(|_| "".into())
     }
@@ -216,27 +227,19 @@ impl RustusConf {
     /// Protocol extensions must be sorted,
     /// because Actix doesn't override
     /// existing methods.
-    pub fn extensions_vec(&self) -> Vec<ProtocolExtensions> {
-        let mut ext = self
-            .extensions
-            .split(',')
-            .flat_map(|ext| ProtocolExtensions::try_from(String::from(ext)))
-            .collect::<Vec<ProtocolExtensions>>();
+    pub fn extensions_vec(&self) -> Vec<Extensions> {
+        let mut ext = self.tus_extensions.clone();
 
         // If create-with-upload extension is enabled
         // creation extension must be enabled too.
-        if ext.contains(&ProtocolExtensions::CreationWithUpload)
-            && !ext.contains(&ProtocolExtensions::Creation)
-        {
-            ext.push(ProtocolExtensions::Creation);
+        if ext.contains(&Extensions::CreationWithUpload) && !ext.contains(&Extensions::Creation) {
+            ext.push(Extensions::Creation);
         }
 
         // If create-defer-length extension is enabled
         // creation extension must be enabled too.
-        if ext.contains(&ProtocolExtensions::CreationDeferLength)
-            && !ext.contains(&ProtocolExtensions::Creation)
-        {
-            ext.push(ProtocolExtensions::Creation);
+        if ext.contains(&Extensions::CreationDeferLength) && !ext.contains(&Extensions::Creation) {
+            ext.push(Extensions::Creation);
         }
 
         ext.sort();
diff --git a/src/errors.rs b/src/errors.rs
index 585adc0..9056373 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -1,8 +1,7 @@
 use std::io::{Error, ErrorKind};
 
-use actix_web::dev::HttpResponseBuilder;
 use actix_web::http::StatusCode;
-use actix_web::{HttpResponse, ResponseError};
+use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError};
 
 pub type RustusResult<T> = Result<T, RustusError>;
 
@@ -16,6 +15,8 @@ pub enum RustusError {
     WrongOffset,
     #[error("Unknown error")]
     Unknown,
+    #[error("File is frozen")]
+    FrozenFile,
     #[error("Unable to serialize object")]
     UnableToSerialize(#[from] serde_json::Error),
     #[cfg(feature = "db_info_storage")]
@@ -39,6 +40,13 @@ pub enum RustusError {
     UnableToPrepareStorage(String),
     #[error("Unknown extension: {0}")]
     UnknownExtension(String),
+    #[cfg(feature = "http_notifier")]
+    #[error("Http request failed: {0}")]
+    HttpRequestError(#[from] reqwest::Error),
+    #[error("Hook invocation failed. Reason: {0}")]
+    HookError(String),
+    #[error("Unable to configure logging: {0}")]
+    LogConfigError(#[from] log::SetLoggerError),
 }
 
 /// This conversion allows us to use `RustusError` in the `main` function.
@@ -52,7 +60,7 @@ impl From<RustusError> for Error {
 impl ResponseError for RustusError {
     fn error_response(&self) -> HttpResponse {
         HttpResponseBuilder::new(self.status_code())
-            .set_header("Content-Type", "text/html; charset=utf-8")
+            .insert_header(("Content-Type", "text/html; charset=utf-8"))
             .body(format!("{}", self))
     }
 
@@ -60,6 +68,7 @@ impl ResponseError for RustusError {
         match self {
             RustusError::FileNotFound => StatusCode::NOT_FOUND,
             RustusError::WrongOffset => StatusCode::CONFLICT,
+            RustusError::FrozenFile | RustusError::HookError(_) => StatusCode::BAD_REQUEST,
             _ => StatusCode::INTERNAL_SERVER_ERROR,
         }
     }
diff --git a/src/info_storages/models/available_info_storages.rs b/src/info_storages/models/available_info_storages.rs
index 256fb34..66a84cd 100644
--- a/src/info_storages/models/available_info_storages.rs
+++ b/src/info_storages/models/available_info_storages.rs
@@ -1,12 +1,10 @@
-use std::str::FromStr;
-
 use derive_more::{Display, From};
 
 use crate::errors::RustusResult;
-use crate::RustusConf;
+use crate::{from_str, RustusConf};
 
 use crate::info_storages::{file_info_storage, InfoStorage};
-use strum::{EnumIter, IntoEnumIterator};
+use strum::EnumIter;
 
 #[cfg(feature = "db_info_storage")]
 use crate::info_storages::db_info_storage;
@@ -16,36 +14,17 @@ use crate::info_storages::redis_info_storage;
 
 #[derive(PartialEq, From, Display, Clone, Debug, EnumIter)]
 pub enum AvailableInfoStores {
-    #[display(fmt = "file_info_storage")]
+    #[display(fmt = "file-info-storage")]
     Files,
     #[cfg(feature = "db_info_storage")]
-    #[display(fmt = "db_info_storage")]
+    #[display(fmt = "db-info-storage")]
     DB,
     #[cfg(feature = "redis_info_storage")]
-    #[display(fmt = "redis_info_storage")]
+    #[display(fmt = "redis-info-storage")]
     Redis,
 }
 
-impl FromStr for AvailableInfoStores {
-    type Err = String;
-
-    fn from_str(input: &str) -> Result<Self, Self::Err> {
-        let available_stores = AvailableInfoStores::iter()
-            .map(|info_store| format!("\t* {}", info_store.to_string()))
-            .collect::<Vec<String>>()
-            .join("\n");
-        let inp_string = String::from(input);
-        for store in AvailableInfoStores::iter() {
-            if inp_string == store.to_string() {
-                return Ok(store);
-            }
-        }
-        Err(format!(
-            "Unknown info storage type.\n Available storages:\n{}",
-            available_stores
-        ))
-    }
-}
+from_str!(AvailableInfoStores, "info storage");
 
 impl AvailableInfoStores {
     /// Convert `AvailableInfoStores` to the impl `InfoStorage`.
diff --git a/src/info_storages/models/file_info.rs b/src/info_storages/models/file_info.rs
index 4d2ff9a..154984e 100644
--- a/src/info_storages/models/file_info.rs
+++ b/src/info_storages/models/file_info.rs
@@ -11,7 +11,7 @@ pub struct FileInfo {
     pub id: String,
     pub offset: usize,
     pub length: usize,
-    pub path: String,
+    pub path: Option<String>,
     #[serde(with = "ts_seconds")]
     pub created_at: DateTime<Utc>,
     pub deferred_size: bool,
@@ -31,7 +31,7 @@ impl FileInfo {
     pub fn new(
         file_id: &str,
         file_size: Option<usize>,
-        path: String,
+        path: Option<String>,
         initial_metadata: Option<HashMap<String, String>>,
     ) -> FileInfo {
         let id = String::from(file_id);
diff --git a/src/main.rs b/src/main.rs
index f906b87..9c737dc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,8 +6,12 @@ use actix_web::{
     dev::{Server, Service},
     middleware, web, App, HttpServer,
 };
-use log::{error, info};
+use fern::colors::{Color, ColoredLevelConfig};
+use fern::Dispatch;
+use log::LevelFilter;
 
+use crate::errors::RustusResult;
+use crate::notifiers::models::notification_manager::NotificationManager;
 use config::RustusConf;
 
 use crate::storages::Storage;
@@ -25,12 +29,25 @@ fn greeting(app_conf: &RustusConf) {
     let extensions = app_conf
         .extensions_vec()
         .into_iter()
-        .map(String::from)
+        .map(|x| x.to_string())
         .collect::<Vec<String>>()
-        .join(",");
-    info!("Welcome to rustus!");
-    info!("Base URL: {}", app_conf.base_url());
-    info!("Available extensions {}", extensions);
+        .join(", ");
+    let hooks = app_conf
+        .notification_opts
+        .hooks
+        .clone()
+        .into_iter()
+        .map(|x| x.to_string())
+        .collect::<Vec<String>>()
+        .join(", ");
+    let rustus_logo = include_str!("../imgs/rustus_startup_logo.txt");
+    eprintln!("\n\n{}", rustus_logo);
+    eprintln!("Welcome to rustus!");
+    eprintln!("Base URL: {}", app_conf.base_url());
+    eprintln!("Available extensions: {}", extensions);
+    eprintln!("Enabled hooks: {}", hooks);
+    eprintln!();
+    eprintln!();
 }
 
 /// Creates Actix server.
@@ -49,24 +66,29 @@ fn greeting(app_conf: &RustusConf) {
 pub fn create_server(
     storage: Box<dyn Storage + Send + Sync>,
     app_conf: RustusConf,
+    notification_manager: NotificationManager,
 ) -> Result<Server, std::io::Error> {
     let host = app_conf.host.clone();
     let port = app_conf.port;
     let workers = app_conf.workers;
+    let app_conf_data = web::Data::new(app_conf.clone());
     let storage_data: web::Data<Box<dyn Storage + Send + Sync>> =
         web::Data::from(Arc::new(storage));
+    let manager_data: web::Data<Box<NotificationManager>> =
+        web::Data::from(Arc::new(Box::new(notification_manager)));
     let mut server = HttpServer::new(move || {
         App::new()
-            .data(app_conf.clone())
+            .app_data(app_conf_data.clone())
             .app_data(storage_data.clone())
+            .app_data(manager_data.clone())
             // Adds all routes.
             .configure(protocol::setup(app_conf.clone()))
             // Main middleware that appends TUS headers.
             .wrap(
                 middleware::DefaultHeaders::new()
-                    .header("Tus-Resumable", "1.0.0")
-                    .header("Tus-Max-Size", app_conf.max_body_size.to_string())
-                    .header("Tus-Version", "1.0.0"),
+                    .add(("Tus-Resumable", "1.0.0"))
+                    .add(("Tus-Max-Size", app_conf.max_body_size.to_string()))
+                    .add(("Tus-Version", "1.0.0")),
             )
             .wrap(middleware::Logger::new("\"%r\" \"-\" \"%s\" \"%a\" \"%D\""))
             // Middleware that overrides method of a request if
@@ -95,24 +117,60 @@ pub fn create_server(
     Ok(server.run())
 }
 
+fn setup_logging(app_config: &RustusConf) -> RustusResult<()> {
+    let colors = ColoredLevelConfig::new()
+        // use builder methods
+        .info(Color::Green)
+        .warn(Color::Yellow)
+        .debug(Color::BrightCyan)
+        .error(Color::BrightRed)
+        .trace(Color::Blue);
+
+    Dispatch::new()
+        .level(app_config.log_level)
+        .level_for("rbatis", LevelFilter::Error)
+        .chain(std::io::stdout())
+        .format(move |out, message, record| {
+            out.finish(format_args!(
+                "{}[{}] {}",
+                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S%:z]"),
+                colors.color(record.level()),
+                message
+            ));
+        })
+        .apply()?;
+    Ok(())
+}
+
 /// Main program entrypoint.
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
     let app_conf = RustusConf::from_args();
-    simple_logging::log_to_stderr(app_conf.log_level);
+    // Configuring logging.
+    // I may change it to another log system like `fern` later, idk.
+    setup_logging(&app_conf)?;
+    // Printing cool message.
+    greeting(&app_conf);
 
+    // Creating info storage.
+    // It's used to store info about files.
     let mut info_storage = app_conf
         .info_storage_opts
         .info_storage
         .get(&app_conf)
         .await?;
+    // Preparing it, lol.
     info_storage.prepare().await?;
+
+    // Creating file storage.
     let mut storage = app_conf.storage_opts.storage.get(&app_conf, info_storage);
-    if let Err(err) = storage.prepare().await {
-        error!("{}", err);
-        return Err(err.into());
-    }
-    greeting(&app_conf);
-    let server = create_server(storage, app_conf)?;
+    // Preparing it.
+    storage.prepare().await?;
+
+    // Creating notification manager.
+    let notification_manager = NotificationManager::new(&app_conf);
+
+    // Creating actual server and running it.
+    let server = create_server(storage, app_conf, notification_manager)?;
     server.await
 }
diff --git a/src/notifiers/http_notifier.rs b/src/notifiers/http_notifier.rs
new file mode 100644
index 0000000..dcd50b3
--- /dev/null
+++ b/src/notifiers/http_notifier.rs
@@ -0,0 +1,48 @@
+use crate::errors::RustusResult;
+
+use crate::notifiers::{Hook, Notifier};
+
+use async_trait::async_trait;
+use futures::future::try_join_all;
+use log::debug;
+use reqwest::Client;
+use std::time::Duration;
+
+pub struct HttpNotifier {
+    urls: Vec<String>,
+    client: Client,
+}
+
+impl HttpNotifier {
+    pub fn new(urls: Vec<String>) -> Self {
+        let client = Client::new();
+        Self { urls, client }
+    }
+}
+
+#[async_trait]
+impl Notifier for HttpNotifier {
+    async fn prepare(&mut self) -> RustusResult<()> {
+        Ok(())
+    }
+
+    async fn send_message(&self, message: String, hook: Hook) -> RustusResult<()> {
+        debug!("Starting HTTP Hook.");
+        let idempotency_key = uuid::Uuid::new_v4().to_string();
+        let requests_vec = self.urls.iter().map(|url| {
+            debug!("Preparing request for {}", url);
+            self.client
+                .post(url.as_str())
+                .header("Idempotency-Key", idempotency_key.as_str())
+                .header("Hook-Name", hook.clone().to_string())
+                .timeout(Duration::from_secs(2))
+                .body(message.clone())
+                .send()
+        });
+        let responses = try_join_all(requests_vec).await?;
+        for resp in responses {
+            resp.error_for_status()?;
+        }
+        Ok(())
+    }
+}
diff --git a/src/notifiers/mod.rs b/src/notifiers/mod.rs
index feb8e34..096a525 100644
--- a/src/notifiers/mod.rs
+++ b/src/notifiers/mod.rs
@@ -1,2 +1,7 @@
-mod models;
-mod notifier;
+#[cfg(feature = "http_notifier")]
+pub mod http_notifier;
+pub mod models;
+
+pub use models::hooks::Hook;
+pub use models::message_format::Format;
+pub use models::notifier::Notifier;
diff --git a/src/notifiers/models/hooks.rs b/src/notifiers/models/hooks.rs
new file mode 100644
index 0000000..fc33fb1
--- /dev/null
+++ b/src/notifiers/models/hooks.rs
@@ -0,0 +1,20 @@
+use crate::from_str;
+use derive_more::{Display, From};
+use strum::EnumIter;
+
+/// Hooks for notifications.
+#[derive(Copy, Clone, Debug, Display, From, EnumIter, Eq, PartialEq)]
+pub enum Hook {
+    #[display(fmt = "pre-create")]
+    PreCreate,
+    #[display(fmt = "post-create")]
+    PostCreate,
+    #[display(fmt = "post-receive")]
+    PostReceive,
+    #[display(fmt = "post-terminate")]
+    PostTerminate,
+    #[display(fmt = "post-finish")]
+    PostFinish,
+}
+
+from_str!(Hook, "hook");
diff --git a/src/notifiers/models/message.rs b/src/notifiers/models/message.rs
deleted file mode 100644
index 4006433..0000000
--- a/src/notifiers/models/message.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use crate::info_storages::FileInfo;
-use actix_web::HttpRequest;
-
-pub struct Message<'a> {
-    request: &'a HttpRequest,
-    file_info: FileInfo,
-}
diff --git a/src/notifiers/models/message_format.rs b/src/notifiers/models/message_format.rs
new file mode 100644
index 0000000..d640192
--- /dev/null
+++ b/src/notifiers/models/message_format.rs
@@ -0,0 +1,162 @@
+use crate::errors::RustusResult;
+use crate::info_storages::FileInfo;
+use actix_web::HttpRequest;
+use derive_more::{Display, From};
+use serde::Serialize;
+use serde_json::Map;
+use serde_json::Value;
+use std::collections::HashMap;
+
+use crate::from_str;
+use strum::EnumIter;
+
+#[derive(Clone, Debug, Eq, Display, From, PartialEq, EnumIter)]
+pub enum Format {
+    #[display(fmt = "default")]
+    Default,
+    #[display(fmt = "tusd")]
+    Tusd,
+    #[display(fmt = "celery")]
+    Celery,
+}
+
+from_str!(Format, "format");
+
+impl Format {
+    pub fn format(&self, request: &HttpRequest, file_info: &FileInfo) -> RustusResult<String> {
+        match self {
+            Self::Default => default_format(request, file_info),
+            Self::Tusd => tusd_format(request, file_info),
+            Self::Celery => celery_format(request, file_info),
+        }
+    }
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "PascalCase")]
+struct TusdStorageInfo {
+    #[serde(rename = "Type")]
+    storage_type: String,
+    path: Option<String>,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "PascalCase")]
+struct TusdFileInfo {
+    #[serde(rename = "ID")]
+    id: String,
+    offset: usize,
+    size: usize,
+    is_final: bool,
+    is_partial: bool,
+    partial_uploads: Option<Vec<String>>,
+    metadata: HashMap<String, String>,
+    storage: TusdStorageInfo,
+}
+
+impl From<FileInfo> for TusdFileInfo {
+    fn from(file_info: FileInfo) -> Self {
+        Self {
+            id: file_info.id,
+            offset: file_info.offset,
+            size: file_info.length,
+            is_final: true,
+            is_partial: false,
+            partial_uploads: None,
+            metadata: file_info.metadata,
+            storage: TusdStorageInfo {
+                storage_type: "filestore".into(),
+                path: file_info.path,
+            },
+        }
+    }
+}
+
+/// Turn request into `serde_json::Value`.
+///
+/// This function is used by different formats.
+fn serialize_request(
+    request: &HttpRequest,
+    method_str: String,
+    remote_addr_str: String,
+    headers_str: String,
+    use_arrays: bool,
+) -> Value {
+    let mut map = Map::new();
+    map.insert("URI".into(), Value::String(request.uri().to_string()));
+    map.insert(method_str, Value::String(request.method().to_string()));
+    map.insert(
+        remote_addr_str,
+        Value::String(
+            request
+                .connection_info()
+                .realip_remote_addr()
+                .map_or_else(String::new, String::from),
+        ),
+    );
+    let mut headers_map = Map::new();
+    for (name, value) in request.headers() {
+        if let Ok(header_val) = value.to_str().map(String::from) {
+            if use_arrays {
+                headers_map.insert(
+                    name.to_string(),
+                    Value::Array(vec![Value::String(header_val)]),
+                );
+            } else {
+                headers_map.insert(name.to_string(), Value::String(header_val));
+            }
+        }
+    }
+    map.insert(headers_str, Value::Object(headers_map));
+    Value::Object(map)
+}
+
+/// Default format is specific for Rustus.
+///
+/// This format is a simple serialized `FileInfo` and some parts of the request.
+pub fn default_format(request: &HttpRequest, file_info: &FileInfo) -> RustusResult<String> {
+    let mut result_map = Map::new();
+    result_map.insert("upload".into(), serde_json::to_value(file_info)?);
+    result_map.insert(
+        "request".into(),
+        serialize_request(
+            request,
+            "method".into(),
+            "remote_addr".into(),
+            "headers".into(),
+            false,
+        ),
+    );
+    Ok(Value::Object(result_map).to_string())
+}
+
+/// This format follows TUSD hooks.
+///
+/// You can read more about tusd hooks
+/// [here](https://github.com/tus/tusd/blob/master/docs/hooks.md).
+///
+/// Generally speaking, it's almost the same as the default format,
+/// but some variables are ommited and headers are added to the request.
+pub fn tusd_format(request: &HttpRequest, file_info: &FileInfo) -> RustusResult<String> {
+    let mut result_map = Map::new();
+
+    result_map.insert(
+        "Upload".into(),
+        serde_json::to_value(TusdFileInfo::from(file_info.clone()))?,
+    );
+    result_map.insert(
+        "HTTPRequest".into(),
+        serialize_request(
+            request,
+            "Method".into(),
+            "RemoteAddr".into(),
+            "Header".into(),
+            true,
+        ),
+    );
+    Ok(Value::Object(result_map).to_string())
+}
+
+pub fn celery_format(_request: &HttpRequest, _file_info: &FileInfo) -> RustusResult<String> {
+    todo!()
+}
diff --git a/src/notifiers/models/mod.rs b/src/notifiers/models/mod.rs
index e216a50..e8571be 100644
--- a/src/notifiers/models/mod.rs
+++ b/src/notifiers/models/mod.rs
@@ -1 +1,4 @@
-pub mod message;
+pub mod hooks;
+pub mod message_format;
+pub mod notification_manager;
+pub mod notifier;
diff --git a/src/notifiers/models/notification_manager.rs b/src/notifiers/models/notification_manager.rs
new file mode 100644
index 0000000..10ccb4f
--- /dev/null
+++ b/src/notifiers/models/notification_manager.rs
@@ -0,0 +1,44 @@
+use crate::errors::{RustusError, RustusResult};
+#[cfg(feature = "http_notifier")]
+use crate::notifiers::http_notifier;
+use crate::notifiers::{Hook, Notifier};
+use crate::RustusConf;
+use futures::future::try_join_all;
+use log::debug;
+
+pub struct NotificationManager {
+    notifiers: Vec<Box<dyn Notifier + Send + Sync>>,
+}
+
+impl NotificationManager {
+    pub fn new(tus_config: &RustusConf) -> Self {
+        let mut manager = Self {
+            notifiers: Vec::new(),
+        };
+        debug!("Initializing notification manager.");
+        #[cfg(feature = "http_notifier")]
+        if !tus_config.notification_opts.hooks_http_urls.is_empty() {
+            debug!("Found http hook urls.");
+            manager
+                .notifiers
+                .push(Box::new(http_notifier::HttpNotifier::new(
+                    tus_config.notification_opts.hooks_http_urls.clone(),
+                )));
+        }
+        debug!("Notification manager initialized.");
+        manager
+    }
+
+    pub async fn send_message(&self, message: String, hook: Hook) -> RustusResult<()> {
+        let mut futures = Vec::new();
+        for notifier in &self.notifiers {
+            futures.push(notifier.send_message(message.clone(), hook));
+        }
+        if !futures.is_empty() {
+            try_join_all(futures)
+                .await
+                .map_err(|err| RustusError::HookError(err.to_string()))?;
+        }
+        Ok(())
+    }
+}
diff --git a/src/notifiers/models/notifier.rs b/src/notifiers/models/notifier.rs
new file mode 100644
index 0000000..1b5b9c8
--- /dev/null
+++ b/src/notifiers/models/notifier.rs
@@ -0,0 +1,10 @@
+use crate::errors::RustusResult;
+
+use crate::notifiers::Hook;
+use async_trait::async_trait;
+
+#[async_trait]
+pub trait Notifier {
+    async fn prepare(&mut self) -> RustusResult<()>;
+    async fn send_message(&self, message: String, hook: Hook) -> RustusResult<()>;
+}
diff --git a/src/notifiers/notifier.rs b/src/notifiers/notifier.rs
deleted file mode 100644
index f71f3cd..0000000
--- a/src/notifiers/notifier.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use async_trait::async_trait;
-
-#[async_trait]
-trait Notifier {
-    async fn send_message();
-}
diff --git a/src/protocol/core/mod.rs b/src/protocol/core/mod.rs
index 312ea32..df00d5c 100644
--- a/src/protocol/core/mod.rs
+++ b/src/protocol/core/mod.rs
@@ -40,7 +40,7 @@ pub fn add_extension(web_app: &mut web::ServiceConfig, app_conf: &RustusConf) {
                 .name("core:file_info")
                 .guard(guard::Head())
                 // Header to prevent the client and/or proxies from caching the response.
-                .wrap(middleware::DefaultHeaders::new().header("Cache-Control", "no-store"))
+                .wrap(middleware::DefaultHeaders::new().add(("Cache-Control", "no-store")))
                 .to(routes::get_file_info),
         );
 }
diff --git a/src/protocol/core/routes.rs b/src/protocol/core/routes.rs
index 3620b79..8fe8366 100644
--- a/src/protocol/core/routes.rs
+++ b/src/protocol/core/routes.rs
@@ -1,24 +1,19 @@
-use actix_web::{
-    dev::HttpResponseBuilder,
-    http::StatusCode,
-    web,
-    web::{Buf, Bytes},
-    HttpRequest, HttpResponse,
-};
+use actix_web::{http::StatusCode, web, web::Bytes, HttpRequest, HttpResponse};
 
+use crate::notifiers::Hook;
 use crate::utils::headers::{check_header, parse_header};
-use crate::{RustusConf, Storage};
+use crate::{NotificationManager, RustusConf, Storage};
 
 #[allow(clippy::needless_pass_by_value)]
 pub fn server_info(app_conf: web::Data<RustusConf>) -> HttpResponse {
     let ext_str = app_conf
         .extensions_vec()
         .into_iter()
-        .map(String::from)
+        .map(|x| x.to_string())
         .collect::<Vec<String>>()
         .join(",");
-    HttpResponseBuilder::new(StatusCode::OK)
-        .set_header("Tus-Extension", ext_str.as_str())
+    HttpResponse::Ok()
+        .insert_header(("Tus-Extension", ext_str.as_str()))
         .body("")
 }
 
@@ -26,15 +21,15 @@ pub async fn get_file_info(
     storage: web::Data<Box<dyn Storage + Send + Sync>>,
     request: HttpRequest,
 ) -> actix_web::Result<HttpResponse> {
-    let mut builder = HttpResponseBuilder::new(StatusCode::OK);
+    let mut builder = HttpResponse::Ok();
     if let Some(file_id) = request.match_info().get("file_id") {
         let file_info = storage.get_file_info(file_id).await?;
         builder
-            .set_header("Upload-Offset", file_info.offset.to_string())
-            .set_header("Upload-Length", file_info.length.to_string())
-            .set_header("Content-Length", file_info.offset.to_string());
+            .insert_header(("Upload-Offset", file_info.offset.to_string()))
+            .insert_header(("Upload-Length", file_info.length.to_string()))
+            .insert_header(("Content-Length", file_info.offset.to_string()));
         if file_info.deferred_size {
-            builder.set_header("Upload-Defer-Length", "1");
+            builder.insert_header(("Upload-Defer-Length", "1"));
         }
     } else {
         builder.status(StatusCode::NOT_FOUND);
@@ -46,6 +41,8 @@ pub async fn write_bytes(
     request: HttpRequest,
     bytes: Bytes,
     storage: web::Data<Box<dyn Storage + Send + Sync>>,
+    notification_manager: web::Data<Box<NotificationManager>>,
+    app_conf: web::Data<RustusConf>,
 ) -> actix_web::Result<HttpResponse> {
     if !check_header(&request, "Content-Type", "application/offset+octet-stream") {
         return Ok(HttpResponse::UnsupportedMediaType().body(""));
@@ -53,17 +50,28 @@ pub async fn write_bytes(
     let offset = parse_header(&request, "Upload-Offset");
 
     if offset.is_none() {
-        return Ok(HttpResponseBuilder::new(StatusCode::UNSUPPORTED_MEDIA_TYPE).body(""));
+        return Ok(HttpResponse::UnsupportedMediaType().body(""));
     }
 
     if let Some(file_id) = request.match_info().get("file_id") {
-        let offset = storage
-            .add_bytes(file_id, offset.unwrap(), bytes.bytes())
+        let file_info = storage
+            .add_bytes(file_id, offset.unwrap(), bytes.as_ref())
             .await?;
-        Ok(HttpResponseBuilder::new(StatusCode::NO_CONTENT)
-            .set_header("Upload-Offset", offset.to_string())
+        let mut hook = Hook::PostReceive;
+        if file_info.length == file_info.offset {
+            hook = Hook::PostFinish;
+        }
+        if app_conf.hook_is_active(hook) {
+            let message = app_conf
+                .notification_opts
+                .notification_format
+                .format(&request, &file_info)?;
+            tokio::spawn(async move { notification_manager.send_message(message, hook).await });
+        }
+        Ok(HttpResponse::NoContent()
+            .insert_header(("Upload-Offset", file_info.offset.to_string()))
             .body(""))
     } else {
-        Ok(HttpResponseBuilder::new(StatusCode::NOT_FOUND).body(""))
+        Ok(HttpResponse::NotFound().body(""))
     }
 }
diff --git a/src/protocol/creation/routes.rs b/src/protocol/creation/routes.rs
index 2bd587d..79079dd 100644
--- a/src/protocol/creation/routes.rs
+++ b/src/protocol/creation/routes.rs
@@ -1,11 +1,13 @@
 use std::collections::HashMap;
 
-use actix_web::web::{Buf, Bytes};
+use actix_web::web::Bytes;
 use actix_web::{web, HttpRequest, HttpResponse};
 
-use crate::config::ProtocolExtensions;
+use crate::info_storages::FileInfo;
+use crate::notifiers::Hook;
+use crate::protocol::extensions::Extensions;
 use crate::utils::headers::{check_header, parse_header};
-use crate::{RustusConf, Storage};
+use crate::{NotificationManager, RustusConf, Storage};
 
 /// Get metadata info from request.
 ///
@@ -49,6 +51,7 @@ fn get_metadata(request: &HttpRequest) -> Option<HashMap<String, String>> {
 
 pub async fn create_file(
     storage: web::Data<Box<dyn Storage + Send + Sync>>,
+    notification_manager: web::Data<Box<NotificationManager>>,
     app_conf: web::Data<RustusConf>,
     request: HttpRequest,
     bytes: Bytes,
@@ -61,7 +64,7 @@ pub async fn create_file(
     // Indicator that creation-defer-length is enabled.
     let defer_ext = app_conf
         .extensions_vec()
-        .contains(&ProtocolExtensions::CreationDeferLength);
+        .contains(&Extensions::CreationDeferLength);
 
     // Check that Upload-Length header is provided.
     // Otherwise checking that defer-size feature is enabled
@@ -71,30 +74,52 @@ pub async fn create_file(
     }
 
     let meta = get_metadata(&request);
+
+    if app_conf.hook_is_active(Hook::PreCreate) {
+        let initial_file_info = FileInfo::new("", length, None, meta.clone());
+        let message = app_conf
+            .notification_opts
+            .notification_format
+            .format(&request, &initial_file_info)?;
+        notification_manager
+            .send_message(message, Hook::PreCreate)
+            .await?;
+    }
+
     // Create file and get the id.
-    let file_id = storage.create_file(length, meta).await?;
+    let mut file_info = storage.create_file(length, meta).await?;
 
     // Create upload URL for this file.
-    let upload_url = request.url_for("core:write_bytes", &[file_id.clone()])?;
-
-    let mut upload_offset = 0;
+    let upload_url = request.url_for("core:write_bytes", &[file_info.id.clone()])?;
 
     // Checking if creation-with-upload extension is enabled.
     let with_upload = app_conf
         .extensions_vec()
-        .contains(&ProtocolExtensions::CreationWithUpload);
+        .contains(&Extensions::CreationWithUpload);
     if with_upload && !bytes.is_empty() {
         if !check_header(&request, "Content-Type", "application/offset+octet-stream") {
             return Ok(HttpResponse::BadRequest().body(""));
         }
         // Writing first bytes.
-        upload_offset = storage
-            .add_bytes(file_id.as_str(), 0, bytes.bytes())
+        file_info = storage
+            .add_bytes(file_info.id.as_str(), 0, bytes.as_ref())
             .await?;
     }
 
+    if app_conf.hook_is_active(Hook::PostCreate) {
+        let message = app_conf
+            .notification_opts
+            .notification_format
+            .format(&request, &file_info)?;
+        tokio::spawn(async move {
+            notification_manager
+                .send_message(message, Hook::PostCreate)
+                .await
+        });
+    }
+
     Ok(HttpResponse::Created()
-        .set_header("Location", upload_url.as_str())
-        .set_header("Upload-Offset", upload_offset.to_string())
+        .insert_header(("Location", upload_url.as_str()))
+        .insert_header(("Upload-Offset", file_info.offset.to_string()))
         .body(""))
 }
diff --git a/src/protocol/extensions.rs b/src/protocol/extensions.rs
new file mode 100644
index 0000000..5624b83
--- /dev/null
+++ b/src/protocol/extensions.rs
@@ -0,0 +1,21 @@
+use derive_more::{Display, From};
+use strum::EnumIter;
+
+use crate::from_str;
+
+/// Enum of available Protocol Extensions
+#[derive(PartialEq, Debug, PartialOrd, Display, EnumIter, From, Clone, Ord, Eq)]
+pub enum Extensions {
+    #[display(fmt = "creation-defer-length")]
+    CreationDeferLength,
+    #[display(fmt = "creation-with-upload")]
+    CreationWithUpload,
+    #[display(fmt = "creation")]
+    Creation,
+    #[display(fmt = "termination")]
+    Termination,
+    #[display(fmt = "getting")]
+    Getting,
+}
+
+from_str!(Extensions, "extension");
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 38d198c..ecd6dba 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -1,10 +1,10 @@
 use actix_web::web;
 
-use crate::config::ProtocolExtensions;
 use crate::RustusConf;
 
 mod core;
 mod creation;
+pub mod extensions;
 mod getting;
 mod termination;
 
@@ -16,11 +16,11 @@ pub fn setup(app_conf: RustusConf) -> Box<dyn Fn(&mut web::ServiceConfig)> {
     Box::new(move |web_app| {
         for extension in app_conf.extensions_vec() {
             match extension {
-                ProtocolExtensions::Creation => creation::add_extension(web_app, &app_conf),
-                ProtocolExtensions::Termination => {
+                extensions::Extensions::Creation => creation::add_extension(web_app, &app_conf),
+                extensions::Extensions::Termination => {
                     termination::add_extension(web_app, &app_conf);
                 }
-                ProtocolExtensions::Getting => {
+                extensions::Extensions::Getting => {
                     getting::add_extension(web_app, &app_conf);
                 }
                 _ => {}
diff --git a/src/protocol/termination/routes.rs b/src/protocol/termination/routes.rs
index 2ae818c..7222b4f 100644
--- a/src/protocol/termination/routes.rs
+++ b/src/protocol/termination/routes.rs
@@ -1,9 +1,8 @@
-use actix_web::dev::HttpResponseBuilder;
-use actix_web::http::StatusCode;
 use actix_web::{web, HttpRequest, HttpResponse};
 
 use crate::errors::RustusResult;
-use crate::Storage;
+use crate::notifiers::Hook;
+use crate::{NotificationManager, RustusConf, Storage};
 
 /// Terminate uploading.
 ///
@@ -12,10 +11,23 @@ use crate::Storage;
 pub async fn terminate(
     storage: web::Data<Box<dyn Storage + Send + Sync>>,
     request: HttpRequest,
+    notification_manager: web::Data<Box<NotificationManager>>,
+    app_conf: web::Data<RustusConf>,
 ) -> RustusResult<HttpResponse> {
     let file_id_opt = request.match_info().get("file_id").map(String::from);
     if let Some(file_id) = file_id_opt {
-        storage.remove_file(file_id.as_str()).await?;
+        let file_info = storage.remove_file(file_id.as_str()).await?;
+        if app_conf.hook_is_active(Hook::PostTerminate) {
+            let message = app_conf
+                .notification_opts
+                .notification_format
+                .format(&request, &file_info)?;
+            tokio::spawn(async move {
+                notification_manager
+                    .send_message(message, Hook::PostTerminate)
+                    .await
+            });
+        }
     }
-    Ok(HttpResponseBuilder::new(StatusCode::NO_CONTENT).body(""))
+    Ok(HttpResponse::NoContent().body(""))
 }
diff --git a/src/storages/file_storage.rs b/src/storages/file_storage.rs
index ccd93bf..bad2c39 100644
--- a/src/storages/file_storage.rs
+++ b/src/storages/file_storage.rs
@@ -35,7 +35,12 @@ impl FileStorage {
             .app_conf
             .storage_opts
             .data_dir
-            .join(self.app_conf.dir_struct());
+            .canonicalize()
+            .map_err(|err| {
+                error!("{}", err);
+                RustusError::UnableToWrite(err.to_string())
+            })?
+            .join(self.app_conf.dir_struct().as_str());
         create_dir_all(dir.as_path()).await.map_err(|err| {
             error!("{}", err);
             RustusError::UnableToWrite(err.to_string())
@@ -62,10 +67,15 @@ impl Storage for FileStorage {
 
     async fn get_contents(&self, file_id: &str) -> RustusResult<NamedFile> {
         let info = self.info_storage.get_info(file_id).await?;
-        NamedFile::open(info.path.as_str()).map_err(|err| {
-            error!("{:?}", err);
-            RustusError::FileNotFound
-        })
+        if info.path.is_none() {
+            return Err(RustusError::FileNotFound);
+        }
+        NamedFile::open_async(info.path.unwrap().as_str())
+            .await
+            .map_err(|err| {
+                error!("{:?}", err);
+                RustusError::FileNotFound
+            })
     }
 
     async fn add_bytes(
@@ -73,16 +83,22 @@ impl Storage for FileStorage {
         file_id: &str,
         request_offset: usize,
         bytes: &[u8],
-    ) -> RustusResult<usize> {
+    ) -> RustusResult<FileInfo> {
         let mut info = self.info_storage.get_info(file_id).await?;
         if info.offset != request_offset {
             return Err(RustusError::WrongOffset);
         }
+        if info.path.is_none() {
+            return Err(RustusError::FileNotFound);
+        }
+        if info.offset == info.length {
+            return Err(RustusError::FrozenFile);
+        }
         let mut file = OpenOptions::new()
             .write(true)
             .append(true)
             .create(false)
-            .open(info.path.as_str())
+            .open(info.path.as_ref().unwrap())
             .await
             .map_err(|err| {
                 error!("{:?}", err);
@@ -90,18 +106,18 @@ impl Storage for FileStorage {
             })?;
         file.write_all(bytes).await.map_err(|err| {
             error!("{:?}", err);
-            RustusError::UnableToWrite(info.path.clone())
+            RustusError::UnableToWrite(info.path.clone().unwrap())
         })?;
         info.offset += bytes.len();
         self.info_storage.set_info(&info, false).await?;
-        Ok(info.offset)
+        Ok(info)
     }
 
     async fn create_file(
         &self,
         file_size: Option<usize>,
         metadata: Option<HashMap<String, String>>,
-    ) -> RustusResult<String> {
+    ) -> RustusResult<FileInfo> {
         let file_id = Uuid::new_v4().simple().to_string();
         let file_path = self.data_file_path(file_id.as_str()).await?;
         let mut file = OpenOptions::new()
@@ -124,20 +140,23 @@ impl Storage for FileStorage {
         let file_info = FileInfo::new(
             file_id.as_str(),
             file_size,
-            file_path.display().to_string(),
+            Some(file_path.display().to_string()),
             metadata,
         );
 
         self.info_storage.set_info(&file_info, true).await?;
 
-        Ok(file_id)
+        Ok(file_info)
     }
 
-    async fn remove_file(&self, file_id: &str) -> RustusResult<()> {
+    async fn remove_file(&self, file_id: &str) -> RustusResult<FileInfo> {
         let info = self.info_storage.get_info(file_id).await?;
+        if info.path.is_none() {
+            return Err(RustusError::FileNotFound);
+        }
         self.info_storage.remove_info(file_id).await?;
 
-        let data_path = PathBuf::from(info.path.clone());
+        let data_path = PathBuf::from(info.path.as_ref().unwrap().clone());
         if !data_path.exists() {
             return Err(RustusError::FileNotFound);
         }
@@ -145,6 +164,6 @@ impl Storage for FileStorage {
             error!("{:?}", err);
             RustusError::UnableToRemove(String::from(file_id))
         })?;
-        Ok(())
+        Ok(info)
     }
 }
diff --git a/src/storages/models/available_stores.rs b/src/storages/models/available_stores.rs
index e13c670..931922a 100644
--- a/src/storages/models/available_stores.rs
+++ b/src/storages/models/available_stores.rs
@@ -1,31 +1,17 @@
 use crate::info_storages::InfoStorage;
 use crate::storages::file_storage;
-use crate::{RustusConf, Storage};
+use crate::{from_str, RustusConf, Storage};
 use derive_more::{Display, From};
-use std::str::FromStr;
+use strum::EnumIter;
 
 /// Enum of available Storage implementations.
-#[derive(PartialEq, From, Display, Clone, Debug)]
+#[derive(PartialEq, From, Display, EnumIter, Clone, Debug)]
 pub enum AvailableStores {
-    #[display(fmt = "FileStorage")]
+    #[display(fmt = "file-storage")]
     FileStorage,
 }
 
-impl FromStr for AvailableStores {
-    type Err = String;
-
-    /// This function converts string to the `AvailableStore` item.
-    /// This function is used by structopt to parse CLI parameters.
-    ///
-    /// # Params
-    /// `input` - input string.
-    fn from_str(input: &str) -> Result<AvailableStores, Self::Err> {
-        match input {
-            "file_storage" => Ok(AvailableStores::FileStorage),
-            _ => Err(String::from("Unknown storage type")),
-        }
-    }
-}
+from_str!(AvailableStores, "storage");
 
 impl AvailableStores {
     /// Convert `AvailableStores` to the Storage.
diff --git a/src/storages/models/storage.rs b/src/storages/models/storage.rs
index 89bed1b..955ad34 100644
--- a/src/storages/models/storage.rs
+++ b/src/storages/models/storage.rs
@@ -44,7 +44,7 @@ pub trait Storage {
         file_id: &str,
         request_offset: usize,
         bytes: &[u8],
-    ) -> RustusResult<usize>;
+    ) -> RustusResult<FileInfo>;
 
     /// Create file in storage.
     ///
@@ -57,7 +57,7 @@ pub trait Storage {
         &self,
         file_size: Option<usize>,
         metadata: Option<HashMap<String, String>>,
-    ) -> RustusResult<String>;
+    ) -> RustusResult<FileInfo>;
 
     /// Remove file from storage
     ///
@@ -66,5 +66,5 @@ pub trait Storage {
     ///
     /// # Params
     /// `file_id` - unique file identifier;
-    async fn remove_file(&self, file_id: &str) -> RustusResult<()>;
+    async fn remove_file(&self, file_id: &str) -> RustusResult<FileInfo>;
 }
diff --git a/src/utils/enums.rs b/src/utils/enums.rs
new file mode 100644
index 0000000..0ae1ae3
--- /dev/null
+++ b/src/utils/enums.rs
@@ -0,0 +1,28 @@
+#[macro_export]
+macro_rules! from_str {
+    ($enum_name:ty, $name:literal) => {
+        use std::str::FromStr;
+        use strum::IntoEnumIterator;
+
+        impl FromStr for $enum_name {
+            type Err = String;
+
+            fn from_str(input: &str) -> Result<Self, Self::Err> {
+                let available_stores = <$enum_name>::iter()
+                    .map(|info_store| format!("\t* {}", info_store.to_string()))
+                    .collect::<Vec<String>>()
+                    .join("\n");
+                let inp_string = String::from(input);
+                for store in <$enum_name>::iter() {
+                    if inp_string == store.to_string() {
+                        return Ok(store);
+                    }
+                }
+                Err(format!(
+                    "Unknown {} '{}'.\n Available {}s:\n{}",
+                    $name, input, $name, available_stores
+                ))
+            }
+        }
+    };
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 7b67a24..0f6b300 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1 +1,2 @@
+pub mod enums;
 pub mod headers;
-- 
GitLab