From ea4f2a828c3151bf460fa856cdadd977596aee42 Mon Sep 17 00:00:00 2001 From: minish Date: Fri, 3 Jan 2025 03:28:03 -0500 Subject: [PATCH] v0.2.7 --- Cargo.lock | 813 +++++++++++++++++++++++++------------------------- Cargo.toml | 22 +- README.md | 19 +- src/cache.rs | 6 +- src/config.rs | 6 +- src/disk.rs | 22 +- src/engine.rs | 294 ++++++++++++------ src/main.rs | 20 +- src/new.rs | 21 +- src/view.rs | 121 ++++---- 10 files changed, 738 insertions(+), 606 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40b57ad..2b90fd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,39 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "android-tzdata" @@ -52,25 +34,26 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "argh" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" +checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240" dependencies = [ "argh_derive", "argh_shared", + "rust-fuzzy-search", ] [[package]] name = "argh_derive" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" +checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803" dependencies = [ "argh_shared", "proc-macro2", @@ -80,35 +63,13 @@ dependencies = [ [[package]] name = "argh_shared" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6" dependencies = [ "serde", ] -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "atomic-time" version = "0.1.5" @@ -127,20 +88,20 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" dependencies = [ - "async-trait", "axum-core", "axum-macros", "bytes", + "form_urlencoded", "futures-util", "http", "http-body", @@ -158,7 +119,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper", "tokio", "tower", "tower-layer", @@ -168,11 +129,10 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ - "async-trait", "bytes", "futures-util", "http", @@ -181,19 +141,40 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", ] [[package]] -name = "axum-macros" -version = "0.4.1" +name = "axum-extra" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ - "heck", "proc-macro2", "quote", "syn", @@ -201,19 +182,25 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.3", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -222,21 +209,31 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "breeze" -version = "0.2.6" +version = "0.2.7" dependencies = [ "anyhow", "argh", - "async-recursion", "atomic-time", "axum", + "axum-extra", "bytes", "dashmap", + "headers", "http", "img-parts", "rand", @@ -260,16 +257,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "bytes" -version = "1.6.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" -version = "1.0.98" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -279,22 +285,31 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] [[package]] name = "crc32fast" @@ -307,9 +322,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -326,15 +341,25 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -342,9 +367,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", @@ -356,9 +381,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", @@ -367,11 +392,12 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.3" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", + "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -390,10 +416,20 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.12.0" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -418,30 +454,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -450,21 +486,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", @@ -474,6 +510,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -487,15 +533,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -503,7 +549,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.2.6", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -521,22 +567,36 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "ahash", - "allocator-api2", + "base64 0.21.7", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", ] [[package]] -name = "heck" -version = "0.4.1" +name = "headers-core" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] [[package]] name = "hex" @@ -546,9 +606,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -557,9 +617,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -567,12 +627,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http", "http-body", "pin-project-lite", @@ -580,9 +640,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -592,9 +652,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -612,9 +672,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.4" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-util", @@ -623,13 +683,14 @@ dependencies = [ "hyper", "pin-project-lite", "tokio", + "tower-service", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -656,13 +717,13 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "img-parts" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19358258d99a5fc34466fed27a5318f92ae636c3e36165cf9b1e87b5b6701f0" +checksum = "dfded0de32cc78ecad0061b3c6a263cec6bce298fc1e670a4926b6723664ed87" dependencies = [ "bytes", "crc32fast", - "miniz_oxide 0.5.4", + "miniz_oxide", ] [[package]] @@ -678,41 +739,42 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "serde", ] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "lock_api" @@ -726,21 +788,21 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -750,31 +812,22 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" -dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -802,30 +855,20 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "overload" @@ -853,7 +896,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] @@ -862,31 +905,11 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -896,9 +919,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -908,24 +931,27 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -982,13 +1008,19 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags", ] +[[package]] +name = "rust-fuzzy-search" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -997,9 +1029,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -1024,18 +1056,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1044,11 +1076,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1065,9 +1098,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1086,15 +1119,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.6", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -1104,9 +1137,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", @@ -1114,6 +1147,17 @@ dependencies = [ "syn", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1123,6 +1167,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -1149,9 +1199,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1165,9 +1215,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", @@ -1176,15 +1226,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" [[package]] name = "thread_local" @@ -1198,9 +1242,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -1219,9 +1263,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -1229,28 +1273,27 @@ dependencies = [ [[package]] name = "tokio" -version = "1.37.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -1259,9 +1302,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -1270,9 +1313,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -1287,9 +1330,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -1299,20 +1342,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -1321,14 +1364,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -1337,21 +1380,21 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -1361,9 +1404,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -1372,9 +1415,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -1393,9 +1436,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -1406,10 +1449,16 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "valuable" @@ -1419,9 +1468,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -1441,23 +1490,23 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -1466,9 +1515,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1476,9 +1525,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -1489,9 +1538,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "winapi" @@ -1511,11 +1560,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1530,16 +1579,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1548,153 +1588,106 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "e6f5bb5257f2407a5425c6e749bfd9692192a73e70a6060516ac04f889087d68" dependencies = [ "memchr", ] [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 21db189..4f2f606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,31 @@ [package] name = "breeze" -version = "0.2.6" +version = "0.2.7" edition = "2021" [dependencies] -axum = { version = "0.7.5", features = ["macros", "http2"] } -tower = "0.4.13" -http = "1.1.0" +axum-extra = { version = "0.10.0", default-features = false, features = [ + "tracing", + "typed-header", +] } +axum = { version = "0.8.1", features = ["macros", "http2"] } +tower = "0.5" +http = "1.2" +headers = "0.4" tokio = { version = "1", features = ["full"] } -tokio-util = { version = "0.7.4", features = ["full"] } +tokio-util = { version = "0.7", features = ["full"] } tokio-stream = "0.1" tracing = "0.1" tracing-subscriber = "0.3" bytes = "1" -async-recursion = "1.0.0" rand = "0.8.5" walkdir = "2" anyhow = "1.0" serde = { version = "1.0", features = ["derive"] } -serde_with = "3.4.0" +serde_with = "3.12" toml = "0.8.2" argh = "0.1.12" -dashmap = { version = "5.5.3", features = ["rayon", "inline"] } +dashmap = { version = "6.1.0", features = ["rayon", "inline"] } rayon = "1.8" atomic-time = "0.1.4" -img-parts = "0.3.0" +img-parts = "0.3" diff --git a/README.md b/README.md index e3ddab7..1b3719d 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,24 @@ breeze is a simple, performant file upload server. The primary instance is https://picture.wtf. ## Features -Compared to the old Express.js backend, breeze has +- Basic upload API tailored towards ShareX - Streamed uploading - Streamed downloading (on larger files) -- Upload caching -- Generally faster speeds overall +- Upload caching in memory - Temporary uploads - Automatic exif data removal -At this time, breeze does not support encrypted uploads on disk. - ## Installation -I wrote breeze with the intention of running it in a container, but it runs just fine outside of one. +On picture.wtf, breeze's primary instance, it is ran using a NixOS module. If you would like to do that too, it is provided by the Nix flake in this repository. -Either way, you need to start off by cloning the Git repository. +It is very much possible to run and deploy breeze without doing that, though. Containerised and bare-metal deployments are also supported. Instructions for those are below. + +To begin, clone the Git repository: ```bash git clone https://git.min.rip/min/breeze.git ``` -To run it in Docker, I recommend using Docker Compose. An example `docker-compose.yaml` configuration is below. You can start it using `docker compose up -d`. +If you would like to run it as a Docker container, here is an example `docker-compose.yaml` that may be useful for reference. ``` version: '3.6' @@ -40,14 +39,14 @@ services: ports: - 8383:8000 ``` -For this configuration, it is expected that: +With this configuration, it is expected that: * there is a clone of the Git repository in the `./breeze` folder * there is a `breeze.toml` config file in current directory * there is a directory at `/srv/uploads` for storing uploads * port 8383 will be made accessible to the Internet somehow (either forwarding the port through your firewall directly, or passing it through a reverse proxy) * you want the uploads to be owned by the user on your system with id 1000. (this is usually your user) -It can also be installed directly if you have the Rust toolchain installed: +It can also be installed directly if the Rust toolchain is installed: ```bash cargo install --path . ``` diff --git a/src/cache.rs b/src/cache.rs index 20b1567..df896e3 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -67,9 +67,9 @@ pub struct Cache { } impl Cache { - pub fn from_config(cfg: config::CacheConfig) -> Self { + pub fn with_config(cfg: config::CacheConfig) -> Self { Self { - map: DashMap::with_capacity(256), + map: DashMap::with_capacity(64), length: AtomicUsize::new(0), cfg, @@ -212,7 +212,7 @@ impl Cache { /// Returns if an upload is able to be cached /// with the current caching rules #[inline(always)] - pub fn will_use(&self, length: usize) -> bool { + pub fn will_use(&self, length: u64) -> bool { length <= self.cfg.max_length } diff --git a/src/config.rs b/src/config.rs index 5e53245..7598fd2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -35,7 +35,7 @@ pub struct EngineConfig { /// Maximum size of an upload that will be accepted. /// Files above this size can not be uploaded. - pub max_upload_len: Option, + pub max_upload_len: Option, /// Maximum lifetime of a temporary upload #[serde_as(as = "DurationSeconds")] @@ -43,7 +43,7 @@ pub struct EngineConfig { /// Maximum length (in bytes) a file can be before the server will /// decide not to remove its EXIF data. - pub max_strip_len: usize, + pub max_strip_len: u64, /// Motd displayed when the server's index page is visited. /// @@ -64,7 +64,7 @@ pub struct DiskConfig { pub struct CacheConfig { /// The maximum length in bytes that a file can be /// before it skips cache (in seconds) - pub max_length: usize, + pub max_length: u64, /// The amount of time a file can last inside the cache (in seconds) #[serde_as(as = "DurationSeconds")] diff --git a/src/disk.rs b/src/disk.rs index 488cd47..6923701 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -4,7 +4,7 @@ use bytes::Bytes; use tokio::{ fs::File, io::{self, AsyncWriteExt}, - sync::mpsc::{self, Receiver, Sender}, + sync::mpsc, }; use tracing::debug; use walkdir::WalkDir; @@ -18,7 +18,7 @@ pub struct Disk { } impl Disk { - pub fn from_config(cfg: config::DiskConfig) -> Self { + pub fn with_config(cfg: config::DiskConfig) -> Self { Self { cfg } } @@ -40,7 +40,7 @@ impl Disk { /// Try to open a file on disk, and if we didn't find it, /// then return [`None`]. - pub async fn open(&self, saved_name: &str) -> Result, io::Error> { + pub async fn open(&self, saved_name: &str) -> io::Result> { let p = self.path_for(saved_name); match File::open(p).await { @@ -53,14 +53,22 @@ impl Disk { } /// Get the size of an upload's file - pub async fn len(&self, f: &File) -> Result { - Ok(f.metadata().await?.len() as usize) + pub async fn len(&self, f: &File) -> io::Result { + Ok(f.metadata().await?.len()) + } + + /// Remove an upload from disk. + pub async fn remove(&self, saved_name: &str) -> io::Result<()> { + let p = self.path_for(saved_name); + + tokio::fs::remove_file(p).await } /// Create a background I/O task - pub async fn start_save(&self, saved_name: &str) -> Sender { + pub async fn start_save(&self, saved_name: &str) -> mpsc::UnboundedSender { // start a task that handles saving files to disk (we can save to cache/disk in parallel that way) - let (tx, mut rx): (Sender, Receiver) = mpsc::channel(256); + let (tx, mut rx): (mpsc::UnboundedSender, mpsc::UnboundedReceiver) = + mpsc::unbounded_channel(); let p = self.path_for(saved_name); diff --git a/src/engine.rs b/src/engine.rs index 55dccb6..7461744 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,4 +1,5 @@ use std::{ + ops::Bound, sync::{ atomic::{AtomicUsize, Ordering}, Arc, @@ -10,9 +11,12 @@ use axum::body::BodyDataStream; use bytes::{BufMut, Bytes, BytesMut}; use img_parts::{DynImage, ImageEXIF}; use rand::distributions::{Alphanumeric, DistString}; -use tokio::{fs::File, io::AsyncReadExt}; +use tokio::{ + fs::File, + io::{AsyncReadExt, AsyncSeekExt}, +}; use tokio_stream::StreamExt; -use tracing::{debug, info}; +use tracing::{debug, error, info}; use crate::{cache, config, disk}; @@ -20,12 +24,18 @@ use crate::{cache, config, disk}; pub enum UploadData { /// Send back the data from memory Cache(Bytes), - /// Stream the file from disk to the client - Disk(File, usize), + Disk(tokio::io::Take), } -/// Rejection outcomes of an [`Engine::process`] call +pub struct UploadResponse { + pub full_len: u64, + pub range: (u64, u64), + pub data: UploadData, +} + +/// Non-error outcomes of an [`Engine::process`] call. +/// Some are rejections. pub enum ProcessOutcome { /// The upload was successful. /// We give the user their file's URL @@ -41,26 +51,68 @@ pub enum ProcessOutcome { TemporaryUploadLifetimeTooLong, } -/// breeze engine! this is the core of everything +/// Non-error outcomes of an [`Engine::get`] call. +pub enum GetOutcome { + /// Successfully read upload. + Success(UploadResponse), + + /// The upload was not found anywhere + NotFound, + + /// A range was requested that exceeds an upload's bounds + RangeNotSatisfiable, +} + +/// breeze engine pub struct Engine { - /// Cached count of uploaded files. + /// Cached count of uploaded files pub upl_count: AtomicUsize, /// Engine configuration pub cfg: config::EngineConfig, - /// The in-memory cache that cached uploads are stored in. + /// The in-memory cache that cached uploads are stored in cache: Arc, /// An interface to the on-disk upload store disk: disk::Disk, } +fn resolve_range(range: Option, full_len: u64) -> Option<(u64, u64)> { + let last_byte = full_len - 1; + + let (start, end) = + if let Some((start, end)) = range.and_then(|r| r.satisfiable_ranges(full_len).next()) { + // satisfiable_ranges will never return Excluded so this is ok + let start = if let Bound::Included(start_incl) = start { + start_incl + } else { + 0 + }; + let end = if let Bound::Included(end_incl) = end { + end_incl + } else { + last_byte + }; + + (start, end) + } else { + (0, last_byte) + }; + + // catch ranges we can't satisfy + if end > last_byte || start > end { + return None; + } + + Some((start, end)) +} + impl Engine { - /// Creates a new instance of the breeze engine. - pub fn from_config(cfg: config::EngineConfig) -> Self { - let cache = cache::Cache::from_config(cfg.cache.clone()); - let disk = disk::Disk::from_config(cfg.disk.clone()); + /// Creates a new instance of the engine + pub fn with_config(cfg: config::EngineConfig) -> Self { + let cache = cache::Cache::with_config(cfg.cache.clone()); + let disk = disk::Disk::with_config(cfg.disk.clone()); let cache = Arc::new(cache); @@ -78,55 +130,99 @@ impl Engine { } } - /// Fetch an upload + /// Fetch an upload. /// /// This will first try to read from cache, and then disk after. /// If an upload is eligible to be cached, it will be cached and /// sent back as a cache response instead of a disk response. - pub async fn get(&self, saved_name: &str) -> anyhow::Result> { - // check the cache first - if let Some(u) = self.cache.get(saved_name) { - return Ok(Some(UploadData::Cache(u))); - } - - // now, check if we have it on disk - let mut f = if let Some(f) = self.disk.open(saved_name).await? { - f + /// + /// If there is a range, it is applied at the very end. + pub async fn get( + &self, + saved_name: &str, + range: Option, + ) -> anyhow::Result { + let data = if let Some(u) = self.cache.get(saved_name) { + u } else { - // file didn't exist - return Ok(None); + // now, check if we have it on disk + let mut f = if let Some(f) = self.disk.open(saved_name).await? { + f + } else { + // file didn't exist + return Ok(GetOutcome::NotFound); + }; + + let full_len = self.disk.len(&f).await?; + + // if possible, recache and send a cache response + // else, send a disk response + if self.cache.will_use(full_len) { + // read file from disk + let mut data = BytesMut::with_capacity(full_len.try_into()?); + + // read file from disk and if it fails at any point, return 500 + loop { + match f.read_buf(&mut data).await { + Ok(n) => { + if n == 0 { + break; + } + } + Err(e) => Err(e)?, + } + } + + let data = data.freeze(); + + // re-insert it into cache + self.cache.add(saved_name, data.clone()); + + data + } else { + let (start, end) = if let Some(range) = resolve_range(range, full_len) { + range + } else { + return Ok(GetOutcome::RangeNotSatisfiable); + }; + + let range_len = (end - start) + 1; + + f.seek(std::io::SeekFrom::Start(start)).await?; + let f = f.take(range_len); + + let res = UploadResponse { + full_len, + range: (start, end), + data: UploadData::Disk(f), + }; + return Ok(GetOutcome::Success(res)); + } }; - let len = self.disk.len(&f).await?; + let full_len = data.len() as u64; + let (start, end) = if let Some(range) = resolve_range(range, full_len) { + range + } else { + return Ok(GetOutcome::RangeNotSatisfiable); + }; - // can this be recached? - if self.cache.will_use(len) { - // read file from disk - let mut full = BytesMut::with_capacity(len); + // cut down to range + let data = data.slice((start as usize)..=(end as usize)); - // read file from disk and if it fails at any point, return 500 - loop { - match f.read_buf(&mut full).await { - Ok(n) => { - if n == 0 { - break; - } - } - Err(e) => Err(e)?, - } - } - - let full = full.freeze(); - - // re-insert it into cache - self.cache.add(saved_name, full.clone()); - - return Ok(Some(UploadData::Cache(full))); - } - - Ok(Some(UploadData::Disk(f, len))) + // build response + let res = UploadResponse { + full_len, + range: (start, end), + data: UploadData::Cache(data), + }; + Ok(GetOutcome::Success(res)) } + /// Check if we have an upload stored anywhere. + /// + /// This is only used to prevent `saved_name` collisions!! + /// It is not used to deliver "not found" errors. pub async fn has(&self, saved_name: &str) -> bool { if self.cache.has(saved_name) { return true; @@ -143,43 +239,56 @@ impl Engine { /// Generate a new saved name for an upload. /// - /// This will call itself recursively if it picks - /// a name that's already used. (it is rare) - #[async_recursion::async_recursion] + /// If it picks a name that already exists, it will try again. pub async fn gen_saved_name(&self, ext: &str) -> String { - // generate a 6-character alphanumeric string - let mut saved_name: String = Alphanumeric.sample_string(&mut rand::thread_rng(), 6); + loop { + // generate a 6-character alphanumeric string + let mut saved_name: String = Alphanumeric.sample_string(&mut rand::thread_rng(), 6); - // if we have an extension, add it now - if !ext.is_empty() { - saved_name.push('.'); - saved_name.push_str(ext); - } + // if we have an extension, add it now + if !ext.is_empty() { + saved_name.push('.'); + saved_name.push_str(ext); + } - if !self.has(&saved_name).await { - saved_name - } else { - // we had a name collision! try again.. - info!("name collision! saved_name= {}", saved_name); - self.gen_saved_name(ext).await + if !self.has(&saved_name).await { + break saved_name; + } else { + // there was a name collision. loop and try again + info!("name collision! saved_name= {}", saved_name); + } } } + /// Wipe out an upload from all storage. + /// + /// This is for deleting failed uploads only!! + pub async fn remove(&self, saved_name: &str) -> anyhow::Result<()> { + info!("!! removing upload: {saved_name}"); + + self.cache.remove(saved_name); + self.disk.remove(saved_name).await?; + + info!("!! successfully removed upload"); + + Ok(()) + } + /// Save a file to disk, and optionally cache. /// /// This also handles custom file lifetimes and EXIF data removal. pub async fn save( &self, saved_name: &str, - provided_len: usize, + provided_len: u64, mut use_cache: bool, mut stream: BodyDataStream, lifetime: Option, keep_exif: bool, - ) -> Result<(), anyhow::Error> { + ) -> anyhow::Result<()> { // if we're using cache, make some space to store the upload in let mut data = if use_cache { - BytesMut::with_capacity(provided_len) + BytesMut::with_capacity(provided_len.try_into()?) } else { BytesMut::new() }; @@ -214,7 +323,7 @@ impl Engine { if !coalesce_and_strip { if let Some(ref tx) = tx { debug!("sending chunk to i/o task"); - tx.send(chunk.clone()).await?; + tx.send(chunk.clone())?; } } @@ -256,7 +365,7 @@ impl Engine { // send what we did over to the i/o task, all in one chunk if let Some(ref tx) = tx { debug!("sending filled buffer to i/o task"); - tx.send(data.clone()).await?; + tx.send(data.clone())?; } data @@ -275,29 +384,24 @@ impl Engine { }; } - info!("finished processing upload!!"); - - // if all goes well, increment the cached upload counter - self.upl_count.fetch_add(1, Ordering::Relaxed); - Ok(()) } pub async fn process( &self, ext: &str, - provided_len: usize, + provided_len: u64, stream: BodyDataStream, lifetime: Option, keep_exif: bool, - ) -> Result { + ) -> anyhow::Result { // if the upload size is greater than our max file size, deny it now if self.cfg.max_upload_len.is_some_and(|l| provided_len > l) { return Ok(ProcessOutcome::UploadTooLarge); } // if the upload size is smaller than the specified maximum, we use the cache! - let use_cache: bool = self.cache.will_use(provided_len); + let use_cache = self.cache.will_use(provided_len); // if a temp file is too big for cache, reject it now if lifetime.is_some() && !use_cache { @@ -313,19 +417,33 @@ impl Engine { let saved_name = self.gen_saved_name(ext).await; // save it - self.save( - &saved_name, - provided_len, - use_cache, - stream, - lifetime, - keep_exif, - ) - .await?; + let save_result = self + .save( + &saved_name, + provided_len, + use_cache, + stream, + lifetime, + keep_exif, + ) + .await; + + // If anything fails, delete the upload and return the error + if save_result.is_err() { + error!("failed processing upload!"); + + self.remove(&saved_name).await?; + save_result?; + } // format and send back the url let url = format!("{}/p/{}", self.cfg.base_url, saved_name); + // if all goes well, increment the cached upload counter + self.upl_count.fetch_add(1, Ordering::Relaxed); + + info!("finished processing upload!"); + Ok(ProcessOutcome::Success(url)) } } diff --git a/src/main.rs b/src/main.rs index 88c8a4d..b3275db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,10 +28,10 @@ struct Args { #[tokio::main] async fn main() { - // read & parse args + // Read & parse args let args: Args = argh::from_env(); - // read & parse config + // Read & parse config let cfg: config::Config = { let config_str = fs::read_to_string(args.config).await.expect( "failed to read config file! make sure it exists and you have read permissions", @@ -42,38 +42,38 @@ async fn main() { }) }; + // Set up tracing tracing_subscriber::fmt() .with_max_level(cfg.logger.level) .init(); + // Check config { let save_path = cfg.engine.disk.save_path.clone(); if !save_path.exists() || !save_path.is_dir() { panic!("the save path does not exist or is not a directory! this is invalid"); } } - if cfg.engine.upload_key.is_empty() { warn!("engine upload_key is empty! no key will be required for uploading new files"); } - // create engine - let engine = Engine::from_config(cfg.engine); + // Create engine + let engine = Engine::with_config(cfg.engine); - // build main router + // Build main router let app = Router::new() .route("/new", post(new::new)) - .route("/p/:name", get(view::view)) + .route("/p/{saved_name}", get(view::view)) .route("/", get(index::index)) .route("/robots.txt", get(index::robots_txt)) .with_state(Arc::new(engine)); - // start web server + // Start web server + info!("starting server."); let listener = TcpListener::bind(&cfg.http.listen_on) .await .expect("failed to bind to given `http.listen_on` address! make sure it's valid, and the port isn't already bound"); - - info!("starting server."); axum::serve(listener, app) .with_graceful_shutdown(shutdown_signal()) .await diff --git a/src/new.rs b/src/new.rs index 1889398..10b59c9 100644 --- a/src/new.rs +++ b/src/new.rs @@ -4,7 +4,9 @@ use axum::{ body::Body, extract::{Query, State}, }; -use http::{header, HeaderMap, HeaderValue, StatusCode}; +use axum_extra::TypedHeader; +use headers::ContentLength; +use http::StatusCode; use serde::Deserialize; use serde_with::{serde_as, DurationSeconds}; @@ -34,7 +36,7 @@ pub struct NewRequest { pub async fn new( State(engine): State>, Query(req): Query, - headers: HeaderMap, + TypedHeader(ContentLength(content_length)): TypedHeader, body: Body, ) -> Result { // check upload key, if i need to @@ -53,19 +55,14 @@ pub async fn new( .unwrap_or_default() .to_string(); - // read and parse content-length, and if it fails just assume it's really high so it doesn't cache - let content_length = headers - .get(header::CONTENT_LENGTH) - .unwrap_or(&HeaderValue::from_static("")) - .to_str() - .map(|s| s.parse::()) - .unwrap() - .unwrap_or(usize::MAX); - // turn body into stream let stream = Body::into_data_stream(body); - // pass it off to the engine to be processed! + // pass it off to the engine to be processed + // -- + // also, error responses here don't get represented properly in ShareX most of the time + // they don't expect the connection to close before they're done uploading, i think + // so it will just present the user with a "connection closed" error match engine .process( &extension, diff --git a/src/view.rs b/src/view.rs index 3691c23..db7a7e6 100644 --- a/src/view.rs +++ b/src/view.rs @@ -6,10 +6,12 @@ use axum::{ response::{IntoResponse, Response}, }; +use axum_extra::TypedHeader; +use headers::Range; use http::{HeaderValue, StatusCode}; use tokio_util::io::ReaderStream; -use crate::engine::UploadData; +use crate::engine::{GetOutcome, UploadData, UploadResponse}; /// Responses for a failed view operation pub enum ViewError { @@ -18,80 +20,91 @@ pub enum ViewError { /// Will send status code 500 with a plaintext "internal server error" message. InternalServerError, -} -impl IntoResponse for UploadData { - fn into_response(self) -> Response { - match self { - UploadData::Disk(file, len) => { - // create our content-length header - let len_str = len.to_string(); - let content_length = HeaderValue::from_str(&len_str).unwrap(); - - // create a streamed body response (we want to stream larger files) - let stream = ReaderStream::new(file); - let body = Body::from_stream(stream); - - // extract mutable headers from the response - let mut res = body.into_response(); - let headers = res.headers_mut(); - - // clear headers, browser can imply content type - headers.clear(); - - // insert Content-Length header - // that way the browser shows how big a file is when it's being downloaded - headers.insert("Content-Length", content_length); - - res - } - UploadData::Cache(data) => { - // extract mutable headers from the response - let mut res = data.into_response(); - let headers = res.headers_mut(); - - // clear the headers, let the browser imply it - headers.clear(); - - res - } - } - } + /// Sends status code 206 with a plaintext "range not satisfiable" message. + RangeNotSatisfiable, } impl IntoResponse for ViewError { fn into_response(self) -> Response { match self { - ViewError::NotFound => ( - StatusCode::NOT_FOUND, - "not found!" - ).into_response(), - - ViewError::InternalServerError => ( - StatusCode::INTERNAL_SERVER_ERROR, - "internal server error!" - ).into_response(), + ViewError::NotFound => (StatusCode::NOT_FOUND, "Not found!").into_response(), + + ViewError::InternalServerError => { + (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error!").into_response() + } + + ViewError::RangeNotSatisfiable => { + (StatusCode::RANGE_NOT_SATISFIABLE, "Range not satisfiable!").into_response() + } } } } -/// The request handler for /p/* path. +impl IntoResponse for UploadResponse { + fn into_response(self) -> Response { + let (start, end) = self.range; + let range_len = (end - start) + 1; + + let mut res = match self.data { + UploadData::Cache(data) => data.into_response(), + UploadData::Disk(file) => { + let reader_stream = ReaderStream::new(file); + let body = Body::from_stream(reader_stream); + let mut res = body.into_response(); + let headers = res.headers_mut(); + + // add Content-Length header so the browser shows how big a file is when it's being downloaded + let content_length = HeaderValue::from_str(&range_len.to_string()) + .expect("construct content-length header failed"); + headers.insert("Content-Length", content_length); + + res + } + }; + + let headers = res.headers_mut(); + + // remove content-type, browser can imply content type + headers.remove("Content-Type"); + headers.insert("Accept-Ranges", HeaderValue::from_static("bytes")); + // ^-- indicate that byte ranges are supported. maybe unneeded, but probably good + + // if it is not the full size, add relevant headers/status for range request + if range_len != self.full_len { + let content_range = + HeaderValue::from_str(&format!("bytes {}-{}/{}", start, end, self.full_len)) + .expect("construct content-range header failed"); + + headers.insert("Content-Range", content_range); + *res.status_mut() = StatusCode::PARTIAL_CONTENT; + } + + res + } +} + +/// GET request handler for /p/* path. /// All file views are handled here. #[axum::debug_handler] pub async fn view( State(engine): State>, Path(original_path): Path, -) -> Result { + range: Option>, +) -> Result { let saved_name = if let Some(Some(n)) = original_path.file_name().map(OsStr::to_str) { n } else { return Err(ViewError::NotFound); }; - // get result from the engine! - match engine.get(saved_name).await { - Ok(Some(u)) => Ok(u), - Ok(None) => Err(ViewError::NotFound), + let range = range.map(|th| th.0); + + // get result from the engine + match engine.get(saved_name, range).await { + Ok(GetOutcome::Success(res)) => Ok(res), + Ok(GetOutcome::NotFound) => Err(ViewError::NotFound), + Ok(GetOutcome::RangeNotSatisfiable) => Err(ViewError::RangeNotSatisfiable), Err(_) => Err(ViewError::InternalServerError), } }