diff --git a/Cargo.lock b/Cargo.lock index 681518e..fb0348a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" 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" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "argon2" @@ -105,21 +105,21 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -157,15 +157,15 @@ dependencies = [ [[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.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -211,7 +211,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -219,9 +219,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" dependencies = [ "axum", "axum-core", @@ -244,11 +244,10 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -256,17 +255,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -322,9 +321,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" dependencies = [ "arrayref", "arrayvec", @@ -356,15 +355,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.3" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950" +checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -385,9 +387,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -395,9 +397,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -407,11 +409,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -443,9 +445,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cookie" @@ -493,9 +495,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -517,9 +519,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "critical-section" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" [[package]] name = "crossbeam-queue" @@ -696,6 +698,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -890,9 +898,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "h2" @@ -966,12 +974,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -980,9 +982,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "heed" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc30da4a93ff8cb98e535d595d6de42731d4719d707bc1c86f579158751a24e" +checksum = "7d4f449bab7320c56003d37732a917e18798e2f1709d80263face2b4f9436ddb" dependencies = [ "bitflags", "byteorder", @@ -1097,9 +1099,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http", @@ -1131,9 +1133,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -1144,7 +1146,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -1171,9 +1172,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1190,9 +1191,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "is_terminal_polyfill" @@ -1223,9 +1224,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" @@ -1251,9 +1252,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lmdb-master-sys" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57640c190703d5ccf4a86aff4aeb749b2d287a8cb1723c76b51f39d77ab53b24" +checksum = "472c3760e2a8d0f61f322fb36788021bb36d573c502b50fa3e2bcaac3ec326c9" dependencies = [ "cc", "doxygen-rs", @@ -1278,9 +1279,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" [[package]] name = "mainline" @@ -1330,11 +1331,11 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1393,18 +1394,21 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "object" -version = "0.36.1" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "opaque-debug" @@ -1554,26 +1558,6 @@ dependencies = [ "siphasher", ] -[[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" @@ -1641,13 +1625,20 @@ dependencies = [ ] [[package]] -name = "postcard" -version = "1.0.8" +name = "portable-atomic" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" dependencies = [ "cobs", - "embedded-io", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless", "serde", ] @@ -1660,9 +1651,12 @@ 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" @@ -1754,9 +1748,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" dependencies = [ "bytes", "pin-project-lite", @@ -1772,9 +1766,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand", @@ -1789,22 +1783,22 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" dependencies = [ "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1841,18 +1835,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -1861,14 +1855,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -1882,13 +1876,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -1899,9 +1893,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -2003,18 +1997,18 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", @@ -2025,9 +2019,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "once_cell", "ring", @@ -2039,25 +2033,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -2137,9 +2130,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -2165,9 +2158,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -2176,11 +2169,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2197,9 +2191,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2229,9 +2223,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -2253,6 +2247,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" @@ -2350,9 +2350,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.71" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -2406,9 +2406,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -2419,18 +2419,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -2545,9 +2545,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -2579,9 +2579,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -2592,14 +2592,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper 0.1.2", "tokio", "tower-layer", "tower-service", @@ -2642,15 +2642,15 @@ 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" @@ -2734,15 +2734,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -2794,9 +2794,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[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 = "want" @@ -2841,9 +2841,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -2892,9 +2892,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] @@ -3101,13 +3101,34 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/docs/src/spec/auth.md b/docs/src/spec/auth.md index 62c57bf..92a3041 100644 --- a/docs/src/spec/auth.md +++ b/docs/src/spec/auth.md @@ -79,7 +79,7 @@ pubkyauth:/// ```abnf AuthToken = signature namespace version timestamp pubky capabilities -signature = 64OCTET ; ed25519 signature over encoded DNS packet +signature = 64OCTET ; ed25519 signature over the rest of the token. namespace = %x50.55.42.4b.59.3a.41.55.54.48 ; "PUBKY:AUTH" in UTF-8 (10 bytes) version = 1*OCTET ; Version of the AuthToken for future proofing. timestamp = 8OCTET ; Big-endian UNIX timestamp in microseconds diff --git a/pubky-common/src/session.rs b/pubky-common/src/session.rs index 5ce64d0..972652c 100644 --- a/pubky-common/src/session.rs +++ b/pubky-common/src/session.rs @@ -68,6 +68,10 @@ impl Session { } pub fn deserialize(bytes: &[u8]) -> Result { + if bytes.is_empty() { + return Err(Error::EmptyPayload); + } + if bytes[0] > 0 { return Err(Error::UnknownVersion); } @@ -80,8 +84,10 @@ impl Session { pub type Result = core::result::Result; -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Debug, PartialEq)] pub enum Error { + #[error("Empty payload")] + EmptyPayload, #[error("Unknown version")] UnknownVersion, #[error(transparent)] @@ -123,4 +129,11 @@ mod tests { assert_eq!(deseiralized, session) } + + #[test] + fn deserialize() { + let result = Session::deserialize(&[]); + + assert_eq!(result, Err(Error::EmptyPayload)); + } } diff --git a/pubky-homeserver/src/config.rs b/pubky-homeserver/src/config.rs index 4645f4c..6e3dea5 100644 --- a/pubky-homeserver/src/config.rs +++ b/pubky-homeserver/src/config.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use pkarr::Keypair; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use std::{ fmt::Debug, path::{Path, PathBuf}, @@ -12,34 +12,102 @@ use tracing::info; use pubky_common::timestamp::Timestamp; -const DEFAULT_HOMESERVER_PORT: u16 = 6287; +// === Database === const DEFAULT_STORAGE_DIR: &str = "pubky"; +pub const DEFAULT_MAP_SIZE: usize = 10995116277760; // 10TB (not = disk-space used) -/// Server configuration -#[derive(Serialize, Deserialize, Clone)] -pub struct Config { - testnet: bool, +// === Server == +pub const DEFAULT_LIST_LIMIT: u16 = 100; +pub const DEFAULT_MAX_LIST_LIMIT: u16 = 1000; + +#[derive(Serialize, Deserialize, Clone, PartialEq)] +struct ConfigToml { + testnet: Option, port: Option, bootstrap: Option>, - domain: String, - /// Path to the storage directory + domain: Option, + storage: Option, + secret_key: Option, + dht_request_timeout: Option, + default_list_limit: Option, + max_list_limit: Option, + db_map_size: Option, +} + +/// Server configuration +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config { + /// Whether or not this server is running in a testnet. + testnet: bool, + /// The configured port for this server. + port: u16, + /// Bootstrapping DHT nodes. + /// + /// Helpful to run the server locally or in testnet. + bootstrap: Option>, + /// A public domain for this server + /// necessary for web browsers running in https environment. + domain: Option, + /// Path to the storage directory. /// /// Defaults to a directory in the OS data directory - storage: Option, - #[serde(deserialize_with = "secret_key_deserialize")] - secret_key: Option<[u8; 32]>, - + storage: PathBuf, + /// Server keypair. + /// + /// Defaults to a random keypair. + keypair: Keypair, dht_request_timeout: Option, + /// The default limit of a list api if no `limit` query parameter is provided. + /// + /// Defaults to `100` + default_list_limit: u16, + /// The maximum limit of a list api, even if a `limit` query parameter is provided. + /// + /// Defaults to `1000` + max_list_limit: u16, + + // === Database params === + db_map_size: usize, } impl Config { - /// Load the config from a file. - pub async fn load(path: impl AsRef) -> Result { - let s = tokio::fs::read_to_string(path.as_ref()) - .await - .with_context(|| format!("failed to read {}", path.as_ref().to_string_lossy()))?; + fn try_from_str(value: &str) -> Result { + let config_toml: ConfigToml = toml::from_str(value)?; - let config: Config = toml::from_str(&s)?; + let keypair = if let Some(secret_key) = config_toml.secret_key { + let secret_key = deserialize_secret_key(secret_key)?; + Keypair::from_secret_key(&secret_key) + } else { + Keypair::random() + }; + + let storage = { + let dir = if let Some(storage) = config_toml.storage { + storage + } else { + let path = dirs_next::data_dir().ok_or_else(|| { + anyhow!("operating environment provides no directory for application data") + })?; + path.join(DEFAULT_STORAGE_DIR) + }; + + dir.join("homeserver") + }; + + let config = Config { + testnet: config_toml.testnet.unwrap_or(false), + port: config_toml.port.unwrap_or(0), + bootstrap: config_toml.bootstrap, + domain: config_toml.domain, + keypair, + storage, + dht_request_timeout: config_toml.dht_request_timeout, + default_list_limit: config_toml.default_list_limit.unwrap_or(DEFAULT_LIST_LIMIT), + max_list_limit: config_toml + .default_list_limit + .unwrap_or(DEFAULT_MAX_LIST_LIMIT), + db_map_size: config_toml.db_map_size.unwrap_or(DEFAULT_MAP_SIZE), + }; if config.testnet { let testnet_config = Config::testnet(); @@ -53,121 +121,185 @@ impl Config { Ok(config) } + /// Load the config from a file. + pub async fn load(path: impl AsRef) -> Result { + let s = tokio::fs::read_to_string(path.as_ref()) + .await + .with_context(|| format!("failed to read {}", path.as_ref().to_string_lossy()))?; + + Config::try_from_str(&s) + } + /// Testnet configurations pub fn testnet() -> Self { let testnet = pkarr::mainline::Testnet::new(10).unwrap(); info!(?testnet.bootstrap, "Testnet bootstrap nodes"); - let bootstrap = Some(testnet.bootstrap.to_owned()); - let storage = Some( - std::env::temp_dir() - .join(Timestamp::now().to_string()) - .join(DEFAULT_STORAGE_DIR), - ); - - Self { - bootstrap, - storage, - port: Some(15411), - dht_request_timeout: Some(Duration::from_millis(10)), - ..Default::default() + Config { + port: 15411, + dht_request_timeout: None, + db_map_size: DEFAULT_MAP_SIZE, + ..Self::test(&testnet) } } /// Test configurations pub fn test(testnet: &pkarr::mainline::Testnet) -> Self { let bootstrap = Some(testnet.bootstrap.to_owned()); - let storage = Some( - std::env::temp_dir() - .join(Timestamp::now().to_string()) - .join(DEFAULT_STORAGE_DIR), - ); + let storage = std::env::temp_dir() + .join(Timestamp::now().to_string()) + .join(DEFAULT_STORAGE_DIR); Self { + testnet: true, bootstrap, storage, + db_map_size: 10485760, + dht_request_timeout: Some(Duration::from_millis(10)), ..Default::default() } } pub fn port(&self) -> u16 { - self.port.unwrap_or(DEFAULT_HOMESERVER_PORT) + self.port } pub fn bootstsrap(&self) -> Option> { self.bootstrap.to_owned() } - pub fn domain(&self) -> &str { + pub fn domain(&self) -> &Option { &self.domain } - /// Get the path to the storage directory - pub fn storage(&self) -> Result { - let dir = if let Some(storage) = &self.storage { - PathBuf::from(storage) - } else { - let path = dirs_next::data_dir().ok_or_else(|| { - anyhow!("operating environment provides no directory for application data") - })?; - path.join(DEFAULT_STORAGE_DIR) - }; - - Ok(dir.join("homeserver")) + pub fn keypair(&self) -> &Keypair { + &self.keypair } - pub fn keypair(&self) -> Keypair { - Keypair::from_secret_key(&self.secret_key.unwrap_or_default()) + pub fn default_list_limit(&self) -> u16 { + self.default_list_limit + } + + pub fn max_list_limit(&self) -> u16 { + self.max_list_limit + } + + /// Get the path to the storage directory + pub fn storage(&self) -> &PathBuf { + &self.storage } pub(crate) fn dht_request_timeout(&self) -> Option { self.dht_request_timeout } + + pub(crate) fn db_map_size(&self) -> usize { + self.db_map_size + } } impl Default for Config { fn default() -> Self { Self { testnet: false, - port: Some(0), + port: 0, bootstrap: None, - domain: "localhost".to_string(), - storage: None, - secret_key: None, + domain: None, + storage: storage(None) + .expect("operating environment provides no directory for application data"), + keypair: Keypair::random(), dht_request_timeout: None, + default_list_limit: DEFAULT_LIST_LIMIT, + max_list_limit: DEFAULT_MAX_LIST_LIMIT, + db_map_size: DEFAULT_MAP_SIZE, } } } -fn secret_key_deserialize<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let opt: Option = Option::deserialize(deserializer)?; +fn deserialize_secret_key(s: String) -> anyhow::Result<[u8; 32]> { + let bytes = + hex::decode(s).map_err(|_| anyhow!("secret_key in config.toml should hex encoded"))?; - match opt { - Some(s) => { - let bytes = hex::decode(s).map_err(serde::de::Error::custom)?; + if bytes.len() != 32 { + return Err(anyhow!(format!( + "secret_key in config.toml should be 32 bytes in hex (64 characters), got: {}", + bytes.len() + ))); + } - if bytes.len() != 32 { - return Err(serde::de::Error::custom("Expected a 32-byte array")); + let mut arr = [0u8; 32]; + arr.copy_from_slice(&bytes); + + Ok(arr) +} + +fn storage(storage: Option) -> Result { + let dir = if let Some(storage) = storage { + PathBuf::from(storage) + } else { + let path = dirs_next::data_dir().ok_or_else(|| { + anyhow!("operating environment provides no directory for application data") + })?; + path.join(DEFAULT_STORAGE_DIR) + }; + + Ok(dir.join("homeserver")) +} + +#[cfg(test)] +mod tests { + use pkarr::mainline::Testnet; + + use super::*; + + #[test] + fn parse_empty() { + let config = Config::try_from_str("").unwrap(); + + assert_eq!( + config, + Config { + keypair: config.keypair.clone(), + ..Default::default() } + ) + } - let mut arr = [0u8; 32]; - arr.copy_from_slice(&bytes); - Ok(Some(arr)) - } - None => Ok(None), - } -} - -impl Debug for Config { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_map() - .entry(&"testnet", &self.testnet) - .entry(&"port", &self.port()) - .entry(&"storage", &self.storage()) - .entry(&"public_key", &self.keypair().public_key()) - .finish() + #[test] + fn config_test() { + let testnet = Testnet::new(3).unwrap(); + let config = Config::test(&testnet); + + assert_eq!( + config, + Config { + testnet: true, + bootstrap: testnet.bootstrap.into(), + db_map_size: 10485760, + dht_request_timeout: Some(Duration::from_millis(10)), + + storage: config.storage.clone(), + keypair: config.keypair.clone(), + ..Default::default() + } + ) + } + + #[test] + fn config_testnet() { + let config = Config::testnet(); + + assert_eq!( + config, + Config { + testnet: true, + port: 15411, + + bootstrap: config.bootstrap.clone(), + storage: config.storage.clone(), + keypair: config.keypair.clone(), + ..Default::default() + } + ) } } diff --git a/pubky-homeserver/src/database.rs b/pubky-homeserver/src/database.rs index 4adc73d..7f52e16 100644 --- a/pubky-homeserver/src/database.rs +++ b/pubky-homeserver/src/database.rs @@ -1,31 +1,39 @@ use std::fs; -use std::path::Path; - use heed::{Env, EnvOpenOptions}; mod migrations; pub mod tables; -use tables::{Tables, TABLES_COUNT}; +use crate::config::Config; -pub const MAX_LIST_LIMIT: u16 = 100; +use tables::{Tables, TABLES_COUNT}; #[derive(Debug, Clone)] pub struct DB { pub(crate) env: Env, pub(crate) tables: Tables, + pub(crate) config: Config, } impl DB { - pub fn open(storage: &Path) -> anyhow::Result { - fs::create_dir_all(storage).unwrap(); + pub fn open(config: Config) -> anyhow::Result { + fs::create_dir_all(config.storage())?; - let env = unsafe { EnvOpenOptions::new().max_dbs(TABLES_COUNT).open(storage) }?; + let env = unsafe { + EnvOpenOptions::new() + .max_dbs(TABLES_COUNT) + .map_size(config.db_map_size()) + .open(config.storage()) + }?; let tables = migrations::run(&env)?; - let db = DB { env, tables }; + let db = DB { + env, + tables, + config, + }; Ok(db) } @@ -34,18 +42,15 @@ impl DB { #[cfg(test)] mod tests { use bytes::Bytes; - use pkarr::Keypair; - use pubky_common::timestamp::Timestamp; + use pkarr::{mainline::Testnet, Keypair}; + + use crate::config::Config; use super::DB; #[tokio::test] async fn entries() { - let storage = std::env::temp_dir() - .join(Timestamp::now().to_string()) - .join("pubky"); - - let db = DB::open(&storage).unwrap(); + let db = DB::open(Config::test(&Testnet::new(0).unwrap())).unwrap(); let keypair = Keypair::random(); let path = "/pub/foo.txt"; diff --git a/pubky-homeserver/src/database/tables/entries.rs b/pubky-homeserver/src/database/tables/entries.rs index 081f606..b1c7039 100644 --- a/pubky-homeserver/src/database/tables/entries.rs +++ b/pubky-homeserver/src/database/tables/entries.rs @@ -13,7 +13,7 @@ use pubky_common::{ timestamp::Timestamp, }; -use crate::database::{DB, MAX_LIST_LIMIT}; +use crate::database::DB; use super::events::Event; @@ -157,7 +157,7 @@ impl DB { /// Return a list of pubky urls. /// - /// - limit defaults to and capped by [MAX_LIST_LIMIT] + /// - limit defaults to [Config::default_list_limit] and capped by [Config::max_list_limit] pub fn list( &self, txn: &RoTxn, @@ -170,7 +170,9 @@ impl DB { // Vector to store results let mut results = Vec::new(); - let limit = limit.unwrap_or(MAX_LIST_LIMIT).min(MAX_LIST_LIMIT); + let limit = limit + .unwrap_or(self.config.default_list_limit()) + .min(self.config.max_list_limit()); // TODO: make this more performant than split and allocations? diff --git a/pubky-homeserver/src/database/tables/events.rs b/pubky-homeserver/src/database/tables/events.rs index cf82e18..76a4d46 100644 --- a/pubky-homeserver/src/database/tables/events.rs +++ b/pubky-homeserver/src/database/tables/events.rs @@ -10,6 +10,8 @@ use heed::{ use postcard::{from_bytes, to_allocvec}; use serde::{Deserialize, Serialize}; +use crate::database::DB; + /// Event [Timestamp] base32 => Encoded event. pub type EventsTable = Database; @@ -56,3 +58,48 @@ impl Event { } } } + +impl DB { + /// Returns a list of events formatted as ` `. + /// + /// - limit defaults to [Config::default_list_limit] and capped by [Config::max_list_limit] + /// - cursor is a 13 character string encoding of a timestamp + pub fn list_events( + &self, + limit: Option, + cursor: Option, + ) -> anyhow::Result> { + let txn = self.env.read_txn()?; + + let limit = limit + .unwrap_or(self.config.default_list_limit()) + .min(self.config.max_list_limit()); + + let cursor = cursor.unwrap_or("0000000000000".to_string()); + + let mut result: Vec = vec![]; + let mut next_cursor = cursor.to_string(); + + for _ in 0..limit { + match self.tables.events.get_greater_than(&txn, &next_cursor)? { + Some((timestamp, event_bytes)) => { + let event = Event::deserialize(event_bytes)?; + + let line = format!("{} {}", event.operation(), event.url()); + next_cursor = timestamp.to_string(); + + result.push(line); + } + None => break, + }; + } + + if !result.is_empty() { + result.push(format!("cursor: {next_cursor}")) + } + + txn.commit()?; + + Ok(result) + } +} diff --git a/pubky-homeserver/src/error.rs b/pubky-homeserver/src/error.rs index b6e5a14..8aa58d2 100644 --- a/pubky-homeserver/src/error.rs +++ b/pubky-homeserver/src/error.rs @@ -5,6 +5,7 @@ use axum::{ http::StatusCode, response::IntoResponse, }; +use tracing::debug; pub type Result = core::result::Result; @@ -86,36 +87,42 @@ impl From for Error { impl From for Error { fn from(error: std::io::Error) -> Self { + debug!(?error); Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } impl From for Error { fn from(error: heed::Error) -> Self { + debug!(?error); Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } impl From for Error { fn from(error: anyhow::Error) -> Self { + debug!(?error); Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } impl From for Error { fn from(error: postcard::Error) -> Self { + debug!(?error); Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } impl From for Error { fn from(error: axum::Error) -> Self { + debug!(?error); Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } impl From> for Error { fn from(error: flume::SendError) -> Self { + debug!(?error); Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } diff --git a/pubky-homeserver/src/extractors.rs b/pubky-homeserver/src/extractors.rs index 567ca6b..779ce65 100644 --- a/pubky-homeserver/src/extractors.rs +++ b/pubky-homeserver/src/extractors.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use axum::{ async_trait, - extract::{FromRequestParts, Path}, + extract::{FromRequestParts, Path, Query}, http::{request::Parts, StatusCode}, response::{IntoResponse, Response}, RequestPartsExt, @@ -74,3 +74,50 @@ where Ok(EntryPath(path.to_string())) } } + +#[derive(Debug)] +pub struct ListQueryParams { + pub limit: Option, + pub cursor: Option, + pub reverse: bool, + pub shallow: bool, +} + +#[async_trait] +impl FromRequestParts for ListQueryParams +where + S: Send + Sync, +{ + type Rejection = Response; + + async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { + let params: Query> = + parts.extract().await.map_err(IntoResponse::into_response)?; + + let reverse = params.contains_key("reverse"); + let shallow = params.contains_key("shallow"); + let limit = params + .get("limit") + // Treat `limit=` as None + .and_then(|l| if l.is_empty() { None } else { Some(l) }) + .and_then(|l| l.parse::().ok()); + let cursor = params + .get("cursor") + .map(|c| c.as_str()) + // Treat `cursor=` as None + .and_then(|c| { + if c.is_empty() { + None + } else { + Some(c.to_string()) + } + }); + + Ok(ListQueryParams { + reverse, + shallow, + limit, + cursor, + }) + } +} diff --git a/pubky-homeserver/src/pkarr.rs b/pubky-homeserver/src/pkarr.rs index 2302ab9..415baa0 100644 --- a/pubky-homeserver/src/pkarr.rs +++ b/pubky-homeserver/src/pkarr.rs @@ -6,7 +6,7 @@ use pkarr::{ }; pub async fn publish_server_packet( - pkarr_client: pkarr::Client, + pkarr_client: &pkarr::Client, keypair: &Keypair, domain: &str, port: u16, diff --git a/pubky-homeserver/src/routes/feed.rs b/pubky-homeserver/src/routes/feed.rs index bd426f3..6271aeb 100644 --- a/pubky-homeserver/src/routes/feed.rs +++ b/pubky-homeserver/src/routes/feed.rs @@ -1,67 +1,37 @@ -use std::collections::HashMap; - use axum::{ body::Body, - extract::{Query, State}, + extract::State, http::{header, Response, StatusCode}, response::IntoResponse, }; +use pubky_common::timestamp::{Timestamp, TimestampError}; use crate::{ - database::{tables::events::Event, MAX_LIST_LIMIT}, - error::Result, + error::{Error, Result}, + extractors::ListQueryParams, server::AppState, }; pub async fn feed( State(state): State, - Query(params): Query>, + params: ListQueryParams, ) -> Result { - let txn = state.db.env.read_txn()?; + if let Some(ref cursor) = params.cursor { + if let Err(timestmap_error) = Timestamp::try_from(cursor.to_string()) { + let cause = match timestmap_error { + TimestampError::InvalidEncoding => { + "Cursor should be valid base32 Crockford encoding of a timestamp" + } + TimestampError::InvalidBytesLength(size) => { + &format!("Cursor should be 13 characters long, got: {size}") + } + }; - let limit = params - .get("limit") - .and_then(|l| l.parse::().ok()) - .unwrap_or(MAX_LIST_LIMIT) - .min(MAX_LIST_LIMIT); - - let mut cursor = params - .get("cursor") - .map(|c| c.as_str()) - .unwrap_or("0000000000000"); - - // Guard against bad cursor - if cursor.len() < 13 { - cursor = "0000000000000" + Err(Error::new(StatusCode::BAD_REQUEST, cause.into()))? + } } - let mut result: Vec = vec![]; - let mut next_cursor = cursor.to_string(); - - for _ in 0..limit { - match state - .db - .tables - .events - .get_greater_than(&txn, &next_cursor)? - { - Some((timestamp, event_bytes)) => { - let event = Event::deserialize(event_bytes)?; - - let line = format!("{} {}", event.operation(), event.url()); - next_cursor = timestamp.to_string(); - - result.push(line); - } - None => break, - }; - } - - if !result.is_empty() { - result.push(format!("cursor: {next_cursor}")) - } - - txn.commit()?; + let result = state.db.list_events(params.limit, params.cursor)?; Ok(Response::builder() .status(StatusCode::OK) diff --git a/pubky-homeserver/src/routes/public.rs b/pubky-homeserver/src/routes/public.rs index 4cf2eed..8c6b2b9 100644 --- a/pubky-homeserver/src/routes/public.rs +++ b/pubky-homeserver/src/routes/public.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; - use axum::{ body::{Body, Bytes}, - extract::{Query, State}, + extract::State, http::{header, Response, StatusCode}, response::IntoResponse, }; @@ -12,7 +10,7 @@ use tower_cookies::Cookies; use crate::{ error::{Error, Result}, - extractors::{EntryPath, Pubky}, + extractors::{EntryPath, ListQueryParams, Pubky}, server::AppState, }; @@ -65,7 +63,7 @@ pub async fn get( State(state): State, pubky: Pubky, path: EntryPath, - Query(params): Query>, + params: ListQueryParams, ) -> Result { verify(path.as_str())?; let public_key = pubky.public_key(); @@ -88,10 +86,10 @@ pub async fn get( let vec = state.db.list( &txn, &path, - params.contains_key("reverse"), - params.get("limit").and_then(|l| l.parse::().ok()), - params.get("cursor").map(|cursor| cursor.into()), - params.contains_key("shallow"), + params.reverse, + params.limit, + params.cursor, + params.shallow, )?; return Ok(Response::builder() diff --git a/pubky-homeserver/src/server.rs b/pubky-homeserver/src/server.rs index 3b39631..89d9dd6 100644 --- a/pubky-homeserver/src/server.rs +++ b/pubky-homeserver/src/server.rs @@ -14,25 +14,24 @@ use crate::{config::Config, database::DB, pkarr::publish_server_packet}; #[derive(Debug)] pub struct Homeserver { - port: u16, - config: Config, + state: AppState, tasks: JoinSet>, } #[derive(Clone, Debug)] pub(crate) struct AppState { - pub verifier: AuthVerifier, - pub db: DB, - pub pkarr_client: pkarr::Client, + pub(crate) verifier: AuthVerifier, + pub(crate) db: DB, + pub(crate) pkarr_client: pkarr::Client, + pub(crate) config: Config, + pub(crate) port: u16, } impl Homeserver { pub async fn start(config: Config) -> Result { debug!(?config); - let keypair = config.keypair(); - - let db = DB::open(&config.storage()?)?; + let db = DB::open(config.clone())?; let pkarr_client = pkarr::Client::new(Settings { dht: DhtSettings { @@ -43,22 +42,22 @@ impl Homeserver { ..Default::default() })?; - let state = AppState { - verifier: AuthVerifier::default(), - db, - pkarr_client: pkarr_client.clone(), - }; - - let app = crate::routes::create_app(state); - let mut tasks = JoinSet::new(); - let app = app.clone(); - let listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], config.port()))).await?; let port = listener.local_addr()?.port(); + let state = AppState { + verifier: AuthVerifier::default(), + db, + pkarr_client, + config: config.clone(), + port, + }; + + let app = crate::routes::create_app(state.clone()); + // Spawn http server task tasks.spawn( axum::serve( @@ -71,15 +70,24 @@ impl Homeserver { info!("Homeserver listening on http://localhost:{port}"); - publish_server_packet(pkarr_client, &keypair, config.domain(), port).await?; - - info!("Homeserver listening on pubky://{}", keypair.public_key()); - - Ok(Self { - tasks, - config, + publish_server_packet( + &state.pkarr_client, + config.keypair(), + &state + .config + .domain() + .clone() + .unwrap_or("localhost".to_string()), port, - }) + ) + .await?; + + info!( + "Homeserver listening on pubky://{}", + config.keypair().public_key() + ); + + Ok(Self { tasks, state }) } /// Test version of [Homeserver::start], using mainline Testnet, and a temporary storage. @@ -92,11 +100,11 @@ impl Homeserver { // === Getters === pub fn port(&self) -> u16 { - self.port + self.state.port } pub fn public_key(&self) -> PublicKey { - self.config.keypair().public_key() + self.state.config.keypair().public_key() } // === Public Methods === diff --git a/pubky/src/native.rs b/pubky/src/native.rs index c2c031b..85b6edb 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -112,6 +112,13 @@ impl PubkyClient { builder.build() } + // === Getters === + + /// Returns a reference to the internal [pkarr] Client. + pub fn pkarr(&self) -> &pkarr::Client { + &self.pkarr + } + // === Auth === /// Signup to a homeserver and update Pkarr accordingly. diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index ceaddbe..d36e849 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -305,9 +305,9 @@ mod tests { .unwrap(); } - let response = pubkyauth_response.await.unwrap(); + let public_key = pubkyauth_response.await.unwrap(); - assert_eq!(&response, &pubky); + assert_eq!(&public_key, &pubky); // Test access control enforcement