diff options
author | Vika <vika@fireburn.ru> | 2022-02-15 02:44:33 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2022-02-15 02:46:24 +0300 |
commit | 9e4c4551a786830bf34d74c4ef111a8ed292fa9f (patch) | |
tree | 7796d7e529c89f22bccfbba4566b6bf5efca8071 | |
parent | d1327ed6b28a49770aa5d9b06245aa063b406f78 (diff) | |
download | kittybox-9e4c4551a786830bf34d74c4ef111a8ed292fa9f.tar.zst |
WIP: convert to Tokio and Warp
Warp allows requests to be applied as "filters", allowing to flexibly split up logic and have it work in a functional style, similar to pipes. Tokio is just an alternative runtime. I thought that maybe switching runtimes and refactoring the code might allow me to fish out that pesky bug with the whole application hanging after a certain amount of requests...
-rw-r--r-- | Cargo.lock | 921 | ||||
-rw-r--r-- | Cargo.toml | 23 | ||||
-rwxr-xr-x | dev.sh | 2 | ||||
-rw-r--r-- | src/database/file/mod.rs | 51 | ||||
-rw-r--r-- | src/database/mod.rs | 38 | ||||
-rw-r--r-- | src/lib.rs | 270 | ||||
-rw-r--r-- | src/main.rs | 118 | ||||
-rw-r--r-- | src/metrics.rs | 23 | ||||
-rw-r--r-- | src/micropub/mod.rs | 128 |
9 files changed, 861 insertions, 713 deletions
diff --git a/Cargo.lock b/Cargo.lock index d4d0544..1b054ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,22 +81,25 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.51" +name = "alloc-no-stdlib" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203" +checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" [[package]] -name = "arrayref" -version = "0.3.6" +name = "alloc-stdlib" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +dependencies = [ + "alloc-no-stdlib", +] [[package]] -name = "arrayvec" -version = "0.5.2" +name = "anyhow" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203" [[package]] name = "assert-json-diff" @@ -109,16 +112,6 @@ dependencies = [ ] [[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn", -] - -[[package]] name = "async-channel" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -130,13 +123,17 @@ dependencies = [ ] [[package]] -name = "async-dup" -version = "1.2.2" +name = "async-compression" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" +checksum = "f2bf394cfbbe876f0ac67b13b6ca819f9c9f2fb9ec67223cceb1555fbab1c31a" dependencies = [ - "futures-io", - "simple-mutex", + "brotli", + "flate2", + "futures-core", + "memchr 2.4.1", + "pin-project-lite", + "tokio", ] [[package]] @@ -170,22 +167,6 @@ dependencies = [ ] [[package]] -name = "async-h1" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8101020758a4fc3a7c326cb42aa99e9fa77cbfb76987c128ad956406fe1f70a7" -dependencies = [ - "async-channel", - "async-dup", - "async-std", - "futures-core", - "http-types", - "httparse", - "log 0.4.14", - "pin-project", -] - -[[package]] name = "async-io" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -223,69 +204,15 @@ dependencies = [ ] [[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-session" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345022a2eed092cd105cc1b26fd61c341e100bd5fcbbd792df4baf31c2cc631f" -dependencies = [ - "anyhow", - "async-std", - "async-trait", - "base64 0.12.3", - "bincode", - "blake3", - "chrono", - "hmac 0.8.1", - "kv-log-macro", - "rand 0.7.3", - "serde", - "serde_json", - "sha2", -] - -[[package]] -name = "async-sse" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53bba003996b8fd22245cd0c59b869ba764188ed435392cf2796d03b805ade10" -dependencies = [ - "async-channel", - "async-std", - "http-types", - "log 0.4.14", - "memchr 2.4.1", - "pin-project-lite 0.1.12", -] - -[[package]] name = "async-std" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" dependencies = [ - "async-attributes", "async-channel", "async-global-executor", "async-io", "async-lock", - "async-process", "crossbeam-utils", "futures-channel", "futures-core", @@ -297,7 +224,7 @@ dependencies = [ "memchr 2.4.1", "num_cpus", "once_cell", - "pin-project-lite 0.2.7", + "pin-project-lite", "pin-utils", "slab", "wasm-bindgen-futures", @@ -321,19 +248,6 @@ dependencies = [ ] [[package]] -name = "async-tls" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85a97c4a0ecce878efd3f945f119c78a646d8975340bca0398f9bb05c30cc52" -dependencies = [ - "futures-core", - "futures-io", - "rustls", - "webpki", - "webpki-roots", -] - -[[package]] name = "async-trait" version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -375,51 +289,30 @@ checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" [[package]] name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "blake3" -version = "0.3.8" +name = "block-buffer" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if 0.1.10", - "constant_time_eq", - "crypto-mac 0.8.0", - "digest", + "generic-array", ] [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array", ] @@ -439,6 +332,37 @@ dependencies = [ ] [[package]] +name = "brotli" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f838e47a451d5a8fa552371f80024dd6ace9b7acdf25c4c3d0f9bc6816fb1c39" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr 2.4.1", + "safemem", +] + +[[package]] name = "bumpalo" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -470,12 +394,6 @@ checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" @@ -523,7 +441,7 @@ dependencies = [ "bytes", "futures-core", "memchr 2.4.1", - "pin-project-lite 0.2.7", + "pin-project-lite", "tokio", "tokio-util", ] @@ -538,29 +456,12 @@ dependencies = [ ] [[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom", - "serde", -] - -[[package]] name = "const_fn" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" [[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -573,9 +474,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" dependencies = [ "aes-gcm", - "base64 0.13.0", + "base64", "hkdf", - "hmac 0.10.1", + "hmac", "percent-encoding", "rand 0.8.4", "sha2", @@ -604,17 +505,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", + "cfg-if", ] [[package]] @@ -623,18 +514,17 @@ 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", ] [[package]] -name = "crypto-mac" -version = "0.8.0" +name = "crypto-common" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ "generic-array", - "subtle", ] [[package]] @@ -655,7 +545,7 @@ checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa", + "itoa 0.4.8", "matches", "phf", "proc-macro2", @@ -694,36 +584,12 @@ dependencies = [ ] [[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if 1.0.0", - "num_cpus", -] - -[[package]] name = "data-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" [[package]] -name = "deadpool" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d" -dependencies = [ - "async-trait", - "config", - "crossbeam-queue", - "num_cpus", - "serde", - "tokio", -] - -[[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -752,6 +618,16 @@ dependencies = [ ] [[package]] +name = "digest" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] name = "discard" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -793,15 +669,6 @@ dependencies = [ ] [[package]] -name = "encoding_rs" -version = "0.8.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] name = "env_logger" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -845,34 +712,18 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfc110fe50727d46a428eed832df40affe9bf74d077cac1bf3f2718e823f14c5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "windows-sys", ] [[package]] -name = "femme" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af1a24f391a5a94d756db5092c6576aad494b88a71a5a36b20c67b63e0df034" -dependencies = [ - "cfg-if 0.1.10", - "js-sys", - "log 0.4.14", - "serde", - "serde_derive", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[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", @@ -969,7 +820,7 @@ dependencies = [ "futures-io", "memchr 2.4.1", "parking", - "pin-project-lite 0.2.7", + "pin-project-lite", "waker-fn", ] @@ -1009,7 +860,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr 2.4.1", - "pin-project-lite 0.2.7", + "pin-project-lite", "pin-utils", "slab", ] @@ -1039,7 +890,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1050,7 +901,7 @@ 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", ] @@ -1079,6 +930,56 @@ dependencies = [ ] [[package]] +name = "h2" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1099,18 +1000,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" dependencies = [ - "digest", - "hmac 0.10.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac 0.8.0", - "digest", + "digest 0.9.0", + "hmac", ] [[package]] @@ -1119,8 +1010,8 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ - "crypto-mac 0.10.1", - "digest", + "crypto-mac", + "digest 0.9.0", ] [[package]] @@ -1138,22 +1029,25 @@ dependencies = [ ] [[package]] -name = "http-client" -version = "6.5.1" +name = "http" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" dependencies = [ - "async-h1", - "async-std", - "async-tls", - "async-trait", - "cfg-if 1.0.0", - "dashmap", - "deadpool", - "futures", - "http-types", - "log 0.4.14", - "rustls", + "bytes", + "fnv", + "itoa 1.0.1", +] + +[[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]] @@ -1165,11 +1059,11 @@ dependencies = [ "anyhow", "async-channel", "async-std", - "base64 0.13.0", + "base64", "cookie", "futures-lite", "infer", - "pin-project-lite 0.2.7", + "pin-project-lite", "rand 0.7.3", "serde", "serde_json", @@ -1180,9 +1074,15 @@ dependencies = [ [[package]] name = "httparse" -version = "1.5.1" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" + +[[package]] +name = "httpdate" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" @@ -1191,6 +1091,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] +name = "hyper" +version = "0.14.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.1", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] name = "idna" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1202,6 +1126,16 @@ dependencies = [ ] [[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] name = "infer" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1213,7 +1147,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1223,6 +1157,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] name = "js-sys" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1246,7 +1186,6 @@ name = "kittybox" version = "0.1.0" dependencies = [ "anyhow", - "async-std", "async-trait", "chrono", "data-encoding", @@ -1257,6 +1196,7 @@ dependencies = [ "futures", "futures-util", "http-types", + "hyper", "lazy_static", "log 0.4.14", "markdown", @@ -1273,12 +1213,11 @@ dependencies = [ "serde_json", "serde_urlencoded", "sha2", - "surf", "tempdir", "test-logger", - "tide", - "tide-testing", + "tokio", "url", + "warp", ] [[package]] @@ -1309,23 +1248,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if 1.0.0", - "ryu", - "static_assertions", -] - -[[package]] name = "libc" -version = "0.2.109" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" [[package]] name = "lock_api" @@ -1351,7 +1277,7 @@ 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", ] @@ -1378,7 +1304,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e56549270844a0e513d26db15562a783dd282e351baec8650c6f4c1bcaee54ef" dependencies = [ - "itoa", + "itoa 0.4.8", "markup-proc-macro", ] @@ -1455,6 +1381,28 @@ dependencies = [ ] [[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log 0.4.14", + "miow", + "ntapi", + "winapi 0.3.9", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] name = "mockito" version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1473,6 +1421,24 @@ dependencies = [ ] [[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "log 0.4.14", + "mime", + "mime_guess", + "quick-error", + "rand 0.8.4", + "safemem", + "tempfile", + "twoway", +] + +[[package]] name = "new_debug_unreachable" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1491,14 +1457,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] -name = "nom" -version = "5.1.2" +name = "ntapi" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ - "lexical-core", - "memchr 2.4.1", - "version_check", + "winapi 0.3.9", ] [[package]] @@ -1565,7 +1529,7 @@ 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", @@ -1641,18 +1605,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" dependencies = [ "proc-macro2", "quote", @@ -1661,12 +1625,6 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" @@ -1689,7 +1647,7 @@ 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 0.4.14", "wepoll-ffi", @@ -1754,7 +1712,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5986aa8d62380092d2f50f8b1cdba9cb9b6731ffd4b25b51fd126b6c3e05b99c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fnv", "lazy_static", "libc", @@ -1772,6 +1730,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" [[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" @@ -1914,15 +1878,14 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f23ceed4c0e76b322657c2c3352ea116f9ec60a1a1aefeb3c84ed062c50865b" dependencies = [ - "async-std", "async-trait", "bytes", "combine", "dtoa", "futures-util", - "itoa", + "itoa 0.4.8", "percent-encoding", - "pin-project-lite 0.2.7", + "pin-project-lite", "sha1", "tokio", "tokio-util", @@ -2002,27 +1965,6 @@ dependencies = [ ] [[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi 0.3.9", -] - -[[package]] -name = "route-recognizer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" - -[[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2041,39 +1983,28 @@ dependencies = [ ] [[package]] -name = "rustls" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" -dependencies = [ - "base64 0.12.3", - "log 0.4.14", - "ring", - "sct", - "webpki", -] - -[[package]] name = "ryu" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" [[package]] -name = "scopeguard" -version = "1.1.0" +name = "safemem" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] -name = "sct" -version = "0.6.1" +name = "scoped-tls" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "selectors" @@ -2142,7 +2073,7 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" dependencies = [ - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -2165,7 +2096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" dependencies = [ "form_urlencoded", - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -2181,6 +2112,17 @@ dependencies = [ ] [[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.2", +] + +[[package]] name = "sha1" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2192,24 +2134,14 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ - "block-buffer", - "cfg-if 1.0.0", + "block-buffer 0.9.0", + "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] [[package]] -name = "signal-hook" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] name = "signal-hook-registry" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2219,15 +2151,6 @@ dependencies = [ ] [[package]] -name = "simple-mutex" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" -dependencies = [ - "event-listener", -] - -[[package]] name = "siphasher" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2256,12 +2179,6 @@ dependencies = [ ] [[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2277,12 +2194,6 @@ dependencies = [ ] [[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] name = "stdweb" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2364,36 +2275,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] -name = "surf" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718b1ae6b50351982dedff021db0def601677f2120938b070eadb10ba4038dd7" -dependencies = [ - "async-std", - "async-trait", - "cfg-if 1.0.0", - "encoding_rs", - "futures-util", - "getrandom 0.2.3", - "http-client", - "http-types", - "log 0.4.14", - "mime_guess", - "once_cell", - "pin-project-lite 0.2.7", - "rustls", - "serde", - "serde_json", - "web-sys", -] - -[[package]] -name = "sval" -version = "1.0.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" - -[[package]] name = "syn" version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2415,6 +2296,20 @@ dependencies = [ ] [[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand 0.8.4", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] name = "tendril" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2489,41 +2384,6 @@ dependencies = [ ] [[package]] -name = "tide" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c459573f0dd2cc734b539047f57489ea875af8ee950860ded20cf93a79a1dee0" -dependencies = [ - "async-h1", - "async-session", - "async-sse", - "async-std", - "async-trait", - "femme", - "futures-util", - "http-client", - "http-types", - "kv-log-macro", - "log 0.4.14", - "pin-project-lite 0.2.7", - "route-recognizer", - "serde", - "serde_json", -] - -[[package]] -name = "tide-testing" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a59ea33dec6d205e4173cf7825dcfc78600c1726d931132d99b38b932495111" -dependencies = [ - "serde", - "serde_json", - "surf", - "tide", -] - -[[package]] name = "time" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2589,14 +2449,43 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.14.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" +checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" dependencies = [ - "autocfg", "bytes", + "libc", "memchr 2.4.1", - "pin-project-lite 0.2.7", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] @@ -2609,11 +2498,53 @@ dependencies = [ "futures-core", "futures-sink", "log 0.4.14", - "pin-project-lite 0.2.7", + "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.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" +dependencies = [ + "cfg-if", + "log 0.4.14", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr 2.4.1", +] + +[[package]] name = "typenum" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2666,12 +2597,6 @@ dependencies = [ ] [[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] name = "url" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2703,7 +2628,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" dependencies = [ "ctor", - "sval", "version_check", ] @@ -2720,6 +2644,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log 0.4.14", + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" +dependencies = [ + "async-compression", + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log 0.4.14", + "mime", + "mime_guess", + "multipart", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-util", + "tower-service", + "tracing", +] + +[[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2737,9 +2701,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ - "cfg-if 1.0.0", - "serde", - "serde_json", + "cfg-if", "wasm-bindgen-macro", ] @@ -2764,7 +2726,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", @@ -2810,25 +2772,6 @@ dependencies = [ ] [[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" -dependencies = [ - "webpki", -] - -[[package]] name = "wepoll-ffi" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 44115df..8b43c63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Vika <vika@fireburn.ru>"] edition = "2021" [features] -default = ["util"] +default = [] util = ["anyhow"] [[bin]] @@ -26,7 +26,6 @@ required-features = ["util", "redis"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dev-dependencies] -tide-testing = "^0.1.3" # tide testing helper mockito = "^0.30.0" # HTTP mocking for Rust. tempdir = "^0.3.7" # A library for managing a temporary directory and deleting all contents when it's dropped paste = "^1.0.5" # Macros for all your token pasting needs @@ -51,32 +50,34 @@ rand = "^0.8.4" # Random number generators. retainer = "^0.2.2" # Minimal async cache in Rust with support for key expirations serde_json = "^1.0.64" # A JSON serialization file format serde_urlencoded = "^0.7.0" # `x-www-form-urlencoded` meets Serde -tide = "^0.16.0" # A minimal and pragmatic Rust web application framework built for rapid development relative-path = "^1.5.0" # Portable relative paths for Rust sha2 = "^0.9.8" # SHA-2 series of algorithms for Rust +[dependencies.tokio] +version = "^1.16.1" +features = ["full"] # TODO determine if my app doesn't need some features [dependencies.anyhow] version = "^1.0.42" optional = true -[dependencies.async-std] # Async version of the Rust standard library -version = "^1.9.0" -features = ["attributes", "unstable"] [dependencies.chrono] # Date and time library for Rust version = "^0.4.19" features = ["serde"] [dependencies.redis] version = "^0.21.3" optional = true -features = ["aio", "async-std-comp"] +features = ["aio", "tokio-comp"] [dependencies.prometheus] # Prometheus instrumentation library for Rust applications version = "^0.12.0" features = ["process"] [dependencies.serde] # A generic serialization/deserialization framework version = "^1.0.125" features = ["derive"] -[dependencies.surf] # Surf the web - HTTP client framework -version = "^2.2.0" -default-features = false -features = ["h1-client-rustls", "encoding", "middleware-logger"] [dependencies.url] # URL library for Rust, based on the WHATWG URL Standard version = "^2.2.1" features = ["serde"] +[dependencies.warp] +version = "^0.3.2" +default-features = false +features = ["multipart", "compression"] +[dependencies.hyper] +version = "^0.14.17" +features = ["client", "stream", "runtime"] \ No newline at end of file diff --git a/dev.sh b/dev.sh index 1faca15..6ebbd2a 100755 --- a/dev.sh +++ b/dev.sh @@ -1,5 +1,5 @@ #!/bin/sh -export RUST_LOG="debug,retainer::cache=warn,html5ever=info" +export RUST_LOG="debug,retainer::cache=warn,html5ever=info,hyper::proto=info" export BACKEND_URI=file://./test-dir export TOKEN_ENDPOINT=https://token.indieauth.com/token export AUTHORIZATION_ENDPOINT=https://indieauth.com/auth diff --git a/src/database/file/mod.rs b/src/database/file/mod.rs index 3717023..6cbe3c6 100644 --- a/src/database/file/mod.rs +++ b/src/database/file/mod.rs @@ -2,11 +2,10 @@ use crate::database::{filter_post, ErrorKind, Result, Storage, StorageError}; use std::fs::{File, OpenOptions}; use std::io::{ErrorKind as IOErrorKind, Seek, SeekFrom, Read, Write}; use std::time::Duration; -use async_std::future::TimeoutError; -use async_std::task::spawn_blocking; +use tokio::task::spawn_blocking; use async_trait::async_trait; use fd_lock::RwLock; -use futures::stream; +use futures_util::stream; use futures_util::StreamExt; use futures_util::TryStreamExt; use log::debug; @@ -27,8 +26,8 @@ impl From<std::io::Error> for StorageError { } } -impl From<TimeoutError> for StorageError { - fn from(source: TimeoutError) -> Self { +impl From<tokio::time::error::Elapsed> for StorageError { + fn from(source: tokio::time::error::Elapsed) -> Self { Self::with_source( ErrorKind::Backend, "timeout on I/O operation", @@ -259,14 +258,14 @@ impl Storage for FileStorage { async fn post_exists(&self, url: &str) -> Result<bool> { let path = url_to_path(&self.root_dir, url); debug!("Checking if {:?} exists...", path); - Ok(spawn_blocking(move || path.is_file()).await) + Ok(spawn_blocking(move || path.is_file()).await.unwrap()) } async fn get_post(&self, url: &str) -> Result<Option<serde_json::Value>> { let path = url_to_path(&self.root_dir, url); debug!("Opening {:?}", path); // Use exclusively synchronous operations to never transfer a lock over an await boundary - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { match File::open(&path) { Ok(file) => { let lock = RwLock::new(file); @@ -289,7 +288,7 @@ impl Storage for FileStorage { } } } - })).await? + })).await?.unwrap() } async fn put_post<'a>(&self, post: &'a serde_json::Value, user: &'a str) -> Result<()> { @@ -303,7 +302,7 @@ impl Storage for FileStorage { let post_json = post.to_string(); let post_path = path.clone(); // Use exclusively synchronous operations to never transfer a lock over an await boundary - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { let parent = post_path.parent().unwrap().to_owned(); if !parent.is_dir() { std::fs::create_dir_all(post_path.parent().unwrap())?; @@ -323,7 +322,7 @@ impl Storage for FileStorage { drop(guard); Result::Ok(()) - })).await??; + })).await?.unwrap()?; if post["properties"]["url"].is_array() { for url in post["properties"]["url"] @@ -345,7 +344,7 @@ impl Storage for FileStorage { })?; let relative = path_relative_from(&orig, basedir).unwrap(); println!("{:?} - {:?} = {:?}", &orig, &basedir, &relative); - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { println!("Created a symlink at {:?}", &link); let symlink_result; #[cfg(unix)] @@ -362,7 +361,7 @@ impl Storage for FileStorage { } else { Result::Ok(()) } - })).await??; + })).await?.unwrap()?; } } } @@ -386,7 +385,7 @@ impl Storage for FileStorage { .unwrap_or_else(String::default); let key = key.to_string(); drop(post); - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { let file = OpenOptions::new() .read(true) .write(true) @@ -417,15 +416,15 @@ impl Storage for FileStorage { (*guard).write_all(serde_json::to_string(&channels)?.as_bytes())?; Result::Ok(()) - })).await??; + })).await?.unwrap()?; } Ok(()) } async fn update_post<'a>(&self, url: &'a str, update: serde_json::Value) -> Result<()> { let path = url_to_path(&self.root_dir, url); - - let (old_json, new_json) = async_std::future::timeout( + #[allow(unused_variables)] + let (old_json, new_json) = tokio::time::timeout( Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { let f = OpenOptions::new() @@ -450,7 +449,7 @@ impl Storage for FileStorage { Result::Ok((json, new_json)) }) - ).await??; + ).await?.unwrap()?; // TODO check if URLs changed between old and new JSON Ok(()) } @@ -461,7 +460,7 @@ impl Storage for FileStorage { path.push("channels"); let path = path.to_path(&self.root_dir); - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { match File::open(&path) { Ok(f) => { let lock = RwLock::new(f); @@ -484,7 +483,7 @@ impl Storage for FileStorage { } } } - })).await? + })).await?.unwrap() } async fn read_feed_with_limit<'a>( @@ -548,7 +547,7 @@ impl Storage for FileStorage { async fn delete_post<'a>(&self, url: &'a str) -> Result<()> { let path = url_to_path(&self.root_dir, url); - if let Err(e) = async_std::fs::remove_file(path).await { + if let Err(e) = tokio::fs::remove_file(path).await { Err(e.into()) } else { // TODO check for dangling references in the channel list @@ -565,7 +564,7 @@ impl Storage for FileStorage { let path = path.to_path(&self.root_dir); let setting = setting.to_string(); - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { let lock = RwLock::new(File::open(path)?); let guard = lock.read()?; @@ -579,7 +578,7 @@ impl Storage for FileStorage { .get(&setting) .cloned() .ok_or_else(|| StorageError::new(ErrorKind::Backend, "Setting not set")) - })).await? + })).await?.unwrap() } async fn set_setting<'a>(&self, setting: &'a str, user: &'a str, value: &'a str) -> Result<()> { @@ -591,13 +590,13 @@ impl Storage for FileStorage { let path = path.to_path(&self.root_dir); let parent = path.parent().unwrap().to_owned(); - if !spawn_blocking(move || parent.is_dir()).await { - async_std::fs::create_dir_all(path.parent().unwrap()).await?; + if !spawn_blocking(move || parent.is_dir()).await.unwrap() { + tokio::fs::create_dir_all(path.parent().unwrap()).await?; } let (setting, value) = (setting.to_string(), value.to_string()); - async_std::future::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { + tokio::time::timeout(Duration::from_secs(IO_TIMEOUT), spawn_blocking(move || { let file = OpenOptions::new() .write(true) .read(true) @@ -622,6 +621,6 @@ impl Storage for FileStorage { (&mut *guard).set_len(0)?; (&mut *guard).write_all(serde_json::to_string(&settings)?.as_bytes())?; Result::Ok(()) - })).await? + })).await?.unwrap() } } diff --git a/src/database/mod.rs b/src/database/mod.rs index c0f9f29..55ab027 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -2,13 +2,6 @@ use async_trait::async_trait; use serde::{Deserialize, Serialize}; -//#[cfg(feature="redis")] -//mod redis; -//#[cfg(feature="redis")] -//pub use crate::database::redis::RedisStorage; -//#[cfg(all(redis, test))] -//pub use redis::tests::{get_redis_instance, RedisInstance}; - mod file; pub use crate::database::file::FileStorage; @@ -49,7 +42,7 @@ pub struct StorageError { kind: ErrorKind, } -impl From<StorageError> for tide::Response { +/*impl From<StorageError> for tide::Response { fn from(err: StorageError) -> Self { tide::Response::builder(match err.kind() { ErrorKind::BadRequest => 400, @@ -66,7 +59,8 @@ impl From<StorageError> for tide::Response { })) .build() } -} +}*/ + impl std::error::Error for StorageError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.source @@ -431,24 +425,7 @@ mod tests { ); } - /*macro_rules! redis_test { - ($func_name:expr) => { - paste! { - #[cfg(feature="redis")] - #[async_std::test] - async fn [<redis_ $func_name>] () { - test_logger::ensure_env_logger_initialized(); - let redis_instance = get_redis_instance().await; - let backend = super::RedisStorage::new(redis_instance.uri().to_string()) - .await - .unwrap(); - $func_name(backend).await - } - } - } - }*/ - - macro_rules! file_test { + /*macro_rules! file_test { ($func_name:expr) => { paste! { #[async_std::test] @@ -461,13 +438,10 @@ mod tests { } }; } - - /*redis_test!(test_backend_basic_operations); - redis_test!(test_backend_get_channel_list); - redis_test!(test_backend_settings); - redis_test!(test_backend_update);*/ + file_test!(test_backend_basic_operations); file_test!(test_backend_get_channel_list); file_test!(test_backend_settings); file_test!(test_backend_update); + */ } diff --git a/src/lib.rs b/src/lib.rs index 2b4d1cc..2585227 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,87 +1,172 @@ -use tide::{Request, Response}; - -/// Database abstraction layer for Kittybox, allowing the CMS to work with any kind of database. -pub mod database; +//use tide::{Request, Response}; +use warp::Filter; +/*pub mod database; mod frontend; mod indieauth; -mod metrics; -mod micropub; +mod micropub;*/ +pub mod metrics; +/// Database abstraction layer for Kittybox, allowing the CMS to work with any kind of database. +pub mod database; +pub mod micropub; +//pub mod indieauth; -use crate::indieauth::IndieAuthMiddleware; -use crate::micropub::CORSMiddleware; +/*use crate::indieauth::IndieAuthMiddleware; +use crate::micropub::CORSMiddleware;*/ -#[derive(Clone)] -pub struct ApplicationState<StorageBackend> -where - StorageBackend: database::Storage + Send + Sync + 'static, -{ - token_endpoint: surf::Url, - authorization_endpoint: surf::Url, - media_endpoint: Option<String>, - internal_token: Option<String>, - cookie_secret: String, - http_client: surf::Client, - storage: StorageBackend, +pub mod rejections { + #[derive(Debug)] + pub struct UnacceptableContentType; + impl warp::reject::Reject for UnacceptableContentType {} + + #[derive(Debug)] + pub struct HostHeaderUnset; + impl warp::reject::Reject for HostHeaderUnset {} } -type App<Storage> = tide::Server<ApplicationState<Storage>>; - -static MICROPUB_CLIENT: &[u8] = include_bytes!("./index.html"); - -fn equip_app<Storage>(mut app: App<Storage>) -> App<Storage> -where - Storage: database::Storage + Send + Sync + Clone, -{ - app.at("/micropub") - .with(CORSMiddleware {}) - .with(IndieAuthMiddleware::new()) - .get(micropub::get_handler) - .post(micropub::post_handler); - // The Micropub client. It'll start small, but could grow into something full-featured - app.at("/micropub/client").get(|_: Request<_>| async move { - Ok(Response::builder(200) - .body(MICROPUB_CLIENT) - .content_type("text/html") - .build()) - }); - app.at("/") - .with(CORSMiddleware {}) - .with(frontend::ErrorHandlerMiddleware {}) - .get(frontend::mainpage) - .post(frontend::onboarding_receiver); - app.at("/login") - .with(frontend::ErrorHandlerMiddleware {}) - .get(frontend::login::form) - .post(frontend::login::handler); - app.at("/login/callback") - .with(frontend::ErrorHandlerMiddleware {}) - .get(frontend::login::callback); - app.at("/static/*path") - .with(frontend::ErrorHandlerMiddleware {}) - .get(frontend::handle_static); - app.at("/*path") - .with(frontend::ErrorHandlerMiddleware {}) - .get(frontend::render_post); - app.at("/coffee") - .with(frontend::ErrorHandlerMiddleware {}) - .get(frontend::coffee); - // TODO make sure the health check actually checks the backend or something - // otherwise it'll get false-negatives for application faults like resource - // exhaustion - app.at("/health").get(|_| async { Ok("OK") }); - app.at("/metrics").get(metrics::gather); - - app.with(metrics::InstrumentationMiddleware {}); - app.with( - tide::sessions::SessionMiddleware::new( - tide::sessions::CookieStore::new(), - app.state().cookie_secret.as_bytes(), - ) - .with_cookie_name("kittybox_session") - .without_save_unchanged(), - ); - app +pub static MICROPUB_CLIENT: &[u8] = include_bytes!("./index.html"); + +pub mod util { + use warp::{Filter, host::Authority}; + use super::rejections; + + pub fn require_host() -> impl Filter<Extract = (Authority,), Error = warp::Rejection> + Copy { + warp::host::optional() + .and_then(|authority: Option<Authority>| async move { + authority.ok_or_else(|| warp::reject::custom(rejections::HostHeaderUnset)) + }) + } + + pub fn template<R>( + template: R + ) -> impl warp::Reply + where + R: markup::Render + std::fmt::Display + { + warp::reply::html(template.to_string()) + } + + pub fn parse_accept() -> impl Filter<Extract = (http_types::Mime,), Error = warp::Rejection> + Copy { + warp::header::value("Accept").and_then(|accept: warp::http::HeaderValue| async move { + let mut accept: http_types::content::Accept = { + // This is unneccesarily complicated because I want to reuse some http-types parsing + // and http-types has constructor for Headers private so I need to construct + // a mock Request to reason about headers... this is so dumb wtf + let bytes: &[u8] = accept.as_bytes(); + let value = http_types::headers::HeaderValue::from_bytes(bytes.to_vec()).unwrap(); + let values: http_types::headers::HeaderValues = vec![value].into(); + let mut request = http_types::Request::new(http_types::Method::Get, "http://example.com/"); + request.append_header("Accept".parse::<http_types::headers::HeaderName>().unwrap(), &values); + http_types::content::Accept::from_headers(&request).unwrap().unwrap() + }; + + // This code is INCREDIBLY dumb, honestly... + // why did I even try to use it? + // TODO vendor this stuff in so I can customize it + match accept.negotiate(&[ + "text/html; encoding=\"utf-8\"".into(), + "application/json; encoding=\"utf-8\"".into(), + "text/html".into(), + "application/json".into(), + + ]) { + Ok(mime) => { + Ok(http_types::Mime::from(mime.value().as_str())) + }, + Err(err) => { + log::error!("Content-Type negotiation error: {:?}, accepting: {:?}", err, accept); + Err(warp::reject::custom(rejections::UnacceptableContentType)) + } + } + }) + } + + mod tests { + #[tokio::test] + async fn test_require_host_with_host() { + use super::require_host; + + let filter = require_host(); + + let res = warp::test::request() + .path("/") + .header("Host", "localhost:8080") + .filter(&filter) + .await + .unwrap(); + + assert_eq!(res, "localhost:8080"); + + } + + #[tokio::test] + async fn test_require_host_no_host() { + use super::require_host; + + let filter = require_host(); + + let res = warp::test::request() + .path("/") + .filter(&filter) + .await; + + assert!(res.is_err()); + } + } } +// fn equip_app<Storage>(mut app: App<Storage>) -> App<Storage> +// where +// Storage: database::Storage + Send + Sync + Clone, +// { +// app.at("/micropub") +// .with(CORSMiddleware {}) +// .with(IndieAuthMiddleware::new()) +// .get(micropub::get_handler) +// .post(micropub::post_handler); +// // The Micropub client. It'll start small, but could grow into something full-featured +// app.at("/micropub/client").get(|_: Request<_>| async move { +// Ok(Response::builder(200) +// .body(MICROPUB_CLIENT) +// .content_type("text/html") +// .build()) +// }); +// app.at("/") +// .with(CORSMiddleware {}) +// .with(frontend::ErrorHandlerMiddleware {}) +// .get(frontend::mainpage) +// .post(frontend::onboarding_receiver); +// app.at("/login") +// .with(frontend::ErrorHandlerMiddleware {}) +// .get(frontend::login::form) +// .post(frontend::login::handler); +// app.at("/login/callback") +// .with(frontend::ErrorHandlerMiddleware {}) +// .get(frontend::login::callback); +// app.at("/static/*path") +// .with(frontend::ErrorHandlerMiddleware {}) +// .get(frontend::handle_static); +// app.at("/*path") +// .with(frontend::ErrorHandlerMiddleware {}) +// .get(frontend::render_post); +// app.at("/coffee") +// .with(frontend::ErrorHandlerMiddleware {}) +// .get(frontend::coffee); +// // TODO make sure the health check actually checks the backend or something +// // otherwise it'll get false-negatives for application faults like resource +// // exhaustion +// app.at("/health").get(|_| async { Ok("OK") }); +// app.at("/metrics").get(metrics::gather); + +// app.with(metrics::InstrumentationMiddleware {}); +// app.with( +// tide::sessions::SessionMiddleware::new( +// tide::sessions::CookieStore::new(), +// app.state().cookie_secret.as_bytes(), +// ) +// .with_cookie_name("kittybox_session") +// .without_save_unchanged(), +// ); +// app +// } /*#[cfg(feature="redis")] pub async fn get_app_with_redis( @@ -103,30 +188,7 @@ pub async fn get_app_with_redis( equip_app(app) }*/ -pub async fn get_app_with_file( - token_endpoint: surf::Url, - authorization_endpoint: surf::Url, - backend_uri: String, - media_endpoint: Option<String>, - cookie_secret: String, - internal_token: Option<String>, -) -> App<database::FileStorage> { - let folder = backend_uri.strip_prefix("file://").unwrap(); - let path = std::path::PathBuf::from(folder); - let app = tide::with_state(ApplicationState { - token_endpoint, - media_endpoint, - authorization_endpoint, - internal_token, - cookie_secret, - storage: database::FileStorage::new(path).await.unwrap(), - http_client: surf::Client::new(), - }); - - equip_app(app) -} - -#[cfg(test)] +/*#[cfg(test)] pub async fn get_app_with_test_file( token_endpoint: surf::Url, ) -> ( @@ -151,7 +213,7 @@ pub async fn get_app_with_test_file( (tempdir, backend, equip_app(app)) } -/*#[cfg(all(redis, test))] +#[cfg(all(redis, test))] pub async fn get_app_with_test_redis( token_endpoint: surf::Url, ) -> ( @@ -176,7 +238,7 @@ pub async fn get_app_with_test_redis( (redis_instance, backend, equip_app(app)) }*/ -#[cfg(test)] +/*#[cfg(test)] #[allow(unused_variables)] mod tests { use super::*; @@ -459,4 +521,4 @@ mod tests { assert_eq!(new_feed["children"][0].as_str().unwrap(), uid); assert_eq!(new_feed["children"][1].as_str().unwrap(), first_uid); } -} +}*/ diff --git a/src/main.rs b/src/main.rs index 79e0cf5..4036d46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ use log::{debug, error, info}; use std::env; -use surf::Url; +use http_types::Url; +use warp::{Filter, host::Authority, path::FullPath}; -#[async_std::main] -async fn main() -> Result<(), std::io::Error> { +#[tokio::main] +async fn main() -> Result<(), kittybox::database::StorageError> { // TODO json logging in the future? let logger_env = env_logger::Env::new().filter_or("RUST_LOG", "info"); env_logger::init_from_env(logger_env); @@ -64,13 +65,14 @@ async fn main() -> Result<(), std::io::Error> { Some(value) => value, None => { if let Ok(filename) = env::var("COOKIE_SECRET_FILE") { - use async_std::io::ReadExt; + /*use async_std::io::ReadExt; let mut file = async_std::fs::File::open(filename).await?; let mut temp_string = String::new(); file.read_to_string(&mut temp_string).await?; - temp_string + temp_string*/ + todo!() } else { error!("COOKIE_SECRET or COOKIE_SECRET_FILE is not set, will not be able to log in users securely!"); std::process::exit(1); @@ -78,24 +80,106 @@ async fn main() -> Result<(), std::io::Error> { } }; - let host = env::var("SERVE_AT") + let host: std::net::SocketAddr = match env::var("SERVE_AT") .ok() - .unwrap_or_else(|| "0.0.0.0:8080".to_string()); + .unwrap_or_else(|| "0.0.0.0:8080".to_string()) + .parse() { + Ok(addr) => addr, + Err(e) => { + error!("Cannot parse SERVE_AT: {}", e); + std::process::exit(1); + } + }; if backend_uri.starts_with("redis") { println!("The Redis backend is deprecated."); std::process::exit(1); } else if backend_uri.starts_with("file") { - let app = kittybox::get_app_with_file( - token_endpoint, - authorization_endpoint, - backend_uri, - media_endpoint, - cookie_secret, - internal_token, - ) - .await; - app.listen(host).await + + let database = { + let folder = backend_uri.strip_prefix("file://").unwrap(); + let path = std::path::PathBuf::from(folder); + kittybox::database::FileStorage::new(path).await? + }; + + // TODO interpret HEAD + let homepage = kittybox::util::require_host() + .and(warp::get()) + .and(warp::path::end()) + // TODO fetch content from the database + // TODO parse content-type and determine appropriate response + .map(|host| format!("front page for {}!", host)); + + let micropub = warp::path("micropub") + .and(warp::path::end() + .and(warp::get() + .and(kittybox::micropub::query(database)) + .or(warp::post() + .and(kittybox::util::require_host()) + .map(|host| "micropub post!")) + .or(warp::options() + .map(|| warp::reply::json::<Option<()>>(&None)) + // TODO: why doesn't this work? + // .map(warp::reply::with::header("Allow", "GET, POST")) + .map(|reply| warp::reply::with_header(reply, "Allow", "GET, POST")) + )) + .or(warp::get() + .and(warp::path("client")) + .and(warp::path::end()) + .map(|| kittybox::MICROPUB_CLIENT))); + + let media = warp::path("media") + .and(warp::path::end() + .and(kittybox::util::require_host()) + .map(|host| "media endpoint?...") + .or(kittybox::util::require_host() + .and(warp::path::param()) + .map(|host: Authority, path: String| format!("media file {}", path)))); + + // TODO remember how login logic works because I forgor + let login = warp::path("login") + .and(warp::path("callback") + .map(|| "callback!") + // TODO form on GET and handler on POST + .or(warp::path::end().map(|| "login page!"))); + + // TODO prettier error response + let coffee = warp::path("coffee") + .map(|| warp::reply::with_status("I'm a teapot!", warp::http::StatusCode::IM_A_TEAPOT)); + + // TODO interpret HEAD + let static_files = warp::get() + .and(warp::path!("static" / String)) + .map(|path| path); + + // TODO interpret HEAD + let catchall = warp::get() + .and(kittybox::util::require_host()) + .and(warp::path::full()) + .map(|host: Authority, path: FullPath| host.to_string() + path.as_str() + ".json") + // TODO fetch content from the database + // TODO parse content-type and determine appropriate response + ; + + let health = warp::path("health").and(warp::path::end()).map(|| "OK"); + // TODO instrumentation middleware (see metrics.rs for comments) + //let metrics = warp::path("metrics").and(warp::path::end()).map(kittybox::metrics::gather); + let app = homepage + .or(login) + .or(static_files) + .or(coffee) + .or(health) + .or(micropub) + .or(media) + .or(catchall) + ; + + let server = warp::serve(app); + + // TODO use warp::Server::bind_with_graceful_shutdown + info!("Listening on {:?}", host); + server.bind(host).await; + Ok(()) } else { println!("Unknown backend, not starting."); std::process::exit(1); diff --git a/src/metrics.rs b/src/metrics.rs index 9f512dd..7bfa2d2 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -1,3 +1,4 @@ +#![allow(unused_imports, dead_code)] use async_trait::async_trait; use lazy_static::lazy_static; use prometheus::{ @@ -5,7 +6,7 @@ use prometheus::{ TextEncoder, }; use std::time::{Duration, Instant}; -use tide::{Next, Request, Response, Result}; +//use tide::{Next, Request, Response, Result}; // Copied from https://docs.rs/prometheus/0.12.0/src/prometheus/histogram.rs.html#885-889 #[inline] @@ -29,7 +30,7 @@ lazy_static! { .unwrap(); } -pub struct InstrumentationMiddleware {} +/*pub struct InstrumentationMiddleware {} #[async_trait] impl<S> tide::Middleware<S> for InstrumentationMiddleware @@ -55,9 +56,9 @@ where Ok(res) } -} +}*/ -pub async fn gather<S>(_: Request<S>) -> Result +/*pub async fn gather<S>(_: Request<S>) -> Result where S: Send + Sync + Clone, { @@ -67,4 +68,18 @@ where encoder.encode(&metric_families, &mut buffer).unwrap(); Ok(Response::builder(200).body(buffer).build()) +}*/ + +// TODO metrics middleware +// warp doesn't allow running a filter manually +// so you need to escape into the world of hyper +// to collect metrics on requests + +pub fn gather() -> Vec<u8> { + let mut buffer: Vec<u8> = vec![]; + let encoder = TextEncoder::new(); + let metric_families = prometheus::gather(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + + buffer } diff --git a/src/micropub/mod.rs b/src/micropub/mod.rs index 23f20c4..95595cf 100644 --- a/src/micropub/mod.rs +++ b/src/micropub/mod.rs @@ -1,31 +1,101 @@ -pub mod get; -pub mod post; - -pub use get::get_handler; -pub use post::normalize_mf2; -pub use post::post_handler; - -pub struct CORSMiddleware {} - -use crate::database; -use crate::ApplicationState; -use async_trait::async_trait; -use tide::{Next, Request, Result}; - -#[async_trait] -impl<B> tide::Middleware<ApplicationState<B>> for CORSMiddleware -where - B: database::Storage + Send + Sync + Clone, -{ - async fn handle( - &self, - req: Request<ApplicationState<B>>, - next: Next<'_, ApplicationState<B>>, - ) -> Result { - let mut res = next.run(req).await; - - res.insert_header("Access-Control-Allow-Origin", "*"); - - Ok(res) +use warp::http::StatusCode; +use warp::{Filter, Rejection, reject::InvalidQuery}; +use serde_json::{json, Value}; +use serde::{Serialize, Deserialize}; +use crate::database::{MicropubChannel, Storage}; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +enum QueryType { + Source, + Config, + Channel, + SyndicateTo +} + +#[derive(Serialize, Deserialize)] +struct MicropubQuery { + q: QueryType, + url: Option<String> +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum ErrorType { + InvalidRequest, + InternalServerError +} + +#[derive(Serialize, Deserialize)] +struct MicropubError { + error: ErrorType, + error_description: String +} + +impl From<MicropubError> for StatusCode { + fn from(err: MicropubError) -> Self { + match err.error { + ErrorType::InvalidRequest => StatusCode::BAD_REQUEST, + ErrorType::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR + } + } +} + +impl MicropubError { + fn new(error: ErrorType, error_description: &str) -> Self { + Self { + error, + error_description: error_description.to_owned() + } } } + +pub fn query<D: Storage>(db: D) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { + warp::get() + .map(move || db.clone()) + .and(crate::util::require_host()) + .and(warp::query::<MicropubQuery>()) + .then(|db: D, host: warp::host::Authority, query: MicropubQuery| async move { + match query.q { + QueryType::Config => { + let channels: Vec<MicropubChannel> = match db.get_channels(host.as_str()).await { + Ok(chans) => chans, + Err(err) => return warp::reply::json(&MicropubError::new( + ErrorType::InternalServerError, + &format!("Error fetching channels: {}", err) + )) + }; + + warp::reply::json(json!({ + "q": [ + QueryType::Source, + QueryType::Config, + QueryType::Channel, + QueryType::SyndicateTo + ], + "channels": channels, + "_kittybox_authority": host.as_str() + }).as_object().unwrap()) + }, + _ => { + todo!() + } + } + }) + .recover(|err: Rejection| async move { + let error = if let Some(_) = err.find::<InvalidQuery>() { + MicropubError::new( + ErrorType::InvalidRequest, + "Invalid query parameters sent. Try ?q=config to see what you can do." + ) + } else { + log::error!("Unhandled rejection: {:?}", err); + MicropubError::new( + ErrorType::InternalServerError, + &format!("Unknown error: {:?}", err) + ) + }; + + Ok(warp::reply::with_status(warp::reply::json(&error), error.into())) + }) +} |