From 3d11f4cd16d8464835fb684e7c5528eb54ed535d Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 30 Mar 2023 17:18:59 +0200 Subject: [PATCH] WebSockets via Rocket's Upgrade connection This PR implements a (not yet fully released) new feature of Rocket which allows WebSockets/Upgrade connections. No more need for multiple ports to be opened for Vaultwarden. No explicit need for a reverse proxy to get WebSockets to work (Although I still suggest to use a reverse proxy). - Using a git revision for Rocket, since `rocket_ws` is not yet released. - Updated other crates as well. - Added a connection guard to clear the WS connection from the Users list. Fixes #685 Fixes #2917 Fixes #1424 --- Cargo.lock | 286 ++++++++++++++++++++++++--------------- Cargo.toml | 17 ++- src/api/notifications.rs | 231 ++++++++++++++++++++----------- 3 files changed, 343 insertions(+), 191 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df4b04f8..01e0309f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock", "async-task", @@ -137,9 +137,9 @@ dependencies = [ [[package]] name = "async-process" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ "async-io", "async-lock", @@ -148,9 +148,9 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "libc", + "rustix", "signal-hook", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -182,9 +182,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -193,13 +193,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.13", ] [[package]] @@ -236,9 +236,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "autocfg" @@ -278,9 +278,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3" [[package]] name = "blake2" @@ -302,9 +302,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", "async-lock", @@ -312,6 +312,7 @@ dependencies = [ "atomic-waker", "fastrand", "futures-lite", + "log", ] [[package]] @@ -418,9 +419,9 @@ dependencies = [ [[package]] name = "chrono-tz" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa48fa079165080f11d7753fd0bc175b7d391f276b965fe4b55bfad67856e463" +checksum = "cf9cc2b23599e6d7479755f3594285efb3f74a1bdca7a7374948bc831e23a552" dependencies = [ "chrono", "chrono-tz-build", @@ -450,9 +451,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] @@ -523,9 +524,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" @@ -715,7 +716,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" dependencies = [ - "bitflags 2.0.2", + "bitflags 2.1.0", "proc-macro2", "proc-macro2-diagnostics", "quote", @@ -839,13 +840,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -996,9 +997,9 @@ checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", @@ -1081,9 +1082,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -1301,16 +1302,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.46.0", + "windows 0.48.0", ] [[package]] @@ -1378,13 +1379,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1407,14 +1408,14 @@ checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1509,15 +1510,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libmimalloc-sys" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8c7cbf8b89019683667e347572e6d55a7df7ea36b0c4ce69961b0cde67b174" +checksum = "43a558e3d911bc3c7bfc8c78bc580b404d6e51c1cefbf656e176a94b49b0df40" dependencies = [ "cc", "libc", @@ -1658,9 +1659,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcb174b18635f7561a0c6c9fc2ce57218ac7523cf72c50af80e2d79ab8f3ba1" +checksum = "3d88dad3f985ec267a3fcb7a1726f5cb1a7e8cad8b646e70a84f967210df23da" dependencies = [ "libmimalloc-sys", ] @@ -1712,7 +1713,7 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.7", + "spin 0.9.8", "tokio", "tokio-util", "version_check", @@ -1846,9 +1847,9 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.49" +version = "0.10.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33" +checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -1887,9 +1888,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.84" +version = "0.9.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa" +checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0" dependencies = [ "cc", "libc", @@ -1906,9 +1907,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -2105,9 +2106,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "polling" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" +checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" dependencies = [ "autocfg", "bitflags 1.3.2", @@ -2116,7 +2117,7 @@ dependencies = [ "libc", "log", "pin-project-lite", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2160,9 +2161,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -2440,8 +2441,7 @@ dependencies = [ [[package]] name = "rocket" version = "0.5.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9" +source = "git+https://github.com/SergioBenitez/Rocket?rev=a82508b403420bd941c32ddec3ee3e4875f2b8a5#a82508b403420bd941c32ddec3ee3e4875f2b8a5" dependencies = [ "async-stream", "async-trait", @@ -2479,8 +2479,7 @@ dependencies = [ [[package]] name = "rocket_codegen" version = "0.5.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b" +source = "git+https://github.com/SergioBenitez/Rocket?rev=a82508b403420bd941c32ddec3ee3e4875f2b8a5#a82508b403420bd941c32ddec3ee3e4875f2b8a5" dependencies = [ "devise", "glob", @@ -2495,8 +2494,7 @@ dependencies = [ [[package]] name = "rocket_http" version = "0.5.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4" +source = "git+https://github.com/SergioBenitez/Rocket?rev=a82508b403420bd941c32ddec3ee3e4875f2b8a5#a82508b403420bd941c32ddec3ee3e4875f2b8a5" dependencies = [ "cookie 0.17.0", "either", @@ -2522,6 +2520,15 @@ dependencies = [ "uncased", ] +[[package]] +name = "rocket_ws" +version = "0.1.0-rc.3" +source = "git+https://github.com/SergioBenitez/Rocket?rev=a82508b403420bd941c32ddec3ee3e4875f2b8a5#a82508b403420bd941c32ddec3ee3e4875f2b8a5" +dependencies = [ + "rocket", + "tokio-tungstenite", +] + [[package]] name = "rpassword" version = "7.2.0" @@ -2545,28 +2552,28 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.6" +version = "0.37.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] @@ -2578,6 +2585,16 @@ dependencies = [ "base64 0.21.0", ] +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -2839,9 +2856,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "stable-pattern" @@ -3055,13 +3072,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" dependencies = [ "rustls", "tokio", - "webpki", ] [[package]] @@ -3363,9 +3379,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" dependencies = [ "getrandom", ] @@ -3413,7 +3429,6 @@ dependencies = [ "job_scheduler_ng", "jsonwebtoken", "lettre", - "libmimalloc-sys", "libsqlite3-sys", "log", "mimalloc", @@ -3430,6 +3445,7 @@ dependencies = [ "ring", "rmpv", "rocket", + "rocket_ws", "rpassword", "semver", "serde", @@ -3605,16 +3621,6 @@ dependencies = [ "url", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "which" version = "4.4.0" @@ -3669,16 +3675,16 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", ] [[package]] @@ -3687,13 +3693,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3702,7 +3708,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[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.0", ] [[package]] @@ -3711,13 +3726,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -3726,42 +3756,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" diff --git a/Cargo.toml b/Cargo.toml index 4a5e6893..a5167a26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,8 @@ num-derive = "0.3.3" # Web framework rocket = { version = "0.5.0-rc.3", features = ["tls", "json"], default-features = false } +# rocket_ws = { version ="0.1.0-rc.3" } +rocket_ws = { git = 'https://github.com/SergioBenitez/Rocket', rev = "a82508b403420bd941c32ddec3ee3e4875f2b8a5" } # WebSockets libraries tokio-tungstenite = "0.18.0" @@ -85,11 +87,11 @@ rand = { version = "0.8.5", features = ["small_rng"] } ring = "0.16.20" # UUID generation -uuid = { version = "1.3.0", features = ["v4"] } +uuid = { version = "1.3.1", features = ["v4"] } # Date and time libraries chrono = { version = "0.4.24", features = ["clock", "serde"], default-features = false } -chrono-tz = "0.8.1" +chrono-tz = "0.8.2" time = "0.3.20" # Job scheduler @@ -114,7 +116,7 @@ webauthn-rs = "0.3.2" url = "2.3.1" # Email libraries -lettre = { version = "0.10.3", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } +lettre = { version = "0.10.4", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } percent-encoding = "2.2.0" # URL encoding library used for URL's in the emails email_address = "0.2.4" @@ -138,7 +140,7 @@ cookie = "0.16.2" cookie_store = "0.19.0" # Used by U2F, JWT and PostgreSQL -openssl = "0.10.48" +openssl = "0.10.50" # CLI argument parsing pico-args = "0.5.0" @@ -152,8 +154,7 @@ semver = "1.0.17" # Allow overriding the default memory allocator # Mainly used for the musl builds, since the default musl malloc is very slow -mimalloc = { version = "=0.1.34", features = ["secure"], default-features = false, optional = true } -libmimalloc-sys = "=0.1.30" +mimalloc = { version = "0.1.36", features = ["secure"], default-features = false, optional = true } which = "4.4.0" # Argon2 library with support for the PHC format @@ -162,6 +163,10 @@ argon2 = "0.5.0" # Reading a password from the cli for generating the Argon2id ADMIN_TOKEN rpassword = "7.2.0" +[patch.crates-io] +rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'a82508b403420bd941c32ddec3ee3e4875f2b8a5' } +# rocket_ws = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'a82508b403420bd941c32ddec3ee3e4875f2b8a5' } + # Strip debuginfo from the release builds # Also enable thin LTO for some optimizations [profile.release] diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 2c96f0e4..22c3bf1b 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -1,16 +1,15 @@ use std::{ - net::SocketAddr, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + net::{IpAddr, SocketAddr}, + sync::Arc, time::Duration, }; use chrono::NaiveDateTime; -use futures::{SinkExt, StreamExt}; use rmpv::Value; -use rocket::Route; +use rocket::{ + futures::{SinkExt, StreamExt}, + Route, +}; use tokio::{ net::{TcpListener, TcpStream}, sync::mpsc::Sender, @@ -21,34 +20,127 @@ use tokio_tungstenite::{ }; use crate::{ - api::EmptyResult, - db::models::{Cipher, Folder, Send, User}, + auth::ClientIp, + db::models::{Cipher, Folder, Send as DbSend, User}, Error, CONFIG, }; +use once_cell::sync::Lazy; + +static WS_USERS: Lazy> = Lazy::new(|| { + Arc::new(WebSocketUsers { + map: Arc::new(dashmap::DashMap::new()), + }) +}); + pub fn routes() -> Vec { - routes![websockets_err] + routes![websockets_hub] } -#[get("/hub")] -fn websockets_err() -> EmptyResult { - static SHOW_WEBSOCKETS_MSG: AtomicBool = AtomicBool::new(true); +#[derive(FromForm, Debug)] +struct WsAccessToken { + access_token: Option, +} - if CONFIG.websocket_enabled() - && SHOW_WEBSOCKETS_MSG.compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed).is_ok() - { - err!( - " - ########################################################### - '/notifications/hub' should be proxied to the websocket server or notifications won't work. - Go to the Wiki for more info, or disable WebSockets setting WEBSOCKET_ENABLED=false. - ###########################################################################################\n" - ) - } else { - Err(Error::empty()) +struct WSEntryMapGuard { + users: Arc, + user_uuid: String, + entry_uuid: uuid::Uuid, + addr: IpAddr, +} + +impl WSEntryMapGuard { + fn new(users: Arc, user_uuid: String, entry_uuid: uuid::Uuid, addr: IpAddr) -> Self { + Self { + users, + user_uuid, + entry_uuid, + addr, + } } } +impl Drop for WSEntryMapGuard { + fn drop(&mut self) { + info!("Closing WS connection from {}", self.addr); + if let Some(mut entry) = self.users.map.get_mut(&self.user_uuid) { + entry.retain(|(uuid, _)| uuid != &self.entry_uuid); + } + } +} + +#[get("/hub?")] +async fn websockets_hub<'r>( + ws: rocket_ws::WebSocket, + data: WsAccessToken, + ip: ClientIp, +) -> Result, Error> { + let addr = ip.ip; + info!("Accepting Rocket WS connection from {addr}"); + + let Some(token) = data.access_token else { err_code!("Invalid claim", 401) }; + let Ok(claims) = crate::auth::decode_login(&token) else { err_code!("Invalid token", 401) }; + + let (mut rx, guard) = { + let users = Arc::clone(&WS_USERS); + + // Add a channel to send messages to this client to the map + let entry_uuid = uuid::Uuid::new_v4(); + let (tx, rx) = tokio::sync::mpsc::channel::(100); + users.map.entry(claims.sub.clone()).or_default().push((entry_uuid, tx)); + + // Once the guard goes out of scope, the connection will have been closed and the entry will be deleted from the map + (rx, WSEntryMapGuard::new(users, claims.sub, entry_uuid, addr)) + }; + + Ok(ws.channel(move |mut stream| { + Box::pin(async move { + // Make sure the guard is moved into the channel future so it's not dropped earlier + let _guard = guard; + let mut interval = tokio::time::interval(Duration::from_secs(15)); + loop { + tokio::select! { + res = stream.next() => { + match res { + Some(Ok(message)) => { + match message { + // Respond to any pings + Message::Ping(ping) => stream.send(Message::Pong(ping)).await?, + Message::Pong(_) => {/* Ignored */}, + + // We should receive an initial message with the protocol and version, and we will reply to it + Message::Text(ref message) => { + let msg = message.strip_suffix(RECORD_SEPARATOR as char).unwrap_or(message); + + if serde_json::from_str(msg).ok() == Some(INITIAL_MESSAGE) { + stream.send(Message::binary(INITIAL_RESPONSE)).await?; + continue; + } + } + // Just echo anything else the client sends + _ => stream.send(message).await?, + } + } + _ => break, + } + } + + res = rx.recv() => { + match res { + Some(res) => stream.send(res).await?, + None => break, + } + } + + _ = interval.tick() => stream.send(Message::Ping(create_ping())).await? + } + } + + Ok(()) + }) + })) +} + // // Websockets server // @@ -127,8 +219,8 @@ impl WebSocketUsers { async fn send_update(&self, user_uuid: &str, data: &[u8]) { if let Some(user) = self.map.get(user_uuid).map(|v| v.clone()) { for (_, sender) in user.iter() { - if sender.send(Message::binary(data)).await.is_err() { - // TODO: Delete from map here too? + if let Err(e) = sender.send(Message::binary(data)).await { + error!("Error sending WS update {e}"); } } } @@ -196,7 +288,7 @@ impl WebSocketUsers { } } - pub async fn send_send_update(&self, ut: UpdateType, send: &Send, user_uuids: &[String]) { + pub async fn send_send_update(&self, ut: UpdateType, send: &DbSend, user_uuids: &[String]) { let user_uuid = convert_option(send.user_uuid.clone()); let data = create_update( @@ -280,15 +372,12 @@ pub enum UpdateType { None = 100, } -pub type Notify<'a> = &'a rocket::State; - -pub fn start_notification_server() -> WebSocketUsers { - let users = WebSocketUsers { - map: Arc::new(dashmap::DashMap::new()), - }; +pub type Notify<'a> = &'a rocket::State>; +pub fn start_notification_server() -> Arc { + let users = Arc::clone(&WS_USERS); if CONFIG.websocket_enabled() { - let users2 = users.clone(); + let users2 = Arc::::clone(&users); tokio::spawn(async move { let addr = (CONFIG.websocket_address(), CONFIG.websocket_port()); info!("Starting WebSockets server on {}:{}", addr.0, addr.1); @@ -300,7 +389,7 @@ pub fn start_notification_server() -> WebSocketUsers { loop { tokio::select! { Ok((stream, addr)) = listener.accept() => { - tokio::spawn(handle_connection(stream, users2.clone(), addr)); + tokio::spawn(handle_connection(stream, Arc::::clone(&users2), addr)); } _ = &mut shutdown_rx => { @@ -316,7 +405,7 @@ pub fn start_notification_server() -> WebSocketUsers { users } -async fn handle_connection(stream: TcpStream, users: WebSocketUsers, addr: SocketAddr) -> Result<(), Error> { +async fn handle_connection(stream: TcpStream, users: Arc, addr: SocketAddr) -> Result<(), Error> { let mut user_uuid: Option = None; info!("Accepting WS connection from {addr}"); @@ -336,41 +425,39 @@ async fn handle_connection(stream: TcpStream, users: WebSocketUsers, addr: Socke let user_uuid = user_uuid.expect("User UUID should be set after the handshake"); - // Add a channel to send messages to this client to the map - let entry_uuid = uuid::Uuid::new_v4(); - let (tx, mut rx) = tokio::sync::mpsc::channel(100); - users.map.entry(user_uuid.clone()).or_default().push((entry_uuid, tx)); + let (mut rx, guard) = { + // Add a channel to send messages to this client to the map + let entry_uuid = uuid::Uuid::new_v4(); + let (tx, rx) = tokio::sync::mpsc::channel::(100); + users.map.entry(user_uuid.clone()).or_default().push((entry_uuid, tx)); + // Once the guard goes out of scope, the connection will have been closed and the entry will be deleted from the map + (rx, WSEntryMapGuard::new(users, user_uuid, entry_uuid, addr.ip())) + }; + + let _guard = guard; let mut interval = tokio::time::interval(Duration::from_secs(15)); loop { tokio::select! { res = stream.next() => { match res { Some(Ok(message)) => { - // Respond to any pings - if let Message::Ping(ping) = message { - if stream.send(Message::Pong(ping)).await.is_err() { - break; + match message { + // Respond to any pings + Message::Ping(ping) => stream.send(Message::Pong(ping)).await?, + Message::Pong(_) => {/* Ignored */}, + + // We should receive an initial message with the protocol and version, and we will reply to it + Message::Text(ref message) => { + let msg = message.strip_suffix(RECORD_SEPARATOR as char).unwrap_or(message); + + if serde_json::from_str(msg).ok() == Some(INITIAL_MESSAGE) { + stream.send(Message::binary(INITIAL_RESPONSE)).await?; + continue; + } } - continue; - } else if let Message::Pong(_) = message { - /* Ignored */ - continue; - } - - // We should receive an initial message with the protocol and version, and we will reply to it - if let Message::Text(ref message) = message { - let msg = message.strip_suffix(RECORD_SEPARATOR as char).unwrap_or(message); - - if serde_json::from_str(msg).ok() == Some(INITIAL_MESSAGE) { - stream.send(Message::binary(INITIAL_RESPONSE)).await?; - continue; - } - } - - // Just echo anything else the client sends - if stream.send(message).await.is_err() { - break; + // Just echo anything else the client sends + _ => stream.send(message).await?, } } _ => break, @@ -379,27 +466,15 @@ async fn handle_connection(stream: TcpStream, users: WebSocketUsers, addr: Socke res = rx.recv() => { match res { - Some(res) => { - if stream.send(res).await.is_err() { - break; - } - }, + Some(res) => stream.send(res).await?, None => break, } } - _= interval.tick() => { - if stream.send(Message::Ping(create_ping())).await.is_err() { - break; - } - } + _ = interval.tick() => stream.send(Message::Ping(create_ping())).await? } } - info!("Closing WS connection from {addr}"); - - // Delete from map - users.map.entry(user_uuid).or_default().retain(|(uuid, _)| uuid != &entry_uuid); Ok(()) }