about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock535
-rw-r--r--Cargo.toml8
-rw-r--r--flake.nix10
-rw-r--r--src/indieauth.rs446
-rw-r--r--src/lib.rs10
5 files changed, 798 insertions, 211 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1b054ff..6694e2f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -102,6 +102,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
 
 [[package]]
+name = "ascii-canvas"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6"
+dependencies = [
+ "term",
+]
+
+[[package]]
 name = "assert-json-diff"
 version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -204,6 +213,32 @@ dependencies = [
 ]
 
 [[package]]
+name = "async-object-pool"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aeb901c30ebc2fc4ab46395bbfbdba9542c16559d853645d75190c3056caf3bc"
+dependencies = [
+ "async-std",
+]
+
+[[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",
+ "event-listener",
+ "futures-lite",
+ "libc",
+ "once_cell",
+ "signal-hook",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "async-std"
 version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -213,6 +248,7 @@ dependencies = [
  "async-global-executor",
  "async-io",
  "async-lock",
+ "async-process",
  "crossbeam-utils",
  "futures-channel",
  "futures-core",
@@ -294,6 +330,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
 [[package]]
+name = "basic-cookies"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38"
+dependencies = [
+ "lalrpop",
+ "lalrpop-util",
+ "regex 1.5.4",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
 name = "bitflags"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -387,6 +449,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
 
 [[package]]
+name = "castaway"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
+
+[[package]]
 name = "cc"
 version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -519,6 +587,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
 name = "crypto-common"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -584,6 +658,37 @@ dependencies = [
 ]
 
 [[package]]
+name = "curl"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7de97b894edd5b5bcceef8b78d7da9b75b1d2f2f9a910569d0bde3dd31d84939"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.52+curl-7.81.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b8c2d1023ea5fded5b7b892e4b8e95f70038a421126a056761a84246a28971"
+dependencies = [
+ "cc",
+ "libc",
+ "libnghttp2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "data-encoding"
 version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -603,6 +708,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "diff"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
+
+[[package]]
 name = "difference"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -628,6 +739,27 @@ dependencies = [
 ]
 
 [[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "discard"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -660,6 +792,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
+[[package]]
 name = "ellipse"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -669,6 +807,24 @@ dependencies = [
 ]
 
 [[package]]
+name = "ena"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
+dependencies = [
+ "log 0.4.14",
+]
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
 name = "env_logger"
 version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -718,6 +874,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "fixedbitset"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
+
+[[package]]
 name = "flate2"
 version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1085,6 +1247,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
 
 [[package]]
+name = "httpmock"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c159c4fc205e6c1a9b325cb7ec135d13b5f47188ce175dabb76ec847f331d9bd"
+dependencies = [
+ "assert-json-diff",
+ "async-object-pool",
+ "async-trait",
+ "base64",
+ "basic-cookies",
+ "crossbeam-utils",
+ "form_urlencoded",
+ "futures-util",
+ "hyper",
+ "isahc",
+ "lazy_static",
+ "levenshtein",
+ "log 0.4.14",
+ "regex 1.5.4",
+ "serde",
+ "serde_json",
+ "serde_regex",
+ "similar",
+ "tokio",
+ "url",
+]
+
+[[package]]
 name = "humantime"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1115,6 +1305,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "hyper-rustls"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
+dependencies = [
+ "http",
+ "hyper",
+ "log 0.4.14",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+ "webpki-roots",
+]
+
+[[package]]
 name = "idna"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1151,6 +1356,42 @@ dependencies = [
 ]
 
 [[package]]
+name = "isahc"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d140e84730d325378912ede32d7cd53ef1542725503b3353e5ec8113c7c6f588"
+dependencies = [
+ "async-channel",
+ "castaway",
+ "crossbeam-utils",
+ "curl",
+ "curl-sys",
+ "encoding_rs",
+ "event-listener",
+ "futures-lite",
+ "http",
+ "log 0.4.14",
+ "mime",
+ "once_cell",
+ "polling",
+ "slab",
+ "sluice",
+ "tracing",
+ "tracing-futures",
+ "url",
+ "waker-fn",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+dependencies = [
+ "either",
+]
+
+[[package]]
 name = "itoa"
 version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1187,6 +1428,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-trait",
+ "bytes",
  "chrono",
  "data-encoding",
  "easy-scraper",
@@ -1196,7 +1438,9 @@ dependencies = [
  "futures",
  "futures-util",
  "http-types",
+ "httpmock",
  "hyper",
+ "hyper-rustls",
  "lazy_static",
  "log 0.4.14",
  "markdown",
@@ -1242,18 +1486,78 @@ dependencies = [
 ]
 
 [[package]]
+name = "lalrpop"
+version = "0.19.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852b75a095da6b69da8c5557731c3afd06525d4f655a4fc1c799e2ec8bc4dce4"
+dependencies = [
+ "ascii-canvas",
+ "atty",
+ "bit-set",
+ "diff",
+ "ena",
+ "itertools",
+ "lalrpop-util",
+ "petgraph",
+ "pico-args",
+ "regex 1.5.4",
+ "regex-syntax 0.6.25",
+ "string_cache",
+ "term",
+ "tiny-keccak",
+ "unicode-xid",
+]
+
+[[package]]
+name = "lalrpop-util"
+version = "0.19.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6d265705249fe209280676d8f68887859fa42e1d34f342fc05bd47726a5e188"
+dependencies = [
+ "regex 1.5.4",
+]
+
+[[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
+name = "levenshtein"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
+
+[[package]]
 name = "libc"
 version = "0.2.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
 
 [[package]]
+name = "libnghttp2-sys"
+version = "0.1.7+1.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
 name = "lock_api"
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1507,6 +1811,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
 name = "parking"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1550,6 +1873,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
+name = "petgraph"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
 name = "phf"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1604,6 +1937,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "pico-args"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
+
+[[package]]
 name = "pin-project"
 version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1642,6 +1981,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d15b6607fa632996eb8a17c9041cb6071cb75ac057abd45dece578723ea8c7c0"
 
 [[package]]
+name = "pkg-config"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
+
+[[package]]
 name = "polling"
 version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1902,6 +2247,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "redox_users"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
+dependencies = [
+ "getrandom 0.2.3",
+ "redox_syscall",
+]
+
+[[package]]
 name = "regex"
 version = "0.1.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1965,6 +2320,21 @@ 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 = "rustc_version"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1983,6 +2353,24 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustls"
+version = "0.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b323592e3164322f5b193dc4302e4e36cd8d37158a712d664efae1a5c2791700"
+dependencies = [
+ "log 0.4.14",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
+
+[[package]]
 name = "ryu"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1995,6 +2383,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 
 [[package]]
+name = "schannel"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
+dependencies = [
+ "lazy_static",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "scoped-tls"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2007,6 +2405,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
 name = "selectors"
 version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2090,6 +2498,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "serde_regex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
+dependencies = [
+ "regex 1.5.4",
+ "serde",
+]
+
+[[package]]
 name = "serde_urlencoded"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2142,6 +2560,16 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2151,6 +2579,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "similar"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
+
+[[package]]
 name = "siphasher"
 version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2163,6 +2597,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
 
 [[package]]
+name = "sluice"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
+dependencies = [
+ "async-channel",
+ "futures-core",
+ "futures-io",
+]
+
+[[package]]
 name = "smallvec"
 version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2179,6 +2624,12 @@ 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"
@@ -2321,6 +2772,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "term"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
+dependencies = [
+ "dirs-next",
+ "rustversion",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "termcolor"
 version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2433,6 +2895,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
 name = "tinyvec"
 version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2478,6 +2949,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "tokio-rustls"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b"
+dependencies = [
+ "rustls",
+ "tokio",
+ "webpki",
+]
+
+[[package]]
 name = "tokio-stream"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2517,10 +2999,22 @@ dependencies = [
  "cfg-if",
  "log 0.4.14",
  "pin-project-lite",
+ "tracing-attributes",
  "tracing-core",
 ]
 
 [[package]]
+name = "tracing-attributes"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "tracing-core"
 version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2530,6 +3024,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "pin-project",
+ "tracing",
+]
+
+[[package]]
 name = "try-lock"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2597,6 +3101,12 @@ 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"
@@ -2632,6 +3142,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
 name = "version_check"
 version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2772,6 +3288,25 @@ dependencies = [
 ]
 
 [[package]]
+name = "webpki"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449"
+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 8b43c63..88eb6d6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,9 +30,11 @@ 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
 test-logger = "^0.1.0"       # Simple helper to initialize env_logger before unit and integration tests
+httpmock = "^0.6"            # HTTP mocking library that allows you to simulate responses from HTTP based services
 
 [dependencies]
 async-trait = "^0.1.50"      # Type erasure for async trait methods
+bytes = "^1.1.0"
 data-encoding = "^2.3.2"     # Efficient and customizable data-encoding functions like base64, base32, and hex
 easy-scraper = "^0.2.0"      # HTML scraping library focused on ease of use
 ellipse = "^0.2.0"           # Truncate and ellipsize strings in a human-friendly way
@@ -80,4 +82,8 @@ default-features = false
 features = ["multipart", "compression"]
 [dependencies.hyper]
 version = "^0.14.17"
-features = ["client", "stream", "runtime"]
\ No newline at end of file
+features = ["client", "stream", "runtime"]
+[dependencies.hyper-rustls]
+version = "^0.23.0"
+default-features = false
+features = ["webpki-tokio", "http1", "http2", "tls12", "logging"]
\ No newline at end of file
diff --git a/flake.nix b/flake.nix
index 18d9751..c1827cd 100644
--- a/flake.nix
+++ b/flake.nix
@@ -187,14 +187,17 @@
     };
     rust-bin = pkgs.rust-bin.stable.latest;
     packages = {
-      kittybox = { stdenv, lib, redis, naersk-lib }:
+      kittybox = { stdenv, lib, openssl, pkg-config, naersk-lib }:
       naersk-lib.buildPackage {
         pname = "kittybox";
         version = "0.1.0";
 
         src = ./.;
 
-        checkInputs = [ redis ];
+        checkInputs = [ openssl.dev ];
+        nativeBuildInputs = [ pkg-config ];
+        nativeCheckInputs = [ pkg-config ];
+
         doCheck = stdenv.hostPlatform == stdenv.targetPlatform;
 
         meta = with lib.meta; {
@@ -259,9 +262,10 @@
       name = "rust-dev-shell";
       nativeBuildInputs = with pkgs; [
         pkg-config lld
+        # required for httpmock, not actually used
+        openssl.dev
         (rust-bin.default.override { extensions = [ "rust-src" ]; })
         (rust-analyzer.override { rustPlatform = with rust-bin; { rustLibSrc = rust-src; }; })
-        redis
       ];
     };
   });
diff --git a/src/indieauth.rs b/src/indieauth.rs
index f8f862b..305452a 100644
--- a/src/indieauth.rs
+++ b/src/indieauth.rs
@@ -1,14 +1,6 @@
-use async_trait::async_trait;
-#[allow(unused_imports)]
-use log::{error, info};
-use std::sync::Arc;
-use tide::prelude::*;
-#[allow(unused_imports)]
-use tide::{Next, Request, Response, Result};
 use url::Url;
-
-use crate::database;
-use crate::ApplicationState;
+use serde::{Serialize, Deserialize};
+use warp::{Filter, Rejection};
 
 #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
 pub struct User {
@@ -17,6 +9,71 @@ pub struct User {
     scope: String,
 }
 
+#[derive(Debug, Clone, PartialEq, Copy)]
+pub enum ErrorKind {
+    PermissionDenied,
+    NotAuthorized,
+    TokenEndpointError,
+    JsonParsing,
+    Other
+}
+
+#[derive(Deserialize, Serialize, Debug, Clone)]
+pub struct TokenEndpointError {
+    error: String,
+    error_description: String
+}
+
+#[derive(Debug)]
+pub struct IndieAuthError {
+    source: Option<Box<dyn std::error::Error + Send + Sync>>,
+    kind: ErrorKind,
+    msg: String
+}
+
+impl std::error::Error for IndieAuthError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        self.source.as_ref().map(|e| e.as_ref() as &dyn std::error::Error)
+    }
+}
+
+impl std::fmt::Display for IndieAuthError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match match self.kind {
+            ErrorKind::TokenEndpointError => write!(f, "token endpoint returned an error: "),
+            ErrorKind::JsonParsing => write!(f, "error while parsing token endpoint response: "),
+            ErrorKind::NotAuthorized => write!(f, "token endpoint did not recognize the token: "),
+            ErrorKind::PermissionDenied => write!(f, "token endpoint rejected the token: "),
+            ErrorKind::Other => write!(f, "token endpoint communication error: "),
+        } {
+            Ok(_) => write!(f, "{}", self.msg),
+            Err(err) => Err(err)
+        }
+    }
+}
+
+impl From<serde_json::Error> for IndieAuthError {
+    fn from(err: serde_json::Error) -> Self {
+        Self {
+            msg: format!("{}", err),
+            source: Some(Box::new(err)),
+            kind: ErrorKind::JsonParsing,
+        }
+    }
+}
+
+impl From<hyper::Error> for IndieAuthError {
+    fn from(err: hyper::Error) -> Self {
+        Self {
+            msg: format!("{}", err),
+            source: Some(Box::new(err)),
+            kind: ErrorKind::Other,
+        }
+    }
+}
+
+impl warp::reject::Reject for IndieAuthError {}
+
 impl User {
     pub fn check_scope(&self, scope: &str) -> bool {
         self.scopes().any(|i| i == scope)
@@ -33,207 +90,106 @@ impl User {
     }
 }
 
-#[cfg(any(not(debug_assertions), test))]
-async fn get_token_data(
-    token: String,
-    token_endpoint: &http_types::Url,
-    http_client: &surf::Client,
-) -> (http_types::StatusCode, Option<User>) {
-    match http_client
-        .get(token_endpoint)
-        .header("Authorization", token)
-        .header("Accept", "application/json")
-        .send()
-        .await
-    {
-        Ok(mut resp) => {
-            if resp.status() == 200 {
-                match resp.body_json::<User>().await {
-                    Ok(user) => {
-                        info!(
-                            "Token endpoint request successful. Validated user: {}",
-                            user.me
-                        );
-                        (resp.status(), Some(user))
-                    }
-                    Err(err) => {
-                        error!(
-                            "Token endpoint parsing error (HTTP status {}): {}",
-                            resp.status(),
-                            err
-                        );
-                        (http_types::StatusCode::InternalServerError, None)
-                    }
-                }
-            } else {
-                error!("Token endpoint returned non-200: {}", resp.status());
-                (resp.status(), None)
-            }
-        }
-        Err(err) => {
-            error!("Token endpoint connection error: {}", err);
-            (http_types::StatusCode::InternalServerError, None)
-        }
-    }
-}
+// TODO: consider making this a generic
+type HttpClient = hyper::Client<hyper_rustls::HttpsConnector<hyper::client::HttpConnector<hyper::client::connect::dns::GaiResolver>>, hyper::Body>;
 
-pub struct IndieAuthMiddleware {
-    #[allow(dead_code)] // it's not really dead since it's only dead in debug scope
-    cache: Arc<retainer::Cache<String, User>>,
-    monitor_task: Option<async_std::task::JoinHandle<()>>,
-}
-impl IndieAuthMiddleware {
-    /// Create a new instance of IndieAuthMiddleware.
-    ///
-    /// Note that creating a new instance automatically launches a task
-    /// to garbage-collect stale cache entries. Please do not create
-    /// instances willy-nilly because of that.
-    pub fn new() -> Self {
-        let cache: Arc<retainer::Cache<String, User>> = Arc::new(retainer::Cache::new());
-        let cache_clone = cache.clone();
-        let task = async_std::task::spawn(async move {
-            cache_clone
-                .monitor(4, 0.1, std::time::Duration::from_secs(30))
-                .await
-        });
-
-        #[cfg(all(debug_assertions, not(test)))]
-        error!("ATTENTION: You are running in debug mode. NO REQUESTS TO TOKEN ENDPOINT WILL BE MADE. YOU WILL BE PROCEEDING WITH DEBUG USER CREDENTIALS. DO NOT RUN LIKE THIS IN PRODUCTION.");
+pub fn require_token(token_endpoint: String, http: HttpClient) -> impl Filter<Extract = (User,), Error = Rejection> {
+    // It might be OK to panic here, because we're still inside the initialisation sequence for now.
+    // Proper error handling on the top of this should be used though.
+    let token_endpoint_uri = hyper::Uri::try_from(&token_endpoint)
+        .expect("Couldn't parse the token endpoint URI!");
+    warp::any()
+        .map(move || token_endpoint_uri.clone())
+        .and(warp::any().map(move || http.clone()))
+        .and(warp::header::<String>("Authorization"))
+        .and_then(|token_endpoint, http: HttpClient, token| async move {
+            let request = hyper::Request::builder()
+                .method(hyper::Method::GET)
+                .uri(token_endpoint)
+                .header("Authorization", token)
+                .header("Accept", "application/json")
+                .body(hyper::Body::from(""))
+                // TODO is it acceptable to panic here?
+                .unwrap();
 
-        Self {
-            cache,
-            monitor_task: Some(task),
-        }
-    }
-}
-impl Drop for IndieAuthMiddleware {
-    fn drop(&mut self) {
-        // Cancel the task, or a VERY FUNNY thing might occur.
-        // If I understand this correctly, keeping a task active
-        // WILL keep an active reference to a value, so I'm pretty sure
-        // that something VERY FUNNY might occur whenever `cache` is dropped
-        // and its related task is not cancelled. So let's cancel it so
-        // [`cache`] can be dropped once and for all.
-
-        // First, get the ownership of a task, sneakily switching it out with None
-        // (wow, this is sneaky, didn't know Safe Rust could even do that!!!)
-        // (it is safe tho cuz None is no nullptr and dereferencing it doesn't cause unsafety)
-        // (could cause a VERY FUNNY race condition to occur though
-        //  if you tried to refer to the value in another thread!)
-        let task = std::mem::take(&mut self.monitor_task)
-            .expect("Dropped IndieAuthMiddleware TWICE? Impossible!");
-        // Then cancel the task, using another task to request cancellation.
-        // Because apparently you can't run async code from Drop...
-        // This should drop the last reference for the [`cache`],
-        // allowing it to be dropped.
-        async_std::task::spawn(async move { task.cancel().await });
-    }
-}
-#[async_trait]
-impl<B> tide::Middleware<ApplicationState<B>> for IndieAuthMiddleware
-where
-    B: database::Storage + Send + Sync + Clone,
-{
-    #[cfg(all(not(test), debug_assertions))]
-    async fn handle(
-        &self,
-        mut req: Request<ApplicationState<B>>,
-        next: Next<'_, ApplicationState<B>>,
-    ) -> Result {
-        req.set_ext(User::new(
-            "https://localhost:8080/",
-            "https://curl.haxx.se/",
-            "create update delete undelete media",
-        ));
-        Ok(next.run(req).await)
-    }
-    #[cfg(any(not(debug_assertions), test))]
-    async fn handle(
-        &self,
-        mut req: Request<ApplicationState<B>>,
-        next: Next<'_, ApplicationState<B>>,
-    ) -> Result {
-        let header = req.header("Authorization");
-        match header {
-            None => {
-                // TODO: move that to the request handling functions
-                // or make a middleware that refuses to accept unauthenticated requests
-                Ok(Response::builder(401)
-                    .body(json!({
-                        "error": "unauthorized",
-                        "error_description": "Please provide an access token."
-                    }))
-                    .build())
-            }
-            Some(value) => {
-                match &req.state().internal_token {
-                    Some(token) => {
-                        if token
-                            == &value
-                                .last()
-                                .to_string()
-                                .split(' ')
-                                .skip(1)
-                                .collect::<String>()
-                        {
-                            req.set_ext::<User>(User::new(
-                                "", // no user ID here
-                                "https://kittybox.fireburn.ru/",
-                                "update delete undelete media kittybox_internal:do_what_thou_wilt",
-                            ));
-                            return Ok(next.run(req).await);
+            use hyper::StatusCode;
+
+            match http.request(request).await {
+                Ok(mut res) => match res.status() {
+                    StatusCode::OK => {
+                        use hyper::body::HttpBody;
+                        use bytes::BufMut;
+                        let mut buf: Vec<u8> = Vec::default();
+                        while let Some(chunk) = res.body_mut().data().await {
+                            if let Err(err) = chunk {
+                                return Err(IndieAuthError::from(err).into());
+                            }
+                            buf.put(chunk.unwrap());
+                        }
+                        match serde_json::from_slice(&buf) {
+                            Ok(user) => Ok(user),
+                            Err(err) => {
+                                if let Ok(json) = serde_json::from_slice::<serde_json::Value>(&buf) {
+                                    if Some(false) == json["active"].as_bool() {
+                                        Err(IndieAuthError {
+                                            source: None,
+                                            kind: ErrorKind::NotAuthorized,
+                                            msg: "The token endpoint deemed the token as not \"active\".".to_string()
+                                        }.into())
+                                    } else {
+                                        Err(IndieAuthError::from(err).into())
+                                    }
+                                } else {
+                                    Err(IndieAuthError::from(err).into())
+                                }
+                            }
                         }
-                    }
-                    None => {}
-                }
-                let endpoint = &req.state().token_endpoint;
-                let http_client = &req.state().http_client;
-                let token = value.last().to_string();
-                match self.cache.get(&token).await {
-                    Some(user) => {
-                        req.set_ext::<User>(user.clone());
-                        Ok(next.run(req).await)
                     },
-                    None => match get_token_data(value.last().to_string(), endpoint, http_client).await {
-                        (http_types::StatusCode::Ok, Some(user)) => {
-                            // Note that this can run multiple requests before the value appears in the cache.
-                            // This seems to be in line with some other implementations of a function cache
-                            // (e.g. the [`cached`](https://lib.rs/crates/cached) crate and Python's `functools.lru_cache`)
-                            //
-                            // TODO: ensure the duration is no more than the token's remaining time until expiration
-                            // (in case the expiration time is defined on the token - AFAIK currently non-standard in IndieAuth)
-                            self.cache.insert(token, user.clone(), std::time::Duration::from_secs(600)).await;
-                            req.set_ext(user);
-                            Ok(next.run(req).await)
-                        },
-                        // TODO: Refactor to return Err(IndieAuthError) so downstream middleware could catch it
-                        // and present a prettier interface to the error (maybe even hiding data from the user)
-                        (http_types::StatusCode::InternalServerError, None) => {
-                            Ok(Response::builder(500).body(json!({
-                                "error": "token_endpoint_fail",
-                                "error_description": "Token endpoint made a boo-boo and refused to answer."
-                            })).build())
-                        },
-                        (_, None) => {
-                            Ok(Response::builder(401).body(json!({
-                                "error": "unauthorized",
-                                "error_description": "The token endpoint refused to accept your token."
-                            })).build())
-                        },
-                        (_, Some(_)) => {
-                            // This shouldn't happen.
-                            panic!("The token validation function has caught rabies and returns malformed responses. Aborting.");
+                    StatusCode::BAD_REQUEST => {
+                        use hyper::body::HttpBody;
+                        use bytes::BufMut;
+                        let mut buf: Vec<u8> = Vec::default();
+                        while let Some(chunk) = res.body_mut().data().await {
+                            if let Err(err) = chunk {
+                                return Err(IndieAuthError::from(err).into());
+                            }
+                            buf.put(chunk.unwrap());
+                        }
+                        match serde_json::from_slice::<TokenEndpointError>(&buf) {
+                            Ok(err) => {
+                                if err.error == "unauthorized" {
+                                    Err(IndieAuthError {
+                                        source: None,
+                                        kind: ErrorKind::NotAuthorized,
+                                        msg: err.error_description
+                                    }.into())
+                                } else {
+                                    Err(IndieAuthError {
+                                        source: None,
+                                        kind: ErrorKind::TokenEndpointError,
+                                        msg: err.error_description
+                                    }.into())
+                                }
+                            },
+                            Err(err) => Err(IndieAuthError::from(err).into())
                         }
-                    }
-                }
+                    },
+                    _ => Err(IndieAuthError {
+                        source: None,
+                        msg: format!("Token endpoint returned {}", res.status()).to_string(),
+                        kind: ErrorKind::TokenEndpointError
+                    }.into())
+                },
+                Err(err) => Err(warp::reject::custom(IndieAuthError::from(err)))
             }
-        }
-    }
+        })
 }
 
 #[cfg(test)]
 mod tests {
-    use super::*;
+    use super::{HttpClient, User, IndieAuthError, require_token};
+    use httpmock::prelude::*;
+    
     #[test]
     fn user_scopes_are_checkable() {
         let user = User::new(
@@ -245,4 +201,92 @@ mod tests {
         assert!(user.check_scope("create"));
         assert!(!user.check_scope("delete"));
     }
+
+    fn get_http_client() -> HttpClient {
+        let builder = hyper::Client::builder();
+        let https = hyper_rustls::HttpsConnectorBuilder::new()
+            .with_webpki_roots()
+            .https_or_http()
+            .enable_http1()
+            .enable_http2()
+            .build();
+        builder.build(https)
+    }
+    
+    #[tokio::test]
+    async fn test_require_token_with_token() {
+        let server = MockServer::start_async().await;
+        server.mock_async(|when, then| {
+            when.path("/token")
+                .header("Authorization", "Bearer token");
+
+            then.status(200)
+                .header("Content-Type", "application/json")
+                .json_body(serde_json::to_value(User::new(
+                    "https://fireburn.ru/",
+                    "https://quill.p3k.io/",
+                    "create update media",
+                )).unwrap());
+        }).await;
+        
+        let filter = require_token(server.url("/token"), get_http_client());
+
+        let res: User = warp::test::request()
+            .path("/")
+            .header("Authorization", "Bearer token")
+            .filter(&filter)
+            .await
+            .unwrap();
+
+        assert_eq!(res.me.as_str(), "https://fireburn.ru/")
+    }
+
+    #[tokio::test]
+    async fn test_require_token_fake_token() {
+        let server = MockServer::start_async().await;
+        server.mock_async(|when, then| {
+            when.path("/refuse_token");
+
+            then.status(200)
+                .json_body(serde_json::json!({"active": false}));
+        }).await;
+
+        let filter = require_token(server.url("/refuse_token"), get_http_client());
+
+        let res = warp::test::request()
+            .path("/")
+            .header("Authorization", "Bearer token")
+            .filter(&filter)
+            .await
+            .unwrap_err();
+
+        let err: &IndieAuthError = res.find().unwrap();
+        assert_eq!(err.kind, super::ErrorKind::NotAuthorized);
+    }
+
+    #[tokio::test]
+    async fn test_require_token_400_error_unauthorized() {
+        let server = MockServer::start_async().await;
+        server.mock_async(|when, then| {
+            when.path("/refuse_token_with_400");
+
+            then.status(400)
+                .json_body(serde_json::json!({
+                    "error": "unauthorized",
+                    "error_description": "The token provided was malformed"
+                }));
+        }).await;
+
+        let filter = require_token(server.url("/refuse_token_with_400"), get_http_client());
+
+        let res = warp::test::request()
+            .path("/")
+            .header("Authorization", "Bearer token")
+            .filter(&filter)
+            .await
+            .unwrap_err();
+
+        let err: &IndieAuthError = res.find().unwrap();
+        assert_eq!(err.kind, super::ErrorKind::NotAuthorized);
+    }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 2585227..93e4593 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,14 +1,12 @@
-//use tide::{Request, Response};
+#[allow(unused_imports)]
 use warp::Filter;
-/*pub mod database;
-mod frontend;
-mod indieauth;
-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;
+pub mod indieauth;
+//pub mod frontend;
 
 /*use crate::indieauth::IndieAuthMiddleware;
 use crate::micropub::CORSMiddleware;*/