diff --git a/Cargo.lock b/Cargo.lock index 13763dc..7ca7ba0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "async-compression" version = "0.3.12" @@ -15,6 +30,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "atty" version = "0.2.14" @@ -32,6 +58,70 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" @@ -129,7 +219,7 @@ dependencies = [ "atty", "bitflags", "clap_lex", - "indexmap", + "indexmap 1.8.0", "strsim", "termcolor", "textwrap", @@ -162,6 +252,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "fern" version = "0.6.1" @@ -239,10 +335,16 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.11" +name = "gimli" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -250,10 +352,10 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.1.0", "slab", "tokio", - "tokio-util", + "tokio-util 0.7.10", "tracing", ] @@ -263,6 +365,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "headers" version = "0.3.6" @@ -299,20 +407,20 @@ dependencies = [ [[package]] name = "http" -version = "0.2.6" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", - "itoa 1.0.1", + "itoa", ] [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -321,9 +429,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.5.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -333,9 +441,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.16" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -346,9 +454,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 0.4.8", + "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -362,41 +470,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] name = "itoa" -version = "0.4.8" +version = "1.0.9" 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 = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" -version = "0.4.14" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matches" @@ -404,6 +507,36 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "maud" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0bab19cef8a7fe1c18a43e881793bfc9d4ea984befec3ae5bd0415abf3ecf00" +dependencies = [ + "axum-core", + "http", + "itoa", + "maud_macros", +] + +[[package]] +name = "maud_macros" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be95d66c3024ffce639216058e5bae17a83ecaf266ffc6e4d060ad447c9eed2" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.86", +] + [[package]] name = "memchr" version = "2.4.1" @@ -427,13 +560,21 @@ dependencies = [ ] [[package]] -name = "mio" -version = "0.8.3" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -468,10 +609,19 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.9.0" +name = "object" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -508,14 +658,14 @@ checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.86", ] [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -530,23 +680,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] -name = "proc-macro2" -version = "1.0.36" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "unicode-xid", + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.86", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.9" @@ -561,22 +747,22 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -585,11 +771,21 @@ version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" dependencies = [ - "itoa 1.0.1", + "itoa", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -597,7 +793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.1", + "itoa", "ryu", "serde", ] @@ -632,14 +828,24 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "strsim" version = "0.10.0" @@ -657,6 +863,23 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "termcolor" version = "1.1.2" @@ -685,32 +908,31 @@ dependencies = [ [[package]] name = "tokio" -version = "1.19.2" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.5", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -738,6 +960,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.8" @@ -747,6 +983,28 @@ dependencies = [ "serde", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.1" @@ -755,11 +1013,10 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-core", @@ -767,11 +1024,11 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -791,12 +1048,14 @@ name = "udpt-rs" version = "3.1.2" dependencies = [ "async-compression", + "axum", "binascii", "bincode", "chrono", "clap", "fern", "log", + "maud", "serde", "serde_json", "tokio", @@ -813,6 +1072,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -858,7 +1123,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-stream", - "tokio-util", + "tokio-util 0.6.9", "tower-service", "tracing", ] @@ -908,43 +1173,66 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows_aarch64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 801dcf9..98cb726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,5 @@ fern = "0.6" serde_json = "1.0" async-compression = {version = "^0.3.8", features = ["bzip2", "tokio"]} chrono = "0.4" +maud = { version = "*", features = ["axum"] } +axum = { version = "0.6.18", features = ["json"] } diff --git a/src/config.rs b/src/config.rs index 462ab80..cfd67ee 100644 --- a/src/config.rs +++ b/src/config.rs @@ -68,12 +68,10 @@ impl Configuration { pub fn load_file(path: &str) -> Result { match std::fs::read(path) { Err(e) => Err(ConfigError::IOError(e)), - Ok(data) => { - match Self::load(data.as_slice()) { - Ok(cfg) => Ok(cfg), - Err(e) => Err(ConfigError::ParseError(e)), - } - } + Ok(data) => match Self::load(data.as_slice()) { + Ok(cfg) => Ok(cfg), + Err(e) => Err(ConfigError::ParseError(e)), + }, } } diff --git a/src/main.rs b/src/main.rs index 22daa48..1235c7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,20 +12,18 @@ use std::process::exit; fn setup_logging(cfg: &Configuration) { let log_level = match cfg.get_log_level() { None => log::LevelFilter::Info, - Some(level) => { - match level.as_str() { - "off" => log::LevelFilter::Off, - "trace" => log::LevelFilter::Trace, - "debug" => log::LevelFilter::Debug, - "info" => log::LevelFilter::Info, - "warn" => log::LevelFilter::Warn, - "error" => log::LevelFilter::Error, - _ => { - eprintln!("udpt: unknown log level encountered '{}'", level.as_str()); - exit(-1); - } + Some(level) => match level.as_str() { + "off" => log::LevelFilter::Off, + "trace" => log::LevelFilter::Trace, + "debug" => log::LevelFilter::Debug, + "info" => log::LevelFilter::Info, + "warn" => log::LevelFilter::Warn, + "error" => log::LevelFilter::Error, + _ => { + eprintln!("udpt: unknown log level encountered '{}'", level.as_str()); + exit(-1); } - } + }, }; if let Err(err) = fern::Dispatch::new() @@ -117,7 +115,9 @@ async fn main() { let tokens = http_cfg.get_access_tokens(); let server = webserver::build_server(https_tracker, tokens.clone()); - server.bind(bind_addr.parse::().unwrap()).await; + let _ = axum::Server::bind(&bind_addr.parse::().unwrap()) + .serve(server.into_make_service()) + .await; }); } diff --git a/src/server.rs b/src/server.rs index 7145c2e..44edf5f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -240,15 +240,18 @@ impl UDPTracker { let mut payload_buffer = vec![0u8; MAX_PACKET_SIZE]; let mut payload = StackVec::from(&mut payload_buffer); - match pack_into(&mut payload, &UDPAnnounceResponse { - header: UDPResponseHeader { - action: Actions::Announce, - transaction_id: packet.header.transaction_id, + match pack_into( + &mut payload, + &UDPAnnounceResponse { + header: UDPResponseHeader { + action: Actions::Announce, + transaction_id: packet.header.transaction_id, + }, + seeders, + interval: self.config.get_udp_config().get_announce_interval(), + leechers, }, - seeders, - interval: self.config.get_udp_config().get_announce_interval(), - leechers, - }) { + ) { Ok(_) => {} Err(_) => { return; @@ -290,10 +293,13 @@ impl UDPTracker { let mut response_buffer = [0u8; 8 + MAX_SCRAPE * 12]; let mut response = StackVec::from(&mut response_buffer); - if pack_into(&mut response, &UDPResponseHeader { - action: Actions::Scrape, - transaction_id: header.transaction_id, - }) + if pack_into( + &mut response, + &UDPResponseHeader { + action: Actions::Scrape, + transaction_id: header.transaction_id, + }, + ) .is_err() { // not much we can do... @@ -331,13 +337,11 @@ impl UDPTracker { leechers, } } - None => { - UDPScrapeResponseEntry { - seeders: 0, - completed: 0, - leechers: 0, - } - } + None => UDPScrapeResponseEntry { + seeders: 0, + completed: 0, + leechers: 0, + }, }; if pack_into(&mut response, &result).is_err() { @@ -363,7 +367,7 @@ impl UDPTracker { Err(err) => { debug!("failed to send a packet: {}", err); Err(err) - }, + } Ok(sz) => Ok(sz), } } @@ -372,10 +376,15 @@ impl UDPTracker { let mut payload_buffer = vec![0u8; MAX_PACKET_SIZE]; let mut payload = StackVec::from(&mut payload_buffer); - if pack_into(&mut payload, &UDPResponseHeader { - transaction_id: header.transaction_id, - action: Actions::Error, - }).is_ok() { + if pack_into( + &mut payload, + &UDPResponseHeader { + transaction_id: header.transaction_id, + action: Actions::Error, + }, + ) + .is_ok() + { let msg_bytes = Vec::from(error_msg.as_bytes()); payload.extend(msg_bytes); @@ -414,7 +423,10 @@ mod tests { assert!(pack_into(&mut payload, &mystruct).is_ok()); assert_eq!(payload.as_slice().len(), 16); - assert_eq!(payload.as_slice(), &[0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0, 0, 0, 0, 0, 1, 47, 203]); + assert_eq!( + payload.as_slice(), + &[0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0, 0, 0, 0, 0, 1, 47, 203] + ); } #[test] diff --git a/src/tracker.rs b/src/tracker.rs index 4d83564..a07e9f0 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -75,9 +75,7 @@ impl std::convert::From<&[u8]> for InfoHash { impl From<[u8; 20]> for InfoHash { fn from(info_hash: [u8; 20]) -> Self { - InfoHash { - info_hash, - } + InfoHash { info_hash } } } @@ -219,7 +217,8 @@ impl PeerId { impl Serialize for PeerId { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, { + S: serde::Serializer, + { let mut tmp = [0u8; 40]; binascii::bin2hex(&self.0, &mut tmp).unwrap(); let id = std::str::from_utf8(&tmp).ok(); @@ -272,14 +271,17 @@ impl TorrentEntry { let is_seeder = left == 0 && uploaded > 0; let mut was_seeder = false; let mut is_completed = left == 0 && (event as u32) == (Events::Complete as u32); - if let Some(prev) = self.peers.insert(*peer_id, TorrentPeer { - updated: std::time::Instant::now(), - left, - downloaded, - uploaded, - ip: *remote_address, - event, - }) { + if let Some(prev) = self.peers.insert( + *peer_id, + TorrentPeer { + updated: std::time::Instant::now(), + left, + downloaded, + uploaded, + ip: *remote_address, + event, + }, + ) { was_seeder = prev.left == 0 && prev.uploaded > 0; if is_completed && (prev.event as u32) == (Events::Complete as u32) { @@ -383,7 +385,7 @@ impl TorrentTracker { Err(ref err) => { error!("failed to read lines! {}", err); continue; - }, + } }; let row: DatabaseRow = match serde_json::from_str(&line) { Ok(v) => v, @@ -411,7 +413,7 @@ impl TorrentTracker { std::collections::btree_map::Entry::Vacant(ve) => { ve.insert(TorrentEntry::new()); Ok(()) - }, + } std::collections::btree_map::Entry::Occupied(_entry) => Err(()), } } @@ -453,9 +455,7 @@ impl TorrentTracker { &self, info_hash: &InfoHash, remote_addr: &std::net::SocketAddr, ) -> Option> { let read_lock = self.database.torrent_peers.read().await; - read_lock - .get(info_hash) - .map(|entry| entry.get_peers(remote_addr)) + read_lock.get(info_hash).map(|entry| entry.get_peers(remote_addr)) } pub async fn update_torrent_and_get_stats( @@ -465,14 +465,12 @@ impl TorrentTracker { use std::collections::btree_map::Entry; let mut torrent_peers = self.database.torrent_peers.write().await; let torrent_entry = match torrent_peers.entry(info_hash.clone()) { - Entry::Vacant(vacant) => { - match self.mode { - TrackerMode::Dynamic => vacant.insert(TorrentEntry::new()), - _ => { - return TorrentStats::TorrentNotRegistered; - } + Entry::Vacant(vacant) => match self.mode { + TrackerMode::Dynamic => vacant.insert(TorrentEntry::new()), + _ => { + return TorrentStats::TorrentNotRegistered; } - } + }, Entry::Occupied(entry) => { if entry.get().is_flagged() { return TorrentStats::TorrentFlagged; diff --git a/src/webserver.rs b/src/webserver.rs index 332c9ec..f8bb9c0 100644 --- a/src/webserver.rs +++ b/src/webserver.rs @@ -1,42 +1,55 @@ use crate::tracker::{InfoHash, TorrentTracker}; +use axum::extract::{Path, Query, State}; +use axum::http::{Request, StatusCode}; +use axum::middleware::{self, Next}; +use axum::response::{IntoResponse, Redirect, Response}; +use axum::Json; +use maud::{html, Markup, DOCTYPE}; use serde::{Deserialize, Serialize}; use std::cmp::min; use std::collections::{HashMap, HashSet}; use std::sync::Arc; -use warp::{filters, reply, reply::Reply, serve, Filter, Server}; -fn view_root() -> impl Reply { - warp::http::Response::builder() - .header("Content-Type", "text/html; charset=utf-8") - .header("Server", concat!("udpt/", env!("CARGO_PKG_VERSION"), "; https://abda.nl/")) - .body(concat!(r#" - - udpt server - - - -

- This server is running udpt, a BitTorrent tracker based on the UDP protocol. -

-
- udpt/"#, env!("CARGO_PKG_VERSION"), r#"
- docs · issues & PRs · donate -
- - "#)) - .unwrap() +async fn view_root() -> Markup { + html! { + (DOCTYPE) + head { + meta charset="utf-8"; + meta name="viewport" content="width=device-width, initial-scale=1"; + title { "udpt server" } + style { "body { + background-color: #222; + color: #eee; + margin-left: auto; + margin-right: auto; + margin-top: 20%; + max-width: 750px; + } + a, a:active, a:visited { + color: lightpink; + }"} + } + body { + p { + "This server is running " + a href="https://github.com/naim94a/udpt" + style="font-weight: bold; font-size: large" { code { "udpt" } } + " a " + a + href="https://en.wikipedia.org/wiki/BitTorrent_tracker" + rel="nofollow" + target="_blank" { "BitTorrent tracker" } + " based on the " + a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol" + rel="nofollow" target="_blank" { "UDP" } " protocol." + } + div style="color: grey; font-size: small; border-top: 1px solid grey; width: 75%; max-width: 300px; margin-left: auto; margin-right: auto; text-align: center; padding-top: 5px" { + "udpt/" (env!("CARGO_PKG_VERSION")) br; + a href="https://naim94a.github.com/udpt/" { "docs" } " ยท " + a href="https://github.com/naim94a/udpt/issues" { "issues & PRs" } + } + } + } } #[derive(Deserialize, Debug)] @@ -46,10 +59,10 @@ struct TorrentInfoQuery { } #[derive(Serialize)] -struct TorrentEntry<'a> { - info_hash: &'a InfoHash, +struct TorrentEntry { + info_hash: InfoHash, #[serde(flatten)] - data: &'a crate::tracker::TorrentEntry, + data: crate::tracker::TorrentEntry, seeders: u32, leechers: u32, @@ -62,6 +75,12 @@ struct TorrentFlag { is_flagged: bool, } +#[derive(Clone)] +struct AppState { + tracker: Arc, + tokens: HashMap, +} + #[derive(Serialize, Debug)] #[serde(tag = "status", rename_all = "snake_case")] enum ActionStatus<'a> { @@ -69,171 +88,120 @@ enum ActionStatus<'a> { Err { reason: std::borrow::Cow<'a, str> }, } -impl warp::reject::Reject for ActionStatus<'static> {} +async fn json_torrent_list(State(state): State, limits: Query) -> Json> { + let offset = limits.offset.unwrap_or(0); + let limit = min(limits.limit.unwrap_or(1000), 4000); -fn authenticate(tokens: HashMap) -> impl Filter + Clone { - #[derive(Deserialize)] - struct AuthToken { - token: Option, + let db = state.tracker.get_database().await; + let results: Vec<_> = db + .iter() + .map(|(k, v)| { + let (seeders, _, leechers) = v.get_stats(); + TorrentEntry { + info_hash: k.clone(), + data: v.clone(), + seeders, + leechers, + peers: None, + } + }) + .skip(offset as usize) + .take(limit as usize) + .collect(); + + return axum::Json(results); +} + +async fn json_torrent_info( + State(state): State, Path(info_hash): Path, +) -> Result, StatusCode> { + let db = state.tracker.get_database().await; + let info = match db.get(&info_hash) { + Some(v) => v, + None => return Err(StatusCode::NOT_FOUND), + }; + let (seeders, _, leechers) = info.get_stats(); + + let peers: Vec<_> = info + .get_peers_iter() + .take(1000) + .map(|(&peer_id, peer_info)| (peer_id, peer_info.clone())) + .collect(); + + return Ok(axum::Json(TorrentEntry { + info_hash, + data: info.clone(), + seeders, + leechers, + peers: Some(peers), + })); +} + +async fn delete_torrent(State(state): State, Path(info_hash): Path) -> Json> { + let resp = match state.tracker.remove_torrent(&info_hash, true).await.is_ok() { + true => ActionStatus::Ok, + false => ActionStatus::Err { + reason: "failed to delete torrent".into(), + }, + }; + + axum::Json(resp) +} + +#[derive(Deserialize, Debug)] +struct ModifyTorrent { + flag: Option, +} + +async fn modify_torrent( + State(state): State, Path(info_hash): Path, Json(payload): Json, +) -> Json> { + let is_flagged = payload.flag.unwrap_or_default(); + let tracker = state.tracker; + if !tracker.set_torrent_flag(&info_hash, is_flagged).await { + if is_flagged { + if tracker.add_torrent(&info_hash).await.is_ok() { + tracker.set_torrent_flag(&info_hash, is_flagged).await; + } else { + return axum::Json(ActionStatus::Err { + reason: "failed to flag torrent".into(), + }); + } + } } - - let tokens: HashSet = tokens.into_iter().map(|(_, v)| v).collect(); - - let tokens = Arc::new(tokens); - warp::filters::any::any() - .map(move || tokens.clone()) - .and(filters::query::query::()) - .and_then(|tokens: Arc>, token: AuthToken| { - async move { - if let Some(token) = token.token { - if tokens.contains(&token) { - return Ok(()); - } - } - Err(warp::reject::custom(ActionStatus::Err { - reason: "Access Denied".into(), - })) - } - }) - .untuple_one() + axum::Json(ActionStatus::Ok) } -pub fn build_server( - tracker: Arc, tokens: HashMap, -) -> Server + Clone + Send + Sync + 'static> { - let root = filters::path::end().map(view_root); - - let t1 = tracker.clone(); - // view_torrent_list -> GET /t/?offset=:u32&limit=:u32 HTTP/1.1 - let view_torrent_list = filters::path::end() - .and(filters::method::get()) - .and(filters::query::query()) - .map(move |limits| { - let tracker = t1.clone(); - (limits, tracker) - }) - .and_then(|(limits, tracker): (TorrentInfoQuery, Arc)| { - async move { - let offset = limits.offset.unwrap_or(0); - let limit = min(limits.limit.unwrap_or(1000), 4000); - - let db = tracker.get_database().await; - let results: Vec<_> = db - .iter() - .map(|(k, v)| { - let (seeders, _, leechers) = v.get_stats(); - TorrentEntry { - info_hash: k, - data: v, - seeders, - leechers, - peers: None, - } - }) - .skip(offset as usize) - .take(limit as usize) - .collect(); - - Result::<_, warp::reject::Rejection>::Ok(reply::json(&results)) +async fn token_auth(State(state): State, request: Request, next: Next) -> Response { + let mut is_authed = false; + let tokens: HashSet = state.tokens.into_iter().map(|(_, v)| v).collect(); + if request.uri().query().is_some() { + let query_params: Vec<&str> = request.uri().query().unwrap().split("&").collect(); + for query_param in query_params { + let split: Vec<&str> = query_param.split("=").collect(); + let (key, value) = (split[0], split[1]); + if key == "token" && tokens.contains(value) { + is_authed = true; } - }); - - let t2 = tracker.clone(); - // view_torrent_info -> GET /t/:infohash HTTP/* - let view_torrent_info = filters::method::get() - .and(filters::path::param()) - .and(filters::path::end()) - .map(move |info_hash: InfoHash| { - let tracker = t2.clone(); - (info_hash, tracker) - }) - .and_then(|(info_hash, tracker): (InfoHash, Arc)| { - async move { - let db = tracker.get_database().await; - let info = match db.get(&info_hash) { - Some(v) => v, - None => return Err(warp::reject::reject()), - }; - let (seeders, _, leechers) = info.get_stats(); - - let peers: Vec<_> = info - .get_peers_iter() - .take(1000) - .map(|(&peer_id, peer_info)| (peer_id, peer_info.clone())) - .collect(); - - Ok(reply::json(&TorrentEntry { - info_hash: &info_hash, - data: info, - seeders, - leechers, - peers: Some(peers), - })) - } - }); - - // DELETE /t/:info_hash - let t3 = tracker.clone(); - let delete_torrent = filters::method::delete() - .and(filters::path::param()) - .and(filters::path::end()) - .map(move |info_hash: InfoHash| { - let tracker = t3.clone(); - (info_hash, tracker) - }) - .and_then(|(info_hash, tracker): (InfoHash, Arc)| { - async move { - let resp = match tracker.remove_torrent(&info_hash, true).await.is_ok() { - true => ActionStatus::Ok, - false => { - ActionStatus::Err { - reason: "failed to delete torrent".into(), - } - } - }; - - Result::<_, warp::Rejection>::Ok(reply::json(&resp)) - } - }); - - let t4 = tracker; - // add_torrent/alter: POST /t/:info_hash - // (optional) BODY: json: {"is_flagged": boolean} - let change_torrent = filters::method::post() - .and(filters::path::param()) - .and(filters::path::end()) - .and(filters::body::content_length_limit(4096)) - .and(filters::body::json()) - .map(move |info_hash: InfoHash, body: Option| { - let tracker = t4.clone(); - (info_hash, tracker, body) - }) - .and_then( - |(info_hash, tracker, body): (InfoHash, Arc, Option)| { - async move { - let is_flagged = body.map(|e| e.is_flagged).unwrap_or(false); - if !tracker.set_torrent_flag(&info_hash, is_flagged).await { - // torrent doesn't exist, add it... - - if is_flagged { - if tracker.add_torrent(&info_hash).await.is_ok() { - tracker.set_torrent_flag(&info_hash, is_flagged).await; - } else { - return Err(warp::reject::custom(ActionStatus::Err { - reason: "failed to flag torrent".into(), - })); - } - } - } - - Result::<_, warp::Rejection>::Ok(reply::json(&ActionStatus::Ok)) - } - }, - ); - let torrent_mgmt = - filters::path::path("t").and(view_torrent_list.or(delete_torrent).or(view_torrent_info).or(change_torrent)); - - let server = root.or(authenticate(tokens).and(torrent_mgmt)); - - serve(server) + } + } + if is_authed { + let response = next.run(request).await; + return response; + } + Redirect::to("/").into_response() +} + +pub fn build_server(tracker: Arc, tokens: HashMap) -> axum::Router { + let state = AppState { tracker, tokens }; + + axum::Router::new() + .route("/t", axum::routing::get(json_torrent_list)) + .route("/t/", axum::routing::get(json_torrent_list)) + .route("/t/:info_hash", axum::routing::get(json_torrent_info)) + .route("/t/:info_hash", axum::routing::delete(delete_torrent)) + .route("/t/:info_hash", axum::routing::post(modify_torrent)) + .layer(middleware::from_fn_with_state(state.clone(), token_auth)) + .route("/", axum::routing::get(view_root)) + .with_state(state) }