From f7ec1e297ecf4c7e95942b6ccefe83c61379d127 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 17:41:42 +0100 Subject: [PATCH 1/7] feat(livekit-net): pluggable, host-providable transport crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce `livekit-net`: a self-contained crate that owns the signalling transport seam. - `PlatformTransport` / `PlatformConnection` async traits (host- or Rust-implementable), value types (`Header`, `HttpResponse`, `TransportError`, `PlatformConnectResult`), and a process-global `set_transport`/`transport` registry. - `foreign` feature exports the traits to Dart/Swift/Kotlin via `#[uniffi::export(with_foreign)]` (uniffi 0.31, pure proc-macro). `connect` returns a `PlatformConnectResult` record and takes `timeout_ms: u64` — the shapes that actually cross uniffi-dart codegen. - Native backend (`native-tokio`/`native-async`/`native-dispatcher`): tungstenite + reqwest/isahc + proxy CONNECT + TLS + runtime pass-through, relocated verbatim from livekit-api's signalling stack. WS-handshake HTTP errors are preserved as `TransportError::Http { status }`. Also make the workspace runtime flavor explicit: `livekit-runtime` is now `default-features = false` at the workspace root, and each consumer (libwebrtc, livekit, livekit-datatrack) selects `tokio` explicitly, so the one-runtime guard is satisfied and non-tokio flavors can be selected per crate. --- Cargo.lock | 1580 +++++++++++++------------- Cargo.toml | 4 +- libwebrtc/Cargo.toml | 2 +- livekit-datatrack/Cargo.toml | 2 +- livekit-net/Cargo.toml | 79 ++ livekit-net/src/lib.rs | 63 + livekit-net/src/native/connection.rs | 83 ++ livekit-net/src/native/mod.rs | 156 +++ livekit-net/src/native/proxy.rs | 253 +++++ livekit-net/src/transport.rs | 60 + livekit-net/src/types.rs | 58 + livekit-net/tests/globals.rs | 62 + livekit-net/tests/native_parity.rs | 113 ++ livekit/Cargo.toml | 2 +- 14 files changed, 1704 insertions(+), 813 deletions(-) create mode 100644 livekit-net/Cargo.toml create mode 100644 livekit-net/src/lib.rs create mode 100644 livekit-net/src/native/connection.rs create mode 100644 livekit-net/src/native/mod.rs create mode 100644 livekit-net/src/native/proxy.rs create mode 100644 livekit-net/src/transport.rs create mode 100644 livekit-net/src/types.rs create mode 100644 livekit-net/tests/globals.rs create mode 100644 livekit-net/tests/native_parity.rs diff --git a/Cargo.lock b/Cargo.lock index f6ac222fe..466460906 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ name = "agent_dispatch" version = "0.1.0" dependencies = [ "clap", - "env_logger 0.11.10", + "env_logger 0.11.11", "livekit-api", "livekit-protocol", "log", @@ -127,23 +127,21 @@ dependencies = [ [[package]] name = "android-activity" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", "bitflags 2.13.0", "cc", - "cesu8", - "jni 0.21.1", - "jni-sys 0.3.1", + "jni 0.22.4", "libc", "log", "ndk 0.9.0", "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] @@ -152,12 +150,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_log-sys" version = "0.3.2" @@ -221,7 +213,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -232,14 +224,14 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "anymap2" @@ -249,9 +241,9 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "anymap3" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170433209e817da6aae2c51aa0dd443009a613425dd041ebfb2492d1c4c11a25" +checksum = "fb5dfbc6d8d2675589ccbe4d0fd61df2419075625f8c1a62325e718e2b0049f9" [[package]] name = "api" @@ -295,7 +287,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -306,9 +298,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "as-raw-xcb-connection" @@ -358,10 +350,10 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "rustc-hash 2.1.1", + "rustc-hash 2.1.3", "serde", "serde_derive", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -420,8 +412,8 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.1", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] @@ -437,7 +429,7 @@ dependencies = [ "async-io", "async-lock", "blocking", - "futures-lite 2.6.1", + "futures-lite", "once_cell", ] @@ -451,9 +443,9 @@ dependencies = [ "cfg-if 1.0.4", "concurrent-queue", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "parking", - "polling 3.11.0", + "polling", "rustix 1.1.4", "slab", "windows-sys 0.61.2", @@ -496,7 +488,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -522,7 +514,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -551,9 +543,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "av-scenechange" @@ -591,9 +583,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" dependencies = [ "arrayvec", ] @@ -655,7 +647,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -684,7 +676,7 @@ name = "basic_data_track" version = "0.1.0" dependencies = [ "anyhow", - "env_logger 0.11.10", + "env_logger 0.11.11", "futures-util", "livekit", "log", @@ -696,7 +688,7 @@ name = "basic_room" version = "0.1.0" dependencies = [ "clap", - "env_logger 0.11.10", + "env_logger 0.11.11", "hound", "livekit", "livekit-api", @@ -708,7 +700,7 @@ dependencies = [ name = "basic_text_stream" version = "0.1.0" dependencies = [ - "env_logger 0.11.10", + "env_logger 0.11.11", "futures-util", "livekit", "log", @@ -733,8 +725,8 @@ dependencies = [ "quote", "regex", "rustc-hash 1.1.0", - "shlex", - "syn 2.0.117", + "shlex 1.3.0", + "syn 2.0.118", "which", ] @@ -753,9 +745,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", - "shlex", - "syn 2.0.117", + "rustc-hash 2.1.3", + "shlex 1.3.0", + "syn 2.0.118", ] [[package]] @@ -802,7 +794,7 @@ checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -822,11 +814,11 @@ dependencies = [ [[package]] name = "bitstream-io" -version = "4.9.0" +version = "4.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" dependencies = [ - "core2", + "no_std_io2", ] [[package]] @@ -871,7 +863,7 @@ dependencies = [ "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "piper", ] @@ -887,15 +879,15 @@ dependencies = [ [[package]] name = "built" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -914,7 +906,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -931,9 +923,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "calloop" @@ -943,7 +935,7 @@ checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ "bitflags 2.13.0", "log", - "polling 3.11.0", + "polling", "rustix 0.38.44", "slab", "thiserror 1.0.69", @@ -956,7 +948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ "bitflags 2.13.0", - "polling 3.11.0", + "polling", "rustix 1.1.4", "slab", "tracing", @@ -988,9 +980,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" dependencies = [ "serde_core", ] @@ -1034,20 +1026,23 @@ dependencies = [ [[package]] name = "castaway" -version = "0.1.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] [[package]] name = "cc" -version = "1.2.57" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -1067,9 +1062,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" +checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" dependencies = [ "smallvec", "target-lexicon", @@ -1093,18 +1088,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -1120,9 +1125,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -1142,14 +1147,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1233,7 +1238,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1415,15 +1420,6 @@ dependencies = [ "objc", ] -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "coreaudio-rs" version = "0.11.3" @@ -1437,9 +1433,9 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6" +checksum = "b9b4739a805a62757a83e5654fa3faabec0442666b263bb2287d5a8185bfd953" dependencies = [ "bindgen 0.72.1", ] @@ -1476,6 +1472,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -1487,18 +1492,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1506,18 +1511,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "4279b0f31df31b4add8aeae57d40f3b5e53aad2b531e89b982bd75ed1898851d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -1527,9 +1532,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1558,40 +1563,30 @@ dependencies = [ [[package]] name = "ctor" -version = "0.6.3" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e" -dependencies = [ - "ctor-proc-macro", - "dtor", -] - -[[package]] -name = "ctor-proc-macro" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +checksum = "fb22e947478ccf9dc44d8922042c677a63fbb88f2cb468521d1145816e5087cb" [[package]] name = "curl" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" +checksum = "3a45ee8994e5307cb4c60cfc1c20bf7263ffb771ddc135c9f768a14bcbc15b09" dependencies = [ "curl-sys", "libc", "openssl-probe 0.1.6", "openssl-sys", "schannel", - "socket2 0.6.3", - "windows-sys 0.59.0", + "socket2 0.6.4", + "windows-sys 0.61.2", ] [[package]] name = "curl-sys" -version = "0.4.86+curl-8.19.0" +version = "0.4.90+curl-8.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1dd6a487cf4532ce0d801634b82aa2deb7c9c3ed930b9dadfce904df000745" +checksum = "97799a0d220bfb3361e0fe4936966ff8c4b24d65c3f06dfc70d7b680b44e7897" dependencies = [ "cc", "libc", @@ -1599,7 +1594,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1610,9 +1605,9 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "cxx" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +checksum = "ac6f8908ed0826ab0aec7c8358a4de3a9b26c5ed391a5dc5135765d2fd1aa8fb" dependencies = [ "cc", "cxx-build", @@ -1625,49 +1620,49 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +checksum = "6d8ae25c0ce72ac21a77b5deec4f49b452238ef97072f697dd6fd752b1355ecd" dependencies = [ "cc", "codespan-reporting", - "indexmap 2.13.0", + "indexmap 2.14.0", "proc-macro2", "quote", "scratch", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +checksum = "27d53791812143bd27f74ab6055f22e875f04c59bbef0ca03d714ebec6f6f484" dependencies = [ "clap", "codespan-reporting", - "indexmap 2.13.0", + "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "cxxbridge-flags" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" +checksum = "fd557619f7dc2252bf7373f6bec5600ce60a2b477e5b3b84eb4e074bb297795e" [[package]] name = "cxxbridge-macro" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +checksum = "7fe465fc5b9d0231ea141c4fae714a08f4f907855e1a7ca10cc90ab6c8b12ece" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1714,7 +1709,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1736,7 +1731,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1761,9 +1756,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "data_track_benchmark" @@ -1772,24 +1767,52 @@ dependencies = [ "anyhow", "clap", "csv", - "env_logger 0.11.10", + "env_logger 0.11.11", "futures-util", "livekit", "livekit-api", "log", - "rand 0.9.3", + "rand 0.9.4", "serde", "tokio", ] +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] [[package]] name = "derive-new" @@ -1850,13 +1873,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1889,21 +1912,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" -[[package]] -name = "dtor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - [[package]] name = "dummy" version = "0.11.0" @@ -1913,7 +1921,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2040,9 +2048,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "emath" @@ -2067,7 +2075,7 @@ dependencies = [ name = "encrypted_text_stream" version = "0.1.0" dependencies = [ - "env_logger 0.11.10", + "env_logger 0.11.11", "futures-util", "livekit", "livekit-api", @@ -2083,14 +2091,14 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "env_filter" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" dependencies = [ "log", "regex", @@ -2108,9 +2116,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" dependencies = [ "anstream", "anstyle", @@ -2168,7 +2176,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2184,7 +2192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2253,43 +2261,20 @@ dependencies = [ "deunicode", "dummy", "either", - "rand 0.9.3", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", + "rand 0.9.4", ] [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] name = "fdeflate" @@ -2308,13 +2293,12 @@ checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693" [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if 1.0.4", "libc", - "libredox", ] [[package]] @@ -2412,7 +2396,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2524,28 +2508,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -2560,7 +2529,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2617,14 +2586,14 @@ checksum = "43eaff6bbc0b3a878361aced5ec6a2818ee7c541c5b33b5880dfa9a86c23e9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2637,7 +2606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.4", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2660,18 +2629,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if 1.0.4", - "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", "wasm-bindgen", ] [[package]] name = "gif" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" dependencies = [ "color_quant", "weezl", @@ -2693,7 +2674,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2738,7 +2719,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2883,7 +2864,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.13.0", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -3032,9 +3013,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -3058,7 +3039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.4.2", ] [[package]] @@ -3069,7 +3050,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "pin-project-lite", ] @@ -3088,9 +3069,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "hyper" @@ -3118,20 +3099,19 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -3139,20 +3119,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http 1.4.0", - "hyper 1.8.1", + "http 1.4.2", + "hyper 1.10.1", "hyper-util", "rustls", "rustls-native-certs", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.6", + "webpki-roots 1.0.8", ] [[package]] @@ -3175,7 +3154,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.8.1", + "hyper 1.10.1", "hyper-util", "native-tls", "tokio", @@ -3193,14 +3172,14 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", - "hyper 1.8.1", + "hyper 1.10.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "tokio", "tower-service", "tracing", @@ -3232,12 +3211,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -3245,9 +3225,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -3258,9 +3238,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -3272,15 +3252,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -3292,15 +3272,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -3330,9 +3310,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3381,9 +3361,9 @@ dependencies = [ [[package]] name = "imgref" -version = "1.12.0" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" +checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" [[package]] name = "indexmap" @@ -3397,25 +3377,16 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if 1.0.4", -] - [[package]] name = "interpolate_name" version = "0.2.4" @@ -3424,7 +3395,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3433,16 +3404,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -3451,23 +3412,22 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "isahc" -version = "1.7.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" +checksum = "fbce0b6b4f5c50b8e014e227d51ddf721558308566b4f6ba608abcea4d272cce" dependencies = [ - "async-channel 1.9.0", + "async-channel 2.5.0", "castaway", "crossbeam-utils", "curl", "curl-sys", "encoding_rs", - "event-listener 2.5.3", - "futures-lite 1.13.0", + "event-listener 5.4.1", + "futures-lite", "http 0.2.12", "log", "mime", - "once_cell", - "polling 2.8.0", + "polling", "serde", "serde_json", "slab", @@ -3531,10 +3491,11 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634" dependencies = [ + "defmt", "jiff-static", "log", "portable-atomic", @@ -3544,13 +3505,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3583,7 +3544,7 @@ dependencies = [ "simd_cesu8", "thiserror 2.0.18", "walkdir", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3596,7 +3557,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3624,34 +3585,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ - "once_cell", + "cfg-if 1.0.4", + "futures-util", "wasm-bindgen", ] [[package]] name = "jsonwebtoken" -version = "10.3.0" +version = "10.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" dependencies = [ "base64 0.22.1", "getrandom 0.2.17", @@ -3659,6 +3621,7 @@ dependencies = [ "serde", "serde_json", "signature", + "zeroize", ] [[package]] @@ -3729,15 +3692,15 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libfuzzer-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", @@ -3750,7 +3713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if 1.0.4", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3760,7 +3723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if 1.0.4", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3771,14 +3734,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.14" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "bitflags 2.13.0", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall 0.9.0", ] [[package]] @@ -3786,7 +3749,7 @@ name = "libwebrtc" version = "0.3.39" dependencies = [ "cxx", - "env_logger 0.11.10", + "env_logger 0.11.11", "glib", "jni 0.21.1", "js-sys", @@ -3807,9 +3770,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.25" +version = "1.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" +checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9" dependencies = [ "cc", "libc", @@ -3881,7 +3844,7 @@ checksum = "de66c928222984aea59fcaed8ba627f388aaac3c1f57dcb05cc25495ef8faefe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3900,9 +3863,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" @@ -3920,7 +3883,7 @@ dependencies = [ "bytes", "chrono", "futures-util", - "http 1.4.0", + "http 1.4.2", "lazy_static", "libloading 0.8.9", "libwebrtc", @@ -3954,7 +3917,7 @@ dependencies = [ "futures-timer", "futures-util", "hmac", - "http 1.4.0", + "http 1.4.2", "isahc", "jsonwebtoken", "livekit-protocol", @@ -3964,7 +3927,7 @@ dependencies = [ "parking_lot", "pbjson-types", "prost 0.12.6", - "rand 0.9.3", + "rand 0.9.4", "reqwest", "rustls-native-certs", "scopeguard", @@ -3989,11 +3952,11 @@ dependencies = [ "from_variants", "futures-core", "futures-util", - "indexmap 2.13.0", + "indexmap 2.14.0", "livekit-protocol", "livekit-runtime", "log", - "rand 0.9.3", + "rand 0.9.4", "test-case", "thiserror 2.0.18", "tokio", @@ -4009,7 +3972,7 @@ dependencies = [ "dashmap", "device-info", "downcast-rs", - "env_logger 0.11.10", + "env_logger 0.11.11", "from_variants", "futures-util", "imgproc", @@ -4021,8 +3984,8 @@ dependencies = [ "livekit-protocol", "log", "parking_lot", - "prost 0.14.3", - "prost-build 0.14.3", + "prost 0.14.4", + "prost-build 0.14.4", "soxr-sys", "thiserror 2.0.18", "tokio", @@ -4030,6 +3993,28 @@ dependencies = [ "webrtc-sys-build", ] +[[package]] +name = "livekit-net" +version = "0.1.0" +dependencies = [ + "async-trait", + "async-tungstenite", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.4.2", + "isahc", + "livekit-runtime", + "log", + "reqwest", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "uniffi", + "url", +] + [[package]] name = "livekit-protocol" version = "0.7.10" @@ -4085,7 +4070,7 @@ dependencies = [ "anyhow", "clap", "cpal", - "env_logger 0.11.10", + "env_logger 0.11.11", "futures-util", "livekit", "livekit-api", @@ -4105,7 +4090,7 @@ dependencies = [ "eframe", "egui", "egui-wgpu", - "env_logger 0.11.10", + "env_logger 0.11.11", "futures", "image", "livekit", @@ -4217,15 +4202,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -4284,9 +4269,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -4326,9 +4311,9 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "naga" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" +checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" dependencies = [ "arrayvec", "bit-set 0.9.1", @@ -4339,7 +4324,7 @@ dependencies = [ "half", "hashbrown 0.16.1", "hexf-parse", - "indexmap 2.13.0", + "indexmap 2.14.0", "libm", "log", "num-traits", @@ -4361,9 +4346,9 @@ dependencies = [ [[package]] name = "napi" -version = "3.8.3" +version = "3.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6944d0bf100571cd6e1a98a316cdca262deb6fccf8d93f5ae1502ca3fc88bd3" +checksum = "0c71997d6f7ad4a756966e452426848ac27d3b37a295302d63afbbcce0270f93" dependencies = [ "bitflags 2.13.0", "ctor", @@ -4371,48 +4356,48 @@ dependencies = [ "napi-build", "napi-sys", "nohash-hasher", - "rustc-hash 2.1.1", + "rustc-hash 2.1.3", "tokio", ] [[package]] name = "napi-build" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d376940fd5b723c6893cd1ee3f33abbfd86acb1cd1ec079f3ab04a2a3bc4d3b1" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" [[package]] name = "napi-derive" -version = "3.5.2" +version = "3.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c914b5e420182bfb73504e0607592cdb8e2e21437d450883077669fb72a114d" +checksum = "d4ba572deef53e2c386759a8c2014175a62679d74ff83adc205c8bc0e0285727" dependencies = [ "convert_case", "ctor", "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "napi-derive-backend" -version = "5.0.2" +version = "5.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0864cf6a82e2cfb69067374b64c9253d7e910e5b34db833ed7495dda56ccb18" +checksum = "ddd961eb2aa8965e3f29722d754f3a86907eb1984e2fbcbe3fe87b9a02d6bfba" dependencies = [ "convert_case", "proc-macro2", "quote", "semver", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "napi-sys" -version = "3.2.1" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb602b84d7c1edae45e50bbf1374696548f36ae179dfa667f577e384bb90c2b" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" dependencies = [ "libloading 0.9.0", ] @@ -4525,9 +4510,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.30.1" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", @@ -4535,6 +4520,15 @@ dependencies = [ "libc", ] +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -4643,14 +4637,14 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -4667,9 +4661,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" @@ -4679,7 +4673,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4731,7 +4725,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5182,9 +5176,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" -version = "0.10.80" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", @@ -5202,7 +5196,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5219,18 +5213,18 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-src" -version = "300.5.5+3.5.5" +version = "300.6.1+3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.116" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -5241,9 +5235,9 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.3.51" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" dependencies = [ "libc", "libredox", @@ -5251,9 +5245,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "5.1.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" dependencies = [ "num-traits", ] @@ -5289,9 +5283,9 @@ dependencies = [ [[package]] name = "os_info" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" dependencies = [ "android_system_properties", "log", @@ -5340,7 +5334,7 @@ dependencies = [ "petgraph 0.6.5", "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -5419,9 +5413,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" dependencies = [ "memchr", "ucd-trie", @@ -5429,9 +5423,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" dependencies = [ "pest", "pest_generator", @@ -5439,25 +5433,24 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "pest_meta" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" dependencies = [ "pest", - "sha2", ] [[package]] @@ -5467,7 +5460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.13.0", + "indexmap 2.14.0", ] [[package]] @@ -5478,27 +5471,27 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset 0.5.7", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.14.0", ] [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5520,15 +5513,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand", "futures-io", ] [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plain" @@ -5540,7 +5533,7 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" name = "play_from_disk" version = "0.1.0" dependencies = [ - "env_logger 0.11.10", + "env_logger 0.11.11", "livekit", "log", "thiserror 2.0.18", @@ -5560,22 +5553,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if 1.0.4", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.11.0" @@ -5613,18 +5590,18 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -5657,7 +5634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5689,21 +5666,21 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5728,12 +5705,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", - "prost-derive 0.14.3", + "prost-derive 0.14.4", ] [[package]] @@ -5753,15 +5730,15 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.117", + "syn 2.0.118", "tempfile", ] [[package]] name = "prost-build" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.5.0", "itertools 0.14.0", @@ -5769,10 +5746,10 @@ dependencies = [ "multimap", "petgraph 0.8.3", "prettyplease", - "prost 0.14.3", - "prost-types 0.14.3", + "prost 0.14.4", + "prost-types 0.14.4", "regex", - "syn 2.0.117", + "syn 2.0.118", "tempfile", ] @@ -5799,20 +5776,20 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5835,18 +5812,18 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ - "prost 0.14.3", + "prost 0.14.4", ] [[package]] name = "pxfm" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" [[package]] name = "qoi" @@ -5865,27 +5842,27 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.39.2" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash 2.1.3", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -5894,16 +5871,17 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.3", + "rand 0.10.2", + "rand_pcg", "ring", - "rustc-hash 2.1.1", + "rustc-hash 2.1.3", "rustls", "rustls-pki-types", "slab", @@ -5915,23 +5893,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -5942,11 +5920,17 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -5955,14 +5939,25 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -6001,6 +5996,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_distr" version = "0.4.3" @@ -6008,7 +6009,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", +] + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", ] [[package]] @@ -6044,7 +6054,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.3", + "rand 0.9.4", "rand_chacha 0.9.0", "simd_helpers", "thiserror 2.0.18", @@ -6093,9 +6103,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -6141,18 +6151,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ "bitflags 2.13.0", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -6173,9 +6183,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "relative-path" @@ -6200,10 +6210,10 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.10.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -6230,7 +6240,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.6", + "webpki-roots 1.0.8", ] [[package]] @@ -6277,11 +6287,11 @@ dependencies = [ name = "rpc" version = "0.1.0" dependencies = [ - "env_logger 0.11.10", + "env_logger 0.11.11", "livekit", "livekit-api", "log", - "rand 0.9.3", + "rand 0.9.4", "serde_json", "tokio", ] @@ -6295,16 +6305,16 @@ dependencies = [ "napi", "napi-build", "napi-derive", - "prost 0.14.3", - "prost-types 0.14.3", + "prost 0.14.4", + "prost-types 0.14.4", "tokio", ] [[package]] name = "rtrb" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7204ed6420f698836b76d4d5c2ec5dec7585fd5c3a788fd1cde855d1de598239" +checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" [[package]] name = "rustc-demangle" @@ -6320,9 +6330,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -6370,14 +6380,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "log", "once_cell", @@ -6390,9 +6400,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe 0.2.1", "rustls-pki-types", @@ -6402,9 +6412,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -6471,15 +6481,6 @@ dependencies = [ "regex", ] -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd", -] - [[package]] name = "schannel" version = "0.1.29" @@ -6512,7 +6513,7 @@ name = "screensharing" version = "0.1.0" dependencies = [ "clap", - "env_logger 0.11.10", + "env_logger 0.11.11", "livekit", "livekit-api", "log", @@ -6536,7 +6537,7 @@ checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6552,12 +6553,6 @@ dependencies = [ "tiny-skia", ] -[[package]] -name = "sdd" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" - [[package]] name = "security-framework" version = "3.7.0" @@ -6589,9 +6584,9 @@ checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -6603,10 +6598,10 @@ version = "0.1.0" dependencies = [ "bitfield-struct", "colored", - "env_logger 0.11.10", + "env_logger 0.11.11", "livekit", "log", - "rand 0.9.3", + "rand 0.9.4", "tokio", ] @@ -6637,14 +6632,14 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -6655,9 +6650,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -6676,28 +6671,27 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" +checksum = "699f4197115b8a7e7ff19c9a315a4bd6fffec26cc4626ef45ecaea389e081c6d" dependencies = [ "futures-executor", "futures-util", "log", "once_cell", "parking_lot", - "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" +checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6707,7 +6701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if 1.0.4", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -6718,7 +6712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if 1.0.4", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -6737,6 +6731,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -6758,9 +6758,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simd_cesu8" @@ -6820,29 +6820,29 @@ dependencies = [ [[package]] name = "sluice" -version = "0.5.5" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +checksum = "160b744a45e8261307bcfe03c98e2f8274502207d534c9a64b675c4db1b6bd58" dependencies = [ - "async-channel 1.9.0", + "async-channel 2.5.0", "futures-core", "futures-io", ] [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" dependencies = [ "serde", ] [[package]] name = "smawk" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "smithay-client-toolkit" @@ -6928,12 +6928,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7040,9 +7040,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -7072,19 +7072,19 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "system-deps" -version = "7.0.7" +version = "7.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 1.1.2+spec-1.1.0", "version-compare", ] @@ -7101,9 +7101,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "tempfile" @@ -7111,11 +7111,11 @@ version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.3.0", - "getrandom 0.3.4", + "fastrand", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7145,7 +7145,7 @@ dependencies = [ "cfg-if 1.0.4", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7156,30 +7156,40 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "test-case-core", ] [[package]] name = "test-log" -version = "0.2.19" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" +checksum = "9b9c218384242b5c89b68303ab6f6fc53a312d923f0c14dc6bb860c6aeee40f1" dependencies = [ - "env_logger 0.11.10", + "env_logger 0.11.11", "test-log-macros", "tracing-subscriber", ] [[package]] -name = "test-log-macros" -version = "0.2.19" +name = "test-log-core" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" +checksum = "c26ef8b00e4d382e59f6a8ddb3cd790b3a5bb29f21a358a9a69ea2f29f13f27b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "test-log-macros" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944ad38adcbb71eaa682c56bceeb079e4ca82b4b3edc2a0fde5cb297b77dac8d" +dependencies = [ + "syn 2.0.118", + "test-log-core", ] [[package]] @@ -7217,7 +7227,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7228,7 +7238,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7256,12 +7266,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -7271,15 +7280,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -7312,9 +7321,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -7337,9 +7346,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -7347,7 +7356,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2 0.6.4", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -7365,13 +7374,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7444,7 +7453,7 @@ version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde_core", "serde_spanned", "toml_datetime 0.7.5+spec-1.1.0", @@ -7453,6 +7462,21 @@ dependencies = [ "winnow 0.7.15", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -7464,39 +7488,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.8+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 1.1.0+spec-1.1.0", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.0", + "winnow 1.0.3", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.3", ] [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" @@ -7537,7 +7561,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand 0.8.5", + "rand 0.8.6", "slab", "tokio", "tokio-util", @@ -7563,20 +7587,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags 2.13.0", "bytes", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", - "iri-string", "pin-project-lite", "tower 0.5.3", "tower-layer", "tower-service", + "url", ] [[package]] @@ -7611,7 +7635,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7665,9 +7689,9 @@ dependencies = [ [[package]] name = "tract-core" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65d67f5190132365dda73fe215bfc5e01b031e8cbfbea9d486bb5b0dbba3545" +checksum = "3f0674154b96abb73f8bdade9e6b6e22fa65f4e13da8d84c0659bd7d3c4739b9" dependencies = [ "anyhow", "anymap3", @@ -7691,9 +7715,9 @@ dependencies = [ [[package]] name = "tract-data" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cd7fda1e5e8b854ea3abdd09126a87fc4af81e6d1e29ec1710a8a4abf4f13a" +checksum = "5a0972068b06792ef536df873857854c41109aefa3ad90432e09b0b6549e7523" dependencies = [ "anyhow", "downcast-rs", @@ -7717,9 +7741,9 @@ dependencies = [ [[package]] name = "tract-hir" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554df991b647dba8af0547ee5838b6912ed20b424f2adda0ea0b7faf8db1b151" +checksum = "fe50ad4a84553a75c2eeba7b705ff32f862d7e4bdb5892397b4560ec59d1627d" dependencies = [ "derive-new", "log", @@ -7728,9 +7752,9 @@ dependencies = [ [[package]] name = "tract-linalg" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72097a89cc4e7c5f1bc4f854b9294dd30fa6f6d8f7f409c556953b49078c94f" +checksum = "a7b582f6ef2dcf32a78f043bbbb93ebc54af247c95cd5d18d5f8b36af47a273e" dependencies = [ "byteorder", "cc", @@ -7756,9 +7780,9 @@ dependencies = [ [[package]] name = "tract-nnef" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b3755dd0948111b407085d11033ba218cb85b85ce8d795cec2b8353db552ea" +checksum = "d4c317f94210bdfc0b81913965c7d7439d401527cc8d9bbc85fffbb59b09af5c" dependencies = [ "byteorder", "flate2", @@ -7776,9 +7800,9 @@ dependencies = [ [[package]] name = "tract-onnx" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac23ad1d2d5da3256ae1a78757b1072a8a3fac2a4b28d27cfb561c5942ec2701" +checksum = "30cf71aa3c8a2ca05258ee0750e428cf2d0e8a38781ccd2ab9a0900551684c2b" dependencies = [ "bytes", "derive-new", @@ -7794,13 +7818,13 @@ dependencies = [ [[package]] name = "tract-onnx-opl" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87561bf0b84f74a124afc0f1997682728da6cd821083511e0357432954fd24f6" +checksum = "a34a0c0d726b7adc04e3ae956fa1e091ac6a33d4b9bc52ef678108c5445efb7d" dependencies = [ "getrandom 0.2.17", "log", - "rand 0.8.5", + "rand 0.8.6", "rand_distr", "rustfft", "tract-nnef", @@ -7836,11 +7860,11 @@ checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http 1.4.2", "httparse", "log", "native-tls", - "rand 0.9.3", + "rand 0.9.4", "sha1", "thiserror 2.0.18", "url", @@ -7855,11 +7879,11 @@ checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http 1.4.2", "httparse", "log", "native-tls", - "rand 0.9.3", + "rand 0.9.4", "rustls", "rustls-pki-types", "sha1", @@ -7873,7 +7897,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" dependencies = [ - "rustc-hash 2.1.1", + "rustc-hash 2.1.3", ] [[package]] @@ -7884,9 +7908,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -7959,7 +7983,7 @@ dependencies = [ "proc-macro2", "serde", "stringcase 0.4.0", - "toml", + "toml 0.9.12+spec-1.1.0", "uniffi", "uniffi_bindgen", "uniffi_dart_macro", @@ -7979,12 +8003,12 @@ dependencies = [ "glob", "goblin", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.14.0", "once_cell", "serde", "tempfile", "textwrap", - "toml", + "toml 0.9.12+spec-1.1.0", "uniffi_internal_macros", "uniffi_meta", "uniffi_pipeline", @@ -8035,10 +8059,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4641669b48fefbc5e80ff08c5004d9c7617fb91232131a6734ab6712779cb04c" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8053,8 +8077,8 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.117", - "toml", + "syn 2.0.118", + "toml 0.9.12+spec-1.1.0", "uniffi_meta", ] @@ -8078,7 +8102,7 @@ checksum = "032739b3ec725576914c15899dedaf080163ced86b6934566c20ec2b20ce90ca" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.14.0", "tempfile", "uniffi_internal_macros", ] @@ -8262,18 +8286,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if 1.0.4", "once_cell", @@ -8284,23 +8308,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ - "cfg-if 1.0.4", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8308,31 +8328,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] [[package]] name = "wayland-backend" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" dependencies = [ "cc", "downcast-rs", @@ -8344,9 +8364,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.13" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ "bitflags 2.13.0", "rustix 1.1.4", @@ -8367,9 +8387,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.13" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b3298683470fbdc6ca40151dfc48c8f2fd4c41a26e13042f801f85002384091" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ "rustix 1.1.4", "wayland-client", @@ -8378,9 +8398,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.11" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ "bitflags 2.13.0", "wayland-backend", @@ -8403,9 +8423,9 @@ dependencies = [ [[package]] name = "wayland-protocols-misc" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "429b99200febaf95d4f4e46deff6fe4382bcff3280ee16a41cf887b3c3364984" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ "bitflags 2.13.0", "wayland-backend", @@ -8416,9 +8436,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d392fc283a87774afc9beefcd6f931582bb97fe0e6ced0b306a62cb1d026527c" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ "bitflags 2.13.0", "wayland-backend", @@ -8429,9 +8449,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ "bitflags 2.13.0", "wayland-backend", @@ -8442,9 +8462,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.9" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", "quick-xml", @@ -8453,9 +8473,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" dependencies = [ "dlib", "log", @@ -8465,9 +8485,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -8485,9 +8505,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" +checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" dependencies = [ "core-foundation 0.10.1", "jni 0.22.4", @@ -8514,14 +8534,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.8", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -8533,7 +8553,7 @@ dependencies = [ "cc", "cxx", "cxx-build", - "env_logger 0.11.10", + "env_logger 0.11.11", "glob", "log", "pkg-config", @@ -8570,9 +8590,9 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" +checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" dependencies = [ "arrayvec", "bitflags 2.13.0", @@ -8600,9 +8620,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02da3ad1b568337f25513b317870960ef87073ea0945502e44b864b67a8c77b7" +checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" dependencies = [ "arrayvec", "bit-set 0.9.1", @@ -8612,7 +8632,7 @@ dependencies = [ "cfg_aliases", "document-features", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", "naga", "once_cell", @@ -8634,45 +8654,45 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e51b5447e144b3dbba4feb01f80f4fa21696fa0cd99afb2c3df1affd6fdb28" +checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3487cd6293a963bc5c0c0396f6a2192043c50003c07f4efdccbad3d90ec9d819" +checksum = "01e09be551dc939498bdd5f6b2c66e55ab275dad25825267a08605a80fc9f0af" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2f2fb042f36920771deb0b966543c5751b18f3d327760ffc90f74e20b2dcd4" +checksum = "af1fb1798be2a912497d4c224f72d39bb0cb34af50e8bcc29865bc339c943059" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb01076d0aa08b0ba9bd741e178b5cc440f5abe99d9581323a4c8b5d1a1916" +checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8e1a9e7a8512f276f7c62e018c7fa8d60954303fed2e5750114332049193f" +checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" dependencies = [ "android_system_properties", "arrayvec", @@ -8724,9 +8744,9 @@ dependencies = [ [[package]] name = "wgpu-naga-bridge" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c654c483f058800972c3645e95388a7eca31bf9fe1933bc20e036588a0be02" +checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" dependencies = [ "naga", "wgpu-types", @@ -8734,9 +8754,9 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "29.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" +checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" dependencies = [ "bitflags 2.13.0", "bytemuck", @@ -8754,7 +8774,7 @@ dependencies = [ "eframe", "egui", "egui-wgpu", - "env_logger 0.11.10", + "env_logger 0.11.11", "futures", "image", "livekit", @@ -8800,7 +8820,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -8858,7 +8878,7 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", + "windows-link", "windows-result 0.4.1", "windows-strings", ] @@ -8870,7 +8890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ "windows-core 0.62.2", - "windows-link 0.2.1", + "windows-link", "windows-threading", ] @@ -8882,7 +8902,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8893,15 +8913,9 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" @@ -8915,7 +8929,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ "windows-core 0.62.2", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -8933,7 +8947,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -8942,7 +8956,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -8954,15 +8968,6 @@ dependencies = [ "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.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -8996,7 +9001,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -9014,21 +9019,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -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.6" @@ -9051,7 +9041,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -9068,7 +9058,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -9077,12 +9067,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -9101,12 +9085,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -9125,12 +9103,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -9161,12 +9133,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -9185,12 +9151,6 @@ 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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -9209,12 +9169,6 @@ 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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -9233,12 +9187,6 @@ 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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -9314,24 +9262,24 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.51.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "x11-dl" @@ -9414,9 +9362,9 @@ checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -9425,13 +9373,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -9448,56 +9396,70 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -9506,9 +9468,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -9517,13 +9479,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9561,9 +9523,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7a1c0af6e5d8d1363f4994b7a091ccf963d8b694f7da5b0b9cceb82da2c0a6" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 93d4a11df..cafdce6df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "livekit-uniffi", "livekit-datatrack", "livekit-ffi-node-bindings", + "livekit-net", "livekit-runtime", "livekit-wakeword", "libwebrtc", @@ -51,8 +52,9 @@ livekit = { version = "0.7.50", path = "livekit" } livekit-api = { version = "0.5.4", path = "livekit-api" } livekit-ffi = { version = "0.12.68", path = "livekit-ffi" } livekit-datatrack = { version = "0.1.9", path = "livekit-datatrack" } +livekit-net = { version = "0.1.0", path = "livekit-net" } livekit-protocol = { version = "0.7.10", path = "livekit-protocol" } -livekit-runtime = { version = "0.4.0", path = "livekit-runtime" } +livekit-runtime = { version = "0.4.0", path = "livekit-runtime", default-features = false } soxr-sys = { version = "0.1.3", path = "soxr-sys" } webrtc-sys = { version = "0.3.36", path = "webrtc-sys" } webrtc-sys-build = { version = "0.3.18", path = "webrtc-sys/build" } diff --git a/libwebrtc/Cargo.toml b/libwebrtc/Cargo.toml index 7efe9f4cf..67177c5d7 100644 --- a/libwebrtc/Cargo.toml +++ b/libwebrtc/Cargo.toml @@ -30,7 +30,7 @@ jni = "0.21" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] webrtc-sys = { workspace = true } -livekit-runtime = { workspace = true } +livekit-runtime = { workspace = true, features = ["tokio"] } lazy_static = { workspace = true } parking_lot = { workspace = true } tokio = { workspace = true, default-features = false, features = ["sync", "macros"] } diff --git a/livekit-datatrack/Cargo.toml b/livekit-datatrack/Cargo.toml index 7dcfb2283..0bf006177 100644 --- a/livekit-datatrack/Cargo.toml +++ b/livekit-datatrack/Cargo.toml @@ -9,7 +9,7 @@ repository.workspace = true [dependencies] livekit-protocol = { workspace = true } -livekit-runtime = { workspace = true } +livekit-runtime = { workspace = true, features = ["tokio"] } log = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, default-features = false, features = ["sync"] } diff --git a/livekit-net/Cargo.toml b/livekit-net/Cargo.toml new file mode 100644 index 000000000..a9b41130b --- /dev/null +++ b/livekit-net/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "livekit-net" +version = "0.1.0" +license.workspace = true +description = "Pluggable, host-providable network transport for LiveKit signalling" +edition.workspace = true +repository.workspace = true + +[features] +default = [] + +# ── transport backend ───────────────────────────────────────────── +foreign = ["dep:uniffi"] + +# Native backend bundles — each pairs the net libraries with a runtime. +native-tokio = ["__native", "__native-tokio", "tokio"] +native-async = ["__native", "__native-async", "async"] +native-dispatcher = ["__native", "__native-async", "dispatcher"] + +# ── runtime pass-through to livekit-runtime (blind consumers rely on this) ── +tokio = ["dep:livekit-runtime", "livekit-runtime/tokio"] +async = ["dep:livekit-runtime", "livekit-runtime/async"] +dispatcher = ["dep:livekit-runtime", "livekit-runtime/dispatcher"] + +# ── TLS (only meaningful with a native backend) ─────────────────── +native-tls = [ + "tokio-tungstenite?/native-tls", + "async-tungstenite?/async-native-tls", + "reqwest?/native-tls", +] +native-tls-vendored = [ + "tokio-tungstenite?/native-tls-vendored", + "reqwest?/native-tls-vendored", +] +rustls-tls-native-roots = [ + "tokio-tungstenite?/rustls-tls-native-roots", + "reqwest?/rustls-tls-native-roots", + "tokio-tungstenite?/__rustls-tls", + "dep:tokio-rustls", + "dep:rustls-native-certs", +] +rustls-tls-webpki-roots = [ + "tokio-tungstenite?/rustls-tls-webpki-roots", + "reqwest?/rustls-tls-webpki-roots", +] +__rustls-tls = ["tokio-tungstenite?/__rustls-tls", "reqwest?/__rustls"] + +# ── internal markers ────────────────────────────────────────────── +__native = [] +__native-tokio = ["dep:tokio-tungstenite", "dep:reqwest", "dep:tokio", "dep:futures-util", "dep:bytes", "dep:base64"] +__native-async = ["dep:async-tungstenite", "dep:isahc", "dep:tokio", "dep:futures-util", "dep:bytes"] + +[dependencies] +async-trait = "0.1" +uniffi = { version = "0.31", optional = true } + +# Direct path dep (not workspace inheritance) so default-features = false is +# honored: cargo ignores a member's default-features override on an inherited dep, +# which would drag in livekit-runtime's default `tokio` and trip its one-runtime +# guard when a non-tokio flavor is selected. +livekit-runtime = { path = "../livekit-runtime", version = "0.4.0", optional = true, default-features = false } +url = "2.3" +http = "1.1" +log = { workspace = true } +tokio-tungstenite = { version = "0.29", features = ["url"], optional = true } +async-tungstenite = { version = "0.29", features = ["async-std-runtime", "async-native-tls", "url"], optional = true } +tokio = { workspace = true, default-features = false, features = ["sync", "macros", "io-util", "net"], optional = true } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true } +rustls-native-certs = { version = "0.8", optional = true } +futures-util = { workspace = true, default-features = false, features = ["sink"], optional = true } +bytes = { workspace = true, optional = true } +reqwest = { version = "0.12", default-features = false, features = ["json"], optional = true } +isahc = { version = "1.7.2", default-features = false, features = ["json", "text-decoding"], optional = true } +base64 = { version = "0.21", optional = true, features = ["std"] } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "net", "time", "macros", "io-util"] } +tokio-tungstenite = { version = "0.29", features = ["url"] } +futures-util = { version = "0.3", default-features = false, features = ["sink"] } diff --git a/livekit-net/src/lib.rs b/livekit-net/src/lib.rs new file mode 100644 index 000000000..0032e7b92 --- /dev/null +++ b/livekit-net/src/lib.rs @@ -0,0 +1,63 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod transport; +mod types; + +#[cfg(feature = "__native")] +mod native; + +pub use transport::{PlatformConnectResult, PlatformConnection, PlatformTransport}; +pub use types::{Header, HttpResponse, TransportError}; + +use std::sync::{Arc, OnceLock}; + +static REGISTERED: OnceLock> = OnceLock::new(); + +/// Register the process-wide transport. Call once at startup, before the first +/// `connect`. A later call is ignored (first registration wins). +pub fn set_transport(t: Arc) { + let _ = REGISTERED.set(t); +} + +/// Resolve the process-wide transport. +/// +/// Returns the explicitly registered transport if any; otherwise, on native +/// builds, the built-in [`NativeTransport`]; otherwise `None`. +pub fn transport() -> Option> { + if let Some(t) = REGISTERED.get() { + return Some(Arc::clone(t)); + } + #[cfg(feature = "__native")] + { + Some(native::native_default()) + } + #[cfg(not(feature = "__native"))] + { + None + } +} + +#[cfg(feature = "foreign")] +uniffi::setup_scaffolding!(); + +#[cfg(feature = "__native")] +pub mod testing { + use crate::PlatformTransport; + use std::sync::Arc; + /// Construct a fresh NativeTransport for tests (bypasses the global registry). + pub fn native_transport() -> Arc { + Arc::new(crate::native::NativeTransport) + } +} diff --git a/livekit-net/src/native/connection.rs b/livekit-net/src/native/connection.rs new file mode 100644 index 000000000..f019f3262 --- /dev/null +++ b/livekit-net/src/native/connection.rs @@ -0,0 +1,83 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{PlatformConnection, TransportError}; +use futures_util::{ + stream::{SplitSink, SplitStream}, + SinkExt, StreamExt, +}; +use livekit_runtime::TcpStream; +use tokio::sync::Mutex; + +#[cfg(feature = "__native-tokio")] +use tokio_tungstenite::{ + tungstenite::{error::ProtocolError, Error as WsError, Message}, + MaybeTlsStream, WebSocketStream, +}; +#[cfg(feature = "__native-async")] +use async_tungstenite::{ + async_std::ClientStream as MaybeTlsStream, + tungstenite::{error::ProtocolError, Error as WsError, Message}, + WebSocketStream, +}; + +type WebSocket = WebSocketStream>; + +pub struct NativeConnection { + writer: Mutex>, + reader: Mutex>, +} + +impl NativeConnection { + pub(super) fn new(ws: WebSocket) -> Self { + let (writer, reader) = ws.split(); + Self { writer: Mutex::new(writer), reader: Mutex::new(reader) } + } +} + +#[async_trait::async_trait] +impl PlatformConnection for NativeConnection { + async fn send(&self, frame: Vec) -> Result<(), TransportError> { + self.writer + .lock() + .await + .send(Message::Binary(frame.into())) + .await + .map_err(|e| TransportError::Connection(e.to_string())) + } + + async fn recv(&self) -> Result>, TransportError> { + let mut reader = self.reader.lock().await; + loop { + match reader.next().await { + Some(Ok(Message::Binary(data))) => return Ok(Some(data.to_vec())), + // Respond to WS ping internally; keep reading for the next app frame. + Some(Ok(Message::Ping(payload))) => { + let _ = self.writer.lock().await.send(Message::Pong(payload)).await; + } + Some(Ok(Message::Pong(_))) | Some(Ok(Message::Frame(_))) => continue, + Some(Ok(Message::Text(_))) => continue, // signalling never sends text + Some(Ok(Message::Close(_))) | None => return Ok(None), + Some(Err(WsError::Protocol(ProtocolError::ResetWithoutClosingHandshake))) => { + return Ok(None) + } + Some(Err(e)) => return Err(TransportError::Connection(e.to_string())), + } + } + } + + async fn close(&self) { + let _ = self.writer.lock().await.close().await; + } +} diff --git a/livekit-net/src/native/mod.rs b/livekit-net/src/native/mod.rs new file mode 100644 index 000000000..a31483760 --- /dev/null +++ b/livekit-net/src/native/mod.rs @@ -0,0 +1,156 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod connection; +mod proxy; + +use crate::{Header, HttpResponse, PlatformConnectResult, PlatformTransport, TransportError}; +use std::sync::Arc; + +fn error_chain(e: &dyn std::error::Error) -> String { + let mut msg = e.to_string(); + let mut src = e.source(); + while let Some(s) = src { + msg.push_str(": "); + msg.push_str(&s.to_string()); + src = s.source(); + } + msg +} + +pub struct NativeTransport; + +pub(crate) fn native_default() -> Arc { + use std::sync::OnceLock; + static DEFAULT: OnceLock> = OnceLock::new(); + DEFAULT.get_or_init(|| Arc::new(NativeTransport)).clone() +} + +#[async_trait::async_trait] +impl PlatformTransport for NativeTransport { + async fn connect( + &self, + url: String, + headers: Vec
, + timeout_ms: u64, + ) -> Result { + let parsed = + url::Url::parse(&url).map_err(|e| TransportError::Connection(e.to_string()))?; + + #[cfg(feature = "__native-tokio")] + use tokio_tungstenite::tungstenite::client::IntoClientRequest; + #[cfg(feature = "__native-async")] + use async_tungstenite::tungstenite::client::IntoClientRequest; + + let mut request = parsed + .clone() + .into_client_request() + .map_err(|e| TransportError::Connection(e.to_string()))?; + for h in &headers { + let name: http::header::HeaderName = h + .name + .parse() + .map_err(|_| TransportError::Other("bad header name".into()))?; + let value = http::header::HeaderValue::from_str(&h.value) + .map_err(|_| TransportError::Other("bad header value".into()))?; + request.headers_mut().insert(name, value); + } + + let connect_fut = async { + #[cfg(feature = "__native-tokio")] + let ws = self::proxy::connect_ws(request, &parsed).await?; + #[cfg(feature = "__native-async")] + let (ws, _) = async_tungstenite::async_std::connect_async(request) + .await + .map_err(|e: async_tungstenite::tungstenite::Error| { + use async_tungstenite::tungstenite::Error; + match e { + Error::Http(resp) => TransportError::Http { status: resp.status().as_u16() }, + other => TransportError::Connection(other.to_string()), + } + })?; + Ok::<_, TransportError>(ws) + }; + + let ws = livekit_runtime::timeout( + std::time::Duration::from_millis(timeout_ms), + connect_fut, + ) + .await + .map_err(|_| TransportError::Timeout)??; + + Ok(PlatformConnectResult { + connection: Arc::new(self::connection::NativeConnection::new(ws)), + }) + } + + async fn http_get( + &self, + url: String, + headers: Vec
, + ) -> Result { + #[cfg(feature = "__native-tokio")] + { + let mut req = reqwest::Client::new().get(&url); + for h in &headers { + req = req.header(&h.name, &h.value); + } + let res = req + .send() + .await + .map_err(|e| TransportError::Connection(error_chain(&e)))?; + let status = res.status().as_u16(); + let resp_headers = res + .headers() + .iter() + .filter_map(|(n, v)| { + v.to_str().ok().map(|v| Header { name: n.as_str().to_string(), value: v.to_string() }) + }) + .collect(); + let body = res + .bytes() + .await + .map_err(|e| TransportError::Other(e.to_string()))? + .to_vec(); + Ok(HttpResponse { status, headers: resp_headers, body }) + } + #[cfg(feature = "__native-async")] + { + let mut builder = isahc::Request::get(&url); + for h in &headers { + builder = builder.header(h.name.as_str(), h.value.as_str()); + } + let request = builder + .body(()) + .map_err(|e| TransportError::Other(e.to_string()))?; + let mut res = isahc::send_async(request) + .await + .map_err(|e| TransportError::Connection(error_chain(&e)))?; + let status = res.status().as_u16(); + let resp_headers = res + .headers() + .iter() + .filter_map(|(n, v)| { + v.to_str().ok().map(|v| Header { name: n.as_str().to_string(), value: v.to_string() }) + }) + .collect(); + use isahc::AsyncReadResponseExt; + let mut body = Vec::new(); + res.copy_to(&mut body) + .await + .map_err(|e| TransportError::Other(e.to_string()))?; + Ok(HttpResponse { status, headers: resp_headers, body }) + } + } +} diff --git a/livekit-net/src/native/proxy.rs b/livekit-net/src/native/proxy.rs new file mode 100644 index 000000000..1f5e3dca5 --- /dev/null +++ b/livekit-net/src/native/proxy.rs @@ -0,0 +1,253 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "__native-tokio")] +use crate::TransportError; + +#[cfg(feature = "__native-tokio")] +use std::env; + +/// Map a tungstenite error from a WS handshake to a [`TransportError`]. +/// +/// When the server returns an HTTP error response during the upgrade (e.g. 404 +/// on the /rtc endpoint), tungstenite surfaces it as `Error::Http(response)`. +/// We extract the status code and return `TransportError::Http { status }` so +/// callers can distinguish HTTP error codes (403, 404) from network errors. +#[cfg(feature = "__native-tokio")] +fn map_ws_err(e: tokio_tungstenite::tungstenite::Error) -> TransportError { + use tokio_tungstenite::tungstenite::Error; + match e { + Error::Http(resp) => TransportError::Http { status: resp.status().as_u16() }, + other => TransportError::Connection(other.to_string()), + } +} + +#[cfg(feature = "__native-tokio")] +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream as TokioTcpStream, +}; + +#[cfg(feature = "__native-tokio")] +use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream}; + +#[cfg(feature = "__native-tokio")] +use livekit_runtime::TcpStream; + +#[cfg(feature = "__native-tokio")] +pub(super) async fn connect_ws( + request: http::Request<()>, + url: &url::Url, +) -> Result>, TransportError> { + // Check for HTTP_PROXY or HTTPS_PROXY environment variables + let proxy_env = if url.scheme() == "wss" { + env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy")) + } else { + env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy")) + }; + + // Connect directly or through proxy + let ws_stream = if let Ok(proxy_url) = proxy_env { + if !proxy_url.is_empty() { + log::info!("Using proxy: {}", proxy_url); + let proxy_url = url::Url::parse(&proxy_url).map_err(|e| { + TransportError::Connection(format!("Invalid proxy URL: {}", e)) + })?; + + let host = url.host_str().ok_or_else(|| { + TransportError::Connection("Target URL has no host".into()) + })?; + + let port = url.port_or_known_default().ok_or_else(|| { + TransportError::Connection( + "Target URL has no port and no default for scheme".into(), + ) + })?; + + let proxy_host = proxy_url.host_str().ok_or_else(|| { + TransportError::Connection("Proxy URL has no host".into()) + })?; + + let proxy_port = proxy_url.port_or_known_default().unwrap_or(80); + let proxy_addr = format!("{}:{}", proxy_host, proxy_port); + + let mut proxy_stream = TokioTcpStream::connect(proxy_addr) + .await + .map_err(|e| TransportError::Connection(e.to_string()))?; + + let mut proxy_auth_header = None; + if let Some(password) = proxy_url.password() { + use base64::Engine as _; + let auth = format!("{}:{}", proxy_url.username(), password); + let auth = format!( + "Basic {}", + base64::engine::general_purpose::STANDARD.encode(auth) + ); + proxy_auth_header = Some(auth); + } + + // Send CONNECT request + let target = format!("{}:{}", host, port); + let mut connect_req = + format!("CONNECT {} HTTP/1.1\r\nHost: {}\r\n", target, target); + + // Add proxy authorization if needed + if let Some(auth) = proxy_auth_header { + connect_req.push_str(&format!("Proxy-Authorization: {}\r\n", auth)); + } + + // Finalize request + connect_req.push_str("\r\n"); + + log::debug!("Sending CONNECT request to proxy"); + proxy_stream + .write_all(connect_req.as_bytes()) + .await + .map_err(|e| TransportError::Connection(e.to_string()))?; + + // Read and parse response + let mut response = Vec::new(); + let mut buf = [0u8; 4096]; + let mut headers_complete = false; + + while !headers_complete { + let n = proxy_stream + .read(&mut buf) + .await + .map_err(|e| TransportError::Connection(e.to_string()))?; + if n == 0 { + return Err(TransportError::Connection( + "Proxy connection closed while reading response".into(), + )); + } + + response.extend_from_slice(&buf[..n]); + + // Check if we've received the end of headers (double CRLF) + if response.windows(4).any(|w| w == b"\r\n\r\n") { + headers_complete = true; + } + } + + // Parse status line + let response_str = String::from_utf8_lossy(&response); + let status_line = response_str.lines().next().ok_or_else(|| { + TransportError::Connection("Invalid proxy response".into()) + })?; + + // Check status code + if !status_line.contains("200") { + return Err(TransportError::Connection(format!( + "Proxy connection failed: {}", + status_line + ))); + } + + log::debug!("Proxy connection established to {}", target); + + // Create MaybeTlsStream based on original URL scheme + let stream = if url.scheme() == "wss" { + // Only enable proxy TLS support when rustls-tls-native-roots is enabled + #[cfg(feature = "rustls-tls-native-roots")] + { + // For WSS, we need to establish TLS over the proxy connection + use std::sync::Arc; + use tokio_rustls::{ + rustls::{self, pki_types::ServerName}, + TlsConnector, + }; + + // Load native root certificates + let mut root_store = rustls::RootCertStore::empty(); + let cert_result = rustls_native_certs::load_native_certs(); + if !cert_result.errors.is_empty() { + log::warn!( + "Native root CA certificate loading errors: {:?}", + cert_result.errors + ); + } + if cert_result.certs.is_empty() { + return Err(TransportError::Connection(format!( + "Could not load any native root certificates: {:?}", + cert_result.errors + ))); + } + let total = cert_result.certs.len(); + let (added, ignored) = + root_store.add_parsable_certificates(cert_result.certs); + log::debug!( + "Added {added}/{total} native root certificates ({ignored} ignored)" + ); + + let tls_config = rustls::ClientConfig::builder() + .with_root_certificates(root_store) + .with_no_client_auth(); + + let server_name = + ServerName::try_from(host.to_owned()).map_err(|_| { + TransportError::Connection(format!( + "Invalid DNS name: {}", + host + )) + })?; + + let connector = TlsConnector::from(Arc::new(tls_config)); + let tls_stream = connector + .connect(server_name, proxy_stream) + .await + .map_err(|e| { + TransportError::Connection(format!( + "TLS connection error: {}", + e + )) + })?; + + MaybeTlsStream::Rustls(tls_stream) + } + + #[cfg(not(feature = "rustls-tls-native-roots"))] + { + // For non-rustls-tls-native-roots builds, don't support proxy for WSS + return Err(TransportError::Connection( + "WSS over proxy requires rustls-tls-native-roots feature".into(), + )); + } + } else { + // For plain WS, just use the proxy stream directly + MaybeTlsStream::Plain(proxy_stream) + }; + + // Now perform WebSocket handshake over the established connection + let (ws_stream, _) = + tokio_tungstenite::client_async_with_config(request, stream, None) + .await + .map_err(map_ws_err)?; + ws_stream + } else { + // Proxy var is empty, connect directly + let (ws_stream, _) = connect_async(request) + .await + .map_err(map_ws_err)?; + ws_stream + } + } else { + // No proxy env var set, connect directly + let (ws_stream, _) = connect_async(request) + .await + .map_err(map_ws_err)?; + ws_stream + }; + + Ok(ws_stream) +} diff --git a/livekit-net/src/transport.rs b/livekit-net/src/transport.rs new file mode 100644 index 000000000..931120484 --- /dev/null +++ b/livekit-net/src/transport.rs @@ -0,0 +1,60 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::types::{Header, HttpResponse, TransportError}; +use std::sync::Arc; + +/// A single open WebSocket connection. Control frames (ping/pong, close handshake) +/// are the implementation's own responsibility; only binary application frames cross here. +#[cfg_attr(feature = "foreign", uniffi::export(with_foreign))] +#[async_trait::async_trait] +pub trait PlatformConnection: Send + Sync + 'static { + /// Send one binary application frame. + async fn send(&self, frame: Vec) -> Result<(), TransportError>; + /// Receive the next binary frame. `Ok(None)` means the peer/transport closed. + async fn recv(&self) -> Result>, TransportError>; + async fn close(&self); +} + +/// The connection opened by [`PlatformTransport::connect`]. +/// +/// A record wrapper, not a bare `Arc`: uniffi 0.31 cannot +/// lift a trait object returned from an async `with_foreign` method. +#[cfg_attr(feature = "foreign", derive(uniffi::Record))] +pub struct PlatformConnectResult { + pub connection: Arc, +} + +/// A host- or Rust-provided network transport. Opens WebSockets and performs the +/// pre-WS HTTP GETs. Knows nothing about LiveKit/protobuf. +#[cfg_attr(feature = "foreign", uniffi::export(with_foreign))] +#[async_trait::async_trait] +pub trait PlatformTransport: Send + Sync { + /// Open a WebSocket. `url` is the full ws(s):// URL including query string. + /// `timeout_ms` bounds connection establishment. Milliseconds, not `Duration`: + /// a `Duration` parameter breaks uniffi-dart's foreign codegen. + async fn connect( + &self, + url: String, + headers: Vec
, + timeout_ms: u64, + ) -> Result; + + /// The pre-WS HTTP GETs (validate, region discovery). + async fn http_get( + &self, + url: String, + headers: Vec
, + ) -> Result; +} diff --git a/livekit-net/src/types.rs b/livekit-net/src/types.rs new file mode 100644 index 000000000..3f754f3ac --- /dev/null +++ b/livekit-net/src/types.rs @@ -0,0 +1,58 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt; + +/// A single HTTP/WebSocket request header. +#[cfg_attr(feature = "foreign", derive(uniffi::Record))] +#[derive(Debug, Clone)] +pub struct Header { + pub name: String, + pub value: String, +} + +/// The result of an HTTP GET performed by the transport. +#[cfg_attr(feature = "foreign", derive(uniffi::Record))] +#[derive(Debug, Clone)] +pub struct HttpResponse { + pub status: u16, + /// Response headers, in receipt order. Lets callers read e.g. `Cache-Control`. + pub headers: Vec
, + pub body: Vec, +} + +/// Errors a transport implementation may return. Mapped onto `SignalError` by the caller. +#[cfg_attr(feature = "foreign", derive(uniffi::Error))] +#[derive(Debug, Clone)] +pub enum TransportError { + Timeout, + Connection(String), + Http { status: u16 }, + Closed, + Other(String), +} + +impl fmt::Display for TransportError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TransportError::Timeout => write!(f, "transport timed out"), + TransportError::Connection(m) => write!(f, "transport connection error: {m}"), + TransportError::Http { status } => write!(f, "transport http error: status {status}"), + TransportError::Closed => write!(f, "transport closed"), + TransportError::Other(m) => write!(f, "transport error: {m}"), + } + } +} + +impl std::error::Error for TransportError {} diff --git a/livekit-net/tests/globals.rs b/livekit-net/tests/globals.rs new file mode 100644 index 000000000..13a33dd7d --- /dev/null +++ b/livekit-net/tests/globals.rs @@ -0,0 +1,62 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use livekit_net::{ + set_transport, transport, Header, HttpResponse, PlatformConnectResult, PlatformConnection, + PlatformTransport, TransportError, +}; +use std::sync::Arc; + +struct StubConn; + +#[async_trait::async_trait] +impl PlatformConnection for StubConn { + async fn send(&self, _frame: Vec) -> Result<(), TransportError> { + Ok(()) + } + async fn recv(&self) -> Result>, TransportError> { + Ok(None) + } + async fn close(&self) {} +} + +struct StubTransport; + +#[async_trait::async_trait] +impl PlatformTransport for StubTransport { + async fn connect( + &self, + _url: String, + _headers: Vec
, + _timeout_ms: u64, + ) -> Result { + Ok(PlatformConnectResult { connection: Arc::new(StubConn) }) + } + async fn http_get( + &self, + _url: String, + _headers: Vec
, + ) -> Result { + Ok(HttpResponse { status: 200, headers: vec![], body: b"ok".to_vec() }) + } +} + +#[tokio::test] +async fn registered_transport_is_returned() { + set_transport(Arc::new(StubTransport)); + let t = transport().expect("transport should be registered"); + let res = t.http_get("http://x".into(), vec![]).await.unwrap(); + assert_eq!(res.body, b"ok"); + assert_eq!(res.status, 200); +} diff --git a/livekit-net/tests/native_parity.rs b/livekit-net/tests/native_parity.rs new file mode 100644 index 000000000..0d8dabbef --- /dev/null +++ b/livekit-net/tests/native_parity.rs @@ -0,0 +1,113 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "__native")] + +use livekit_net::Header; + +// A hand-rolled one-shot HTTP server avoids adding a web-framework dep. +async fn spawn_http_once(status_line: &'static str, body: &'static str) -> u16 { + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let port = listener.local_addr().unwrap().port(); + tokio::spawn(async move { + let (mut sock, _) = listener.accept().await.unwrap(); + let mut buf = [0u8; 1024]; + let _ = sock.read(&mut buf).await; + let resp = format!( + "{status_line}\r\nContent-Length: {}\r\n\r\n{body}", + body.len() + ); + sock.write_all(resp.as_bytes()).await.unwrap(); + }); + port +} + +#[tokio::test] +async fn ws_send_recv_roundtrip() { + // Minimal echo WS server using tokio-tungstenite (dev-dep). + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let port = listener.local_addr().unwrap().port(); + tokio::spawn(async move { + let (stream, _) = listener.accept().await.unwrap(); + let mut ws = tokio_tungstenite::accept_async(stream).await.unwrap(); + use futures_util::{SinkExt, StreamExt}; + while let Some(Ok(msg)) = ws.next().await { + if msg.is_binary() { + ws.send(msg).await.unwrap(); + } + } + }); + + let t = livekit_net::testing::native_transport(); + let result = t + .connect(format!("ws://127.0.0.1:{port}"), vec![], 5000) + .await + .unwrap(); + let conn = result.connection; + conn.send(b"ping".to_vec()).await.unwrap(); + let echoed = conn.recv().await.unwrap(); + assert_eq!(echoed, Some(b"ping".to_vec())); + conn.close().await; +} + +#[tokio::test] +async fn http_get_returns_status_and_body() { + let port = spawn_http_once("HTTP/1.1 200 OK", "hello").await; + let t = livekit_net::testing::native_transport(); + let res = t + .http_get( + format!("http://127.0.0.1:{port}/settings/regions"), + vec![Header { name: "Authorization".into(), value: "Bearer x".into() }], + ) + .await + .unwrap(); + assert_eq!(res.status, 200); + assert_eq!(res.body, b"hello"); +} + +#[cfg(feature = "__native-tokio")] +#[tokio::test] +async fn ws_connect_404_yields_transport_http_error() { + // Spin up a one-shot TCP server that accepts the WS upgrade request and + // immediately responds HTTP 404, simulating the /rtc/v1 endpoint not existing. + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let port = listener.local_addr().unwrap().port(); + tokio::spawn(async move { + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + let (mut sock, _) = listener.accept().await.unwrap(); + // Read the upgrade request (discard it) + let mut buf = [0u8; 4096]; + let _ = sock.read(&mut buf).await; + // Respond with a 404 + let resp = "HTTP/1.1 404 Not Found +Content-Length: 0 + +"; + sock.write_all(resp.as_bytes()).await.unwrap(); + }); + + let t = livekit_net::testing::native_transport(); + let result = t + .connect(format!("ws://127.0.0.1:{port}"), vec![], 5000) + .await; + + match result { + Err(livekit_net::TransportError::Http { status }) => { + assert_eq!(status, 404, "expected HTTP 404, got {status}"); + } + Ok(_) => panic!("expected Err(TransportError::Http {{ status: 404 }}), but connect succeeded"), + Err(other) => panic!("expected Err(TransportError::Http {{ status: 404 }}), got Err({:?})", other), + } +} diff --git a/livekit/Cargo.toml b/livekit/Cargo.toml index 3eb83261e..a97fa4171 100644 --- a/livekit/Cargo.toml +++ b/livekit/Cargo.toml @@ -32,7 +32,7 @@ __lk-internal = [] # internal features (used by livekit-ffi) __lk-e2e-test = [] # end-to-end testing with a LiveKit server [dependencies] -livekit-runtime = { workspace = true } +livekit-runtime = { workspace = true, features = ["tokio"] } livekit-api = { workspace = true } libwebrtc = { workspace = true } livekit-protocol = { workspace = true } From 4cadc82f87552bf8c9f7ae9c3bf90418f1749146 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 17:42:13 +0100 Subject: [PATCH 2/7] refactor(livekit-api): route signalling through the livekit-net seam Make `signal_client` blind to the transport backend: it resolves `livekit_net::transport()` and drives the WebSocket via `PlatformConnection`, and the two pre-WS HTTP GETs (validate, region discovery) via `PlatformTransport::http_get`. Deletes the local tungstenite/proxy/TLS stack and the `WsError` coupling. - `signal_stream` runs its read/write tasks over `Arc`; WS control frames (ping/pong, close, reset) are the transport's concern. - `validate`/`fetch_from_endpoint` go through `http_get`; `TransportError` maps to `SignalError` via `map_transport_err` (handshake HTTP status is preserved, so the v1->v0 404 fallback and 403 handling still fire). - Feature decomposition: a blind `signal-client` feature (livekit-net, no backend) with thin `signal-client-{tokio,async,dispatcher}` wrappers that add `livekit-net/native-*`; TLS delegates to livekit-net; signalling WS/HTTP/TLS deps removed. `signal_client` has zero backend cfg branches. - Tests: a shared, once-registered deterministic mock transport (`test_transport.rs`) exercises the connect/validate/region paths. --- livekit-api/Cargo.toml | 82 ++-- livekit-api/src/http_client.rs | 60 +-- livekit-api/src/lib.rs | 18 +- livekit-api/src/signal_client/mod.rs | 387 ++++++++---------- .../src/signal_client/region_url_provider.rs | 41 +- .../src/signal_client/signal_stream.rs | 360 +++------------- .../src/signal_client/test_transport.rs | 141 +++++++ 7 files changed, 415 insertions(+), 674 deletions(-) create mode 100644 livekit-api/src/signal_client/test_transport.rs diff --git a/livekit-api/Cargo.toml b/livekit-api/Cargo.toml index 8afd55fc7..4b5197a71 100644 --- a/livekit-api/Cargo.toml +++ b/livekit-api/Cargo.toml @@ -11,39 +11,25 @@ readme = "README.md" # By default ws TLS is not enabled default = ["services-tokio", "access-token", "webhooks"] -signal-client-tokio = [ - "dep:tokio-tungstenite", - "dep:tokio", - "dep:futures-util", - "dep:reqwest", - "dep:livekit-runtime", - "livekit-runtime/tokio", - "dep:base64", - "dep:flate2", - "dep:bytes" -] - -signal-client-async = [ - "__signal-client-async-compatible", - "livekit-runtime/async" -] - -signal-client-dispatcher = [ - "__signal-client-async-compatible", - "livekit-runtime/dispatcher" -] - -__signal-client-async-compatible = [ - "dep:async-tungstenite", - "dep:tokio", # For macros and sync - "dep:futures-util", - "dep:isahc", +# Signalling client, blind to the transport backend. Pulls livekit-net (no +# backend), the tokio crate (signal_client uses tokio::sync directly), and +# protobuf helpers. The livekit-runtime flavor comes from the signal-client- +# wrappers below or the top crate — not here, so async/dispatcher don't collide +# with tokio under the one-runtime guard. Needs a runtime flavor to compile, and +# a registered livekit_net::transport() at runtime. +signal-client = [ + "dep:livekit-net", "dep:livekit-runtime", + "dep:tokio", "dep:base64", "dep:flate2", - "dep:bytes" + "dep:bytes", + "dep:serde_json", ] +signal-client-tokio = ["signal-client", "livekit-net/native-tokio", "livekit-runtime/tokio"] +signal-client-async = ["signal-client", "livekit-net/native-async", "livekit-runtime/async"] +signal-client-dispatcher = ["signal-client", "livekit-net/native-dispatcher", "livekit-runtime/dispatcher"] services-tokio = ["dep:reqwest", "dep:tokio", "tokio/time"] services-async = ["dep:isahc", "dep:futures-timer"] @@ -53,8 +39,7 @@ webhooks = ["access-token", "dep:serde_json", "dep:base64"] # TLS Configuration # ----------------- # These features control TLS behavior for WebSocket and HTTP connections. -# Note: These features only change the behavior of tokio-tungstenite and reqwest. -# They don't change the behavior of libwebrtc/webrtc-sys. +# TLS is delegated to livekit-net; no local tungstenite/rustls deps. # # IMPORTANT FOR CONTAINER DEPLOYMENTS: # When using `rustls-tls-native-roots`, the SDK relies on the operating system's @@ -72,34 +57,19 @@ webhooks = ["access-token", "dep:serde_json", "dep:base64"] # the embedded certificates can expire, requiring a fresh build to update them. # Uses the platform's native TLS implementation (OpenSSL on Linux, Secure Transport on macOS, SChannel on Windows) -native-tls = [ - "tokio-tungstenite?/native-tls", - "async-tungstenite?/async-native-tls", - "reqwest?/native-tls" -] +native-tls = ["livekit-net?/native-tls", "reqwest?/native-tls"] # Same as native-tls but compiles OpenSSL from source (useful for cross-compilation) -native-tls-vendored = [ - "tokio-tungstenite?/native-tls-vendored", - "reqwest?/native-tls-vendored", -] +native-tls-vendored = ["livekit-net?/native-tls-vendored", "reqwest?/native-tls-vendored"] # Uses rustls with the operating system's CA certificate store. # Requires ca-certificates to be installed in container environments. -rustls-tls-native-roots = [ - "tokio-tungstenite?/rustls-tls-native-roots", - "reqwest?/rustls-tls-native-roots", - "tokio-tungstenite?/__rustls-tls", - "dep:tokio-rustls", - "dep:rustls-native-certs" -] +rustls-tls-native-roots = ["livekit-net?/rustls-tls-native-roots", "reqwest?/rustls-tls-native-roots"] # Uses rustls with Mozilla's bundled root certificates. # RECOMMENDED for container deployments - no system CA certificates required. -rustls-tls-webpki-roots = [ - "tokio-tungstenite?/rustls-tls-webpki-roots", - "reqwest?/rustls-tls-webpki-roots", -] -__rustls-tls = ["tokio-tungstenite?/__rustls-tls", "reqwest?/__rustls"] +rustls-tls-webpki-roots = ["livekit-net?/rustls-tls-webpki-roots", "reqwest?/rustls-tls-webpki-roots"] +__rustls-tls = ["livekit-net?/__rustls-tls", "reqwest?/__rustls"] [dependencies] +livekit-net = { workspace = true, optional = true } livekit-protocol = { workspace = true } thiserror = { workspace = true } serde = { workspace = true, features = ["derive"] } @@ -121,14 +91,8 @@ hmac = { version = "0.12", optional = true } signature = { version = "2", optional = true } # signal_client -livekit-runtime = { workspace = true, optional = true} -tokio-tungstenite = { version = "0.29", features = ["url"], optional = true } -async-tungstenite = { version = "0.29", features = [ "async-std-runtime", "async-native-tls", "url"], optional = true } +livekit-runtime = { workspace = true, optional = true, default-features = false } tokio = { workspace = true, default-features = false, features = ["sync", "macros", "signal", "io-util", "net"], optional = true } -tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true } -rustls-native-certs = { version = "0.8", optional = true } -futures-util = { workspace = true, default-features = false, features = [ "sink" ], optional = true } -bytes = { workspace = true, optional = true } # This dependency must be kept in sync with reqwest's version http = "1.1" @@ -138,6 +102,7 @@ isahc = { version = "1.7.2", default-features = false, features = [ "json", "tex futures-timer = { version = "3", optional = true } flate2 = { version = "1", optional = true } +bytes = { workspace = true, optional = true } scopeguard = "1.2.0" rand = { workspace = true } os_info = "3.14.0" @@ -145,3 +110,4 @@ device-info = { workspace = true } [dev-dependencies] tokio = { workspace = true, features = ["rt", "rt-multi-thread", "net", "time", "macros", "io-util"] } +async-trait = "0.1" diff --git a/livekit-api/src/http_client.rs b/livekit-api/src/http_client.rs index 1ab8826c0..ec61f3745 100644 --- a/livekit-api/src/http_client.rs +++ b/livekit-api/src/http_client.rs @@ -12,27 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(any(feature = "services-tokio", feature = "signal-client-tokio"))] +#[cfg(feature = "services-tokio")] mod tokio { - #[cfg(feature = "services-tokio")] pub use reqwest::Client; - - /// GET with an `Authorization: Bearer ` header attached. - /// - /// `reqwest::get(url)` (the free function) constructs a fresh default - /// `Client` and attaches no auth, which is why `SignalInner::validate` — - /// the sole caller — uses this helper: the access token must reach the - /// server or the server returns 401 regardless of the underlying error. - #[cfg(feature = "signal-client-tokio")] - pub async fn get_with_token(url: &str, token: &str) -> reqwest::Result { - reqwest::Client::new().get(url).bearer_auth(token).send().await - } } -#[cfg(any(feature = "services-tokio", feature = "signal-client-tokio"))] +#[cfg(feature = "services-tokio")] pub use tokio::*; -#[cfg(any(feature = "__signal-client-async-compatible", feature = "services-async"))] +#[cfg(feature = "services-async")] mod async_std { #[cfg(any( @@ -43,47 +31,8 @@ mod async_std { ))] compile_error!("the async std compatible libraries do not support these features"); - #[cfg(any(feature = "__signal-client-async-compatible", feature = "services-async"))] pub struct Response(http::Response); - #[cfg(feature = "__signal-client-async-compatible")] - mod signal_client { - use std::io; - - use isahc::AsyncReadResponseExt; - - use super::Response; - - impl Response { - pub fn status(&self) -> http::StatusCode { - self.0.status() - } - - pub async fn text(mut self) -> io::Result { - self.0.text().await - } - } - - /// GET with an `Authorization: Bearer ` header attached. - /// - /// `isahc::get_async(url)` attaches no auth, which is why - /// `SignalInner::validate` — the sole caller — uses this helper: the - /// access token must reach the server or the server returns 401 - /// regardless of the underlying error. Mirrors the tokio variant. - pub async fn get_with_token(url: &str, token: &str) -> io::Result { - let request = isahc::Request::get(url) - .header("Authorization", format!("Bearer {}", token)) - .body(()) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - let response = isahc::send_async(request).await?; - Ok(Response(response)) - } - } - - #[cfg(feature = "__signal-client-async-compatible")] - pub use signal_client::*; - - #[cfg(feature = "services-async")] mod services { use std::io; @@ -177,9 +126,8 @@ mod async_std { } } - #[cfg(feature = "services-async")] pub use services::*; } -#[cfg(any(feature = "__signal-client-async-compatible", feature = "services-async"))] +#[cfg(feature = "services-async")] pub use async_std::*; diff --git a/livekit-api/src/lib.rs b/livekit-api/src/lib.rs index 878da8147..4e041aea0 100644 --- a/livekit-api/src/lib.rs +++ b/livekit-api/src/lib.rs @@ -23,29 +23,17 @@ mod jwt_provider; #[cfg(any(feature = "services-tokio", feature = "services-async"))] pub mod services; -#[cfg(any( - feature = "signal-client-tokio", - feature = "signal-client-async", - feature = "signal-client-dispatcher" -))] +#[cfg(feature = "signal-client")] pub mod signal_client; -#[cfg(any( - feature = "signal-client-tokio", - feature = "signal-client-async", - feature = "signal-client-dispatcher", - feature = "services-tokio", - feature = "services-async" -))] +#[cfg(any(feature = "services-tokio", feature = "services-async"))] mod http_client; // Region-discovery helpers shared by the signaling region provider // (signal_client::region_url_provider) and the API failover region cache // (services::failover). #[cfg(any( - feature = "signal-client-tokio", - feature = "signal-client-async", - feature = "signal-client-dispatcher", + feature = "signal-client", feature = "services-tokio", feature = "services-async" ))] diff --git a/livekit-api/src/signal_client/mod.rs b/livekit-api/src/signal_client/mod.rs index 76b7ba491..0fc20b1e0 100644 --- a/livekit-api/src/signal_client/mod.rs +++ b/livekit-api/src/signal_client/mod.rs @@ -33,17 +33,14 @@ use prost::Message; use thiserror::Error; use tokio::sync::{mpsc, Mutex as AsyncMutex, RwLock as AsyncRwLock}; -#[cfg(feature = "signal-client-tokio")] -use tokio_tungstenite::tungstenite::Error as WsError; - -#[cfg(feature = "__signal-client-async-compatible")] -use async_tungstenite::tungstenite::Error as WsError; - -use crate::{http_client, signal_client::signal_stream::SignalStream}; +use crate::signal_client::signal_stream::SignalStream; mod region_url_provider; mod signal_stream; +#[cfg(test)] +pub(crate) mod test_transport; + pub use region_url_provider::RegionUrlProvider; pub type SignalEmitter = mpsc::UnboundedSender; @@ -72,8 +69,6 @@ const CLIENT_PROTOCOL_VERSION: i32 = CLIENT_PROTOCOL_DATA_STREAM_RPC; #[derive(Error, Debug)] pub enum SignalError { - #[error("ws failure: {0}")] - WsError(#[from] WsError), #[error("failed to parse the url: {0}")] UrlParse(String), #[error("access token has invalid characters")] @@ -212,7 +207,7 @@ impl SignalClient { } Err(err) => { // fallback to region urls - if matches!(&err, SignalError::WsError(WsError::Http(e)) if e.status() != 403) { + if matches!(&err, SignalError::Client(code, _) if code.as_u16() != 403) { log::error!("unexpected signal error: {}", err.to_string()); } @@ -399,7 +394,7 @@ impl SignalInner { // Other errors (401, 403, 500, etc.) indicate real issues that shouldn't // be masked by falling back to a different signaling mode. let is_not_found = - matches!(&err, SignalError::WsError(WsError::Http(e)) if e.status() == 404); + matches!(&err, SignalError::Client(code, _) if code.as_u16() == 404); if use_v1_path && is_not_found { let lk_url_v0 = @@ -460,19 +455,28 @@ impl SignalInner { /// https://github.com/livekit/rust-sdks/issues/1042. async fn validate(ws_url: url::Url, token: &str) -> SignalResult<()> { let validate_url = get_validate_url(ws_url); + let transport = livekit_net::transport() + .ok_or_else(|| SignalError::Timeout("no platform transport registered".into()))?; + let headers = bearer_headers(token); let validate_fut = async { - if let Ok(res) = http_client::get_with_token(validate_url.as_str(), token).await { - let status = res.status(); - let body = res.text().await.ok().unwrap_or_default(); - - if status.is_client_error() { - return Err(SignalError::Client(status, body)); - } else if status.is_server_error() { - return Err(SignalError::Server(status, body)); - } + let res = transport + .http_get(validate_url.to_string(), headers) + .await + .map_err(map_transport_err)?; + let status = + http::StatusCode::from_u16(res.status).unwrap_or(http::StatusCode::OK); + if status.is_client_error() { + return Err(SignalError::Client( + status, + String::from_utf8_lossy(&res.body).into_owned(), + )); + } else if status.is_server_error() { + return Err(SignalError::Server( + status, + String::from_utf8_lossy(&res.body).into_owned(), + )); } - Ok(()) }; @@ -914,6 +918,37 @@ fn get_livekit_url( Ok(lk_url) } +/// Build the `Authorization: Bearer ` header vec used by HTTP/WS callers. +pub(super) fn bearer_headers(token: &str) -> Vec { + vec![livekit_net::Header { name: "Authorization".into(), value: format!("Bearer {token}") }] +} + +/// Map a [`livekit_net::TransportError`] to a [`SignalError`]. +/// +/// - `Timeout` → `SignalError::Timeout` (timed out at transport layer) +/// - `Http { status }` → `Client` for 4xx, `Server` for 5xx (empty body — caller +/// may have already read the body separately) +/// - `Closed` / `Connection` / `Other` → `SignalError::Timeout` (drives the +/// reconnect path; no dedicated closed/connection variant exists on `SignalError`) +pub(super) fn map_transport_err(e: livekit_net::TransportError) -> SignalError { + use livekit_net::TransportError as TE; + match e { + TE::Timeout => SignalError::Timeout("transport timed out".into()), + TE::Http { status } => { + let code = http::StatusCode::from_u16(status) + .unwrap_or(http::StatusCode::BAD_GATEWAY); + if code.is_client_error() { + SignalError::Client(code, String::new()) + } else { + SignalError::Server(code, String::new()) + } + } + TE::Closed => SignalError::Timeout("transport closed".into()), + TE::Connection(m) => SignalError::Timeout(m), + TE::Other(m) => SignalError::Timeout(m), + } +} + /// Convert a WebSocket URL (with /rtc or /rtc/v1 path) to the validate endpoint URL fn get_validate_url(mut ws_url: url::Url) -> url::Url { ws_url.set_scheme(if ws_url.scheme() == "wss" { "https" } else { "http" }).unwrap(); @@ -936,7 +971,7 @@ macro_rules! get_async_message { } } - Err(WsError::ConnectionClosed)? + Err(SignalError::Timeout("connection closed before message received".into())) }; livekit_runtime::timeout(JOIN_RESPONSE_TIMEOUT, join).await.map_err(|_| { @@ -969,7 +1004,7 @@ async fn get_reconnect_response( } } - Err(WsError::ConnectionClosed)? + Err(SignalError::Timeout("connection closed before message received".into())) }; livekit_runtime::timeout(JOIN_RESPONSE_TIMEOUT, join).await.map_err(|_| { @@ -1199,237 +1234,161 @@ mod tests { assert_eq!(capabilities, expected); } - /// Regression test for https://github.com/livekit/rust-sdks/issues/1042. - /// - /// Prior to the fix, `SignalInner::validate` issued an auth-less GET to - /// `/rtc/validate`, so a server performing the standard auth check (claims - /// parsing) would return 401 regardless of the real reason the WebSocket - /// upgrade had failed. That 401 then masked the original error (e.g. a - /// 503 from a saturated node) at the caller. - /// - /// This test binds a TCP listener, calls `validate()`, captures the first - /// request sent to that listener, and asserts the request carries an - /// `Authorization: Bearer ` header. - #[cfg(feature = "signal-client-tokio")] - #[tokio::test] - async fn validate_sends_bearer_token() { - use tokio::io::{AsyncReadExt, AsyncWriteExt}; - use tokio::net::TcpListener; - - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - - let (tx, rx) = tokio::sync::oneshot::channel::>(); - - tokio::spawn(async move { - let (mut socket, _) = listener.accept().await.unwrap(); - let mut buf = vec![0u8; 4096]; - let n = socket.read(&mut buf).await.unwrap(); - buf.truncate(n); - let _ = tx.send(buf); - - // Respond 200 OK so `validate()` returns Ok() rather than - // surfacing a synthetic client/server error. The purpose of this - // test is the request side, not the response side. - let response = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"; - let _ = socket.write_all(response).await; - }); - let ws_url = url::Url::parse(&format!("ws://127.0.0.1:{}/rtc", addr.port())).unwrap(); - let result = SignalInner::validate(ws_url, "test-bearer-token").await; - assert!(result.is_ok(), "expected Ok from validate, got: {:?}", result); + // ----------------------------------------------------------------------- + // map_transport_err unit tests (pure function, no transport needed) + // ----------------------------------------------------------------------- - let request = rx.await.expect("server task never received a request"); - let request = String::from_utf8_lossy(&request); - - // Header names are case-insensitive per RFC 9110; reqwest emits the - // canonical `Authorization` but we normalise both for robustness. - assert!( - request.to_lowercase().contains("authorization: bearer test-bearer-token"), - "validate() must attach the access token as a Bearer header; request was:\n{}", - request - ); + #[test] + fn map_transport_err_http_401_yields_client_error() { + use livekit_net::TransportError; + let err = map_transport_err(TransportError::Http { status: 401 }); + match err { + SignalError::Client(code, _) => { + assert_eq!(code.as_u16(), 401); + } + other => panic!("expected Client, got {:?}", other), + } } - #[cfg(feature = "signal-client-tokio")] - #[tokio::test] - async fn signal_stream_connect_timeout() { - use tokio::net::TcpListener; - - // Bind a TCP listener that accepts connections but never sends data - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - - // Spawn a task that accepts connections but does nothing (simulates a hanging server) - let _accept_task = tokio::spawn(async move { - loop { - let Ok((_socket, _)) = listener.accept().await else { - break; - }; - // Hold the connection open but never write anything - tokio::time::sleep(Duration::from_secs(60)).await; + #[test] + fn map_transport_err_http_503_yields_server_error() { + use livekit_net::TransportError; + let err = map_transport_err(TransportError::Http { status: 503 }); + match err { + SignalError::Server(code, _) => { + assert_eq!(code.as_u16(), 503); } - }); - - let url = url::Url::parse(&format!("ws://127.0.0.1:{}", addr.port())).unwrap(); - let result = SignalStream::connect(url, "fake-token", Duration::from_millis(500)).await; - - assert!(result.is_err()); - let err = result.unwrap_err(); - assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout error, got: {:?}", err); + other => panic!("expected Server, got {:?}", other), + } } - #[cfg(feature = "signal-client-tokio")] - #[tokio::test] - async fn region_fetch_parses_response() { - use tokio::io::AsyncWriteExt; - use tokio::net::TcpListener; - - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - - // Spawn a task that serves a hand-crafted HTTP response with region JSON - tokio::spawn(async move { - let (mut socket, _) = listener.accept().await.unwrap(); + #[test] + fn map_transport_err_timeout_yields_timeout() { + use livekit_net::TransportError; + let err = map_transport_err(TransportError::Timeout); + assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + } - // Read the request (consume it so the connection doesn't stall) - let mut buf = [0u8; 4096]; - let _ = tokio::io::AsyncReadExt::read(&mut socket, &mut buf).await; + #[test] + fn map_transport_err_connection_yields_timeout() { + use livekit_net::TransportError; + let err = map_transport_err(TransportError::Connection("conn failed".into())); + assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + } - let body = r#"{"regions":[{"region":"us-east-1","url":"wss://us-east.livekit.cloud","distance":"100"},{"region":"eu-west-1","url":"wss://eu-west.livekit.cloud","distance":"200"}]}"#; + #[test] + fn map_transport_err_other_yields_timeout() { + use livekit_net::TransportError; + let err = map_transport_err(TransportError::Other("something went wrong".into())); + assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + } - let response = format!( - "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}", - body.len(), - body - ); - socket.write_all(response.as_bytes()).await.unwrap(); - }); + #[test] + fn map_transport_err_closed_yields_timeout() { + use livekit_net::TransportError; + let err = map_transport_err(TransportError::Closed); + assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + } - let endpoint = format!("http://127.0.0.1:{}/settings/regions", addr.port()); - let result = region_url_provider::fetch_from_endpoint(&endpoint, "fake-token").await; + // Region + validate + stream behaviour, driven by the shared mock transport. - let (urls, _max_age) = result.unwrap(); - assert_eq!( - urls, - vec![ - "wss://us-east.livekit.cloud".to_string(), - "wss://eu-west.livekit.cloud".to_string(), - ] - ); + #[cfg(feature = "signal-client-tokio")] + #[tokio::test] + async fn region_fetch_via_mock_transport_parses_urls() { + use crate::signal_client::test_transport::install_mock_transport; + install_mock_transport(); + + // fetch_from_endpoint bypasses the is_cloud gate; the mock serves canned + // region JSON for URLs containing /settings/regions. + let (urls, _max_age) = region_url_provider::fetch_from_endpoint( + "https://mock.livekit.cloud/settings/regions", + "test-tok", + ) + .await + .expect("fetch_from_endpoint should succeed via mock"); + assert_eq!(urls, vec!["wss://us-mock.livekit.cloud".to_string()]); } + /// Regression test for https://github.com/livekit/rust-sdks/issues/1042. + /// + /// The mock returns HTTP 401 when the `Authorization: Bearer ` header + /// is absent; `validate()` maps 401 to `SignalError::Client`, so `is_ok()` + /// passes only when `validate()` forwarded a valid Bearer token. #[cfg(feature = "signal-client-tokio")] #[tokio::test] - async fn region_fetch_timeout() { - use tokio::net::TcpListener; - - // Bind a listener that accepts but never responds - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - - tokio::spawn(async move { - loop { - let Ok((_socket, _)) = listener.accept().await else { - break; - }; - // Hold connection open, never write - tokio::time::sleep(Duration::from_secs(60)).await; - } - }); - - let endpoint = format!("http://127.0.0.1:{}/settings/regions", addr.port()); - let result = region_url_provider::fetch_from_endpoint(&endpoint, "fake-token").await; + async fn validate_via_mock_transport_succeeds() { + use crate::signal_client::test_transport::install_mock_transport; + install_mock_transport(); - assert!(result.is_err()); - let err = result.unwrap_err(); + let ws_url = url::Url::parse("ws://mock.livekit.cloud/rtc").unwrap(); + let result = SignalInner::validate(ws_url, "test-tok").await; assert!( - matches!(err, SignalError::RegionError(ref msg) if msg.contains("timed out")), - "expected RegionError with 'timed out', got: {:?}", - err + result.is_ok(), + "validate() must forward Bearer token; mock returned 401 without it — got: {:?}", + result ); } - /// Test that connection errors include the full error chain. - /// This is critical for diagnosing TLS certificate issues in container deployments. + /// SignalStream delivers the first protobuf frame from the transport to the + /// events channel. The mock returns one canned Pong frame then closes. + #[cfg(feature = "signal-client-tokio")] + #[tokio::test] + async fn stream_delivers_first_frame() { + use crate::signal_client::test_transport::install_mock_transport; + install_mock_transport(); + + let (_stream, mut events) = SignalStream::connect( + url::Url::parse("ws://mock/rtc").unwrap(), + "tok", + Duration::from_secs(1), + ) + .await + .unwrap(); + let msg = events.recv().await.expect("expected a frame"); + assert!(matches!(*msg, proto::signal_response::Message::PongResp(_))); + } + + /// A transport `Connection` error is surfaced through `error_with_chain` into + /// `SignalError::RegionError`. The mock returns + /// `TransportError::Connection(..connection refused..)` for URLs marked + /// `connrefused`. #[cfg(feature = "signal-client-tokio")] #[tokio::test] async fn region_fetch_connection_refused_includes_error_chain() { - // Try to connect to a port that's definitely not listening - // This simulates a network-level failure - let endpoint = "http://127.0.0.1:1/settings/regions"; - let result = region_url_provider::fetch_from_endpoint(endpoint, "fake-token").await; + use crate::signal_client::test_transport::install_mock_transport; + install_mock_transport(); - assert!(result.is_err()); - let err = result.unwrap_err(); + let endpoint = "http://mock.test/connrefused/settings/regions"; + let result = region_url_provider::fetch_from_endpoint(endpoint, "test-tok").await; - // The error should be a RegionError + let err = result.expect_err("expected Err from connrefused endpoint"); let SignalError::RegionError(msg) = err else { panic!("expected RegionError, got: {:?}", err); }; - - // The error message should contain information about the connection failure. - // The exact message varies by platform, but it should contain more than just - // "error sending request" - it should include the underlying cause. - assert!( - msg.contains("error sending request") || msg.contains("connection"), - "Error should mention the request failure, got: {}", - msg - ); - - // Most importantly, verify the error contains a colon, indicating the chain - // was preserved (format is "outer: middle: inner") - // Note: On some platforms the error might be simple, so we just verify - // we got a descriptive error message assert!( - msg.len() > 20, - "Error message should be descriptive with chain info, got: {}", + msg.contains("connection refused"), + "error_with_chain must include 'connection refused' in the message, got: {}", msg ); } - /// Test that JSON parsing errors include the full error chain. + /// A non-JSON region body yields a descriptive `RegionError`. The mock + /// returns a 200 with a non-JSON body for URLs marked `badjson`. #[cfg(feature = "signal-client-tokio")] #[tokio::test] async fn region_fetch_invalid_json_includes_error_chain() { - use tokio::io::AsyncWriteExt; - use tokio::net::TcpListener; - - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - - // Spawn a task that returns invalid JSON - tokio::spawn(async move { - let (mut socket, _) = listener.accept().await.unwrap(); - - let mut buf = [0u8; 4096]; - let _ = tokio::io::AsyncReadExt::read(&mut socket, &mut buf).await; - - // Return invalid JSON that will fail to parse - let body = r#"{"invalid": "not a regions response"}"#; - let response = format!( - "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}", - body.len(), - body - ); - socket.write_all(response.as_bytes()).await.unwrap(); - }); - - let endpoint = format!("http://127.0.0.1:{}/settings/regions", addr.port()); - let result = region_url_provider::fetch_from_endpoint(&endpoint, "fake-token").await; - - assert!(result.is_err()); - let err = result.unwrap_err(); + use crate::signal_client::test_transport::install_mock_transport; + install_mock_transport(); - let SignalError::RegionError(msg) = err else { - panic!("expected RegionError, got: {:?}", err); - }; + let endpoint = "http://mock.test/badjson"; + let result = region_url_provider::fetch_from_endpoint(endpoint, "test-tok").await; - // The error should mention JSON parsing failure + let err = result.expect_err("expected Err from badjson endpoint"); assert!( - msg.contains("missing field") || msg.contains("error decoding") || msg.contains("JSON"), - "Error should mention JSON parsing failure, got: {}", - msg + matches!(err, SignalError::RegionError(_)), + "expected RegionError from serde parse failure, got: {:?}", + err ); } } diff --git a/livekit-api/src/signal_client/region_url_provider.rs b/livekit-api/src/signal_client/region_url_provider.rs index c27515aa7..b059fd500 100644 --- a/livekit-api/src/signal_client/region_url_provider.rs +++ b/livekit-api/src/signal_client/region_url_provider.rs @@ -19,11 +19,9 @@ use std::{ time::Duration, }; -use http::header::{HeaderMap, HeaderValue, AUTHORIZATION, CACHE_CONTROL}; use parking_lot::Mutex; use tokio::sync::Mutex as AsyncMutex; -use crate::http_client; use crate::region::{is_cloud_host, parse_max_age, Cached, RegionCache, RegionsResponse}; use super::{SignalError, SignalResult, REGION_FETCH_TIMEOUT}; @@ -169,30 +167,35 @@ pub(crate) async fn fetch_from_endpoint( endpoint_url: &str, token: &str, ) -> SignalResult<(Vec, Option)> { + let transport = livekit_net::transport() + .ok_or_else(|| SignalError::RegionError("no platform transport registered".into()))?; + let headers = super::bearer_headers(token); + let endpoint_url = endpoint_url.to_string(); + let fetch_fut = async { - let client = http_client::Client::new(); - let mut headers = HeaderMap::new(); - headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", token)).unwrap()); - let res = client - .get(endpoint_url) - .headers(headers) - .send() + let res = transport + .http_get(endpoint_url, headers) .await .map_err(|e| SignalError::RegionError(error_with_chain(&e)))?; - - if !res.status().is_success() { - return Err(SignalError::Client(res.status(), res.text().await.unwrap_or_default())); + let status = + http::StatusCode::from_u16(res.status).unwrap_or(http::StatusCode::BAD_GATEWAY); + if !status.is_success() { + return Err(SignalError::Client( + status, + String::from_utf8_lossy(&res.body).into_owned(), + )); } - // Read the cache lifetime before `json()` consumes the response. - let max_age = - res.headers().get(CACHE_CONTROL).and_then(|v| v.to_str().ok()).and_then(parse_max_age); + // Cache lifetime from the server's `Cache-Control: max-age`, if present. + let max_age = res + .headers + .iter() + .find(|h| h.name.eq_ignore_ascii_case("cache-control")) + .and_then(|h| parse_max_age(&h.value)); - let res = res - .json::() - .await + let parsed: RegionsResponse = serde_json::from_slice(&res.body) .map_err(|e| SignalError::RegionError(error_with_chain(&e)))?; - Ok((res.regions.into_iter().map(|i| i.url).collect(), max_age)) + Ok((parsed.regions.into_iter().map(|i| i.url).collect(), max_age)) }; livekit_runtime::timeout(REGION_FETCH_TIMEOUT, fetch_fut) diff --git a/livekit-api/src/signal_client/signal_stream.rs b/livekit-api/src/signal_client/signal_stream.rs index 7394b86a0..04a9e318c 100644 --- a/livekit-api/src/signal_client/signal_stream.rs +++ b/livekit-api/src/signal_client/signal_stream.rs @@ -12,65 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -use bytes::Bytes; -use futures_util::{ - stream::{SplitSink, SplitStream}, - SinkExt, StreamExt, -}; use livekit_protocol as proto; -use livekit_runtime::{JoinHandle, TcpStream}; +use livekit_runtime::JoinHandle; use prost::Message as ProtoMessage; -use std::{env, io, time::Duration}; +use std::{sync::Arc, time::Duration}; use tokio::sync::{mpsc, oneshot}; -#[cfg(feature = "signal-client-tokio")] -use base64; - -#[cfg(feature = "signal-client-tokio")] -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt}, - net::TcpStream as TokioTcpStream, -}; - -#[cfg(feature = "signal-client-tokio")] -use tokio_tungstenite::{ - connect_async, - tungstenite::client::IntoClientRequest, - tungstenite::error::ProtocolError, - tungstenite::http::{header::AUTHORIZATION, HeaderValue}, - tungstenite::{Error as WsError, Message}, - MaybeTlsStream, WebSocketStream, -}; - -#[cfg(feature = "__signal-client-async-compatible")] -use async_tungstenite::{ - async_std::connect_async, - async_std::ClientStream as MaybeTlsStream, - tungstenite::client::IntoClientRequest, - tungstenite::error::ProtocolError, - tungstenite::http::{header::AUTHORIZATION, HeaderValue}, - tungstenite::{Error as WsError, Message}, - WebSocketStream, -}; - use super::{SignalError, SignalResult}; -type WebSocket = WebSocketStream>; - #[derive(Debug)] enum InternalMessage { Signal { signal: proto::signal_request::Message, response_chn: oneshot::Sender>, }, - Pong { - ping_data: Bytes, - }, Close, } -/// SignalStream hold the WebSocket connection +/// SignalStream holds the WebSocket connection (via `PlatformConnection`). /// /// It is replaced by [SignalClient] at each reconnection. #[derive(Debug)] @@ -82,255 +42,39 @@ pub(super) struct SignalStream { impl SignalStream { /// Connect to livekit websocket. - /// Return SignalError if the connections failed + /// Returns SignalError if the connection failed. /// - /// SignalStream will never try to reconnect if the connection has been - /// closed. + /// SignalStream will never try to reconnect if the connection has been closed. pub async fn connect( url: url::Url, token: &str, connect_timeout: Duration, - ) -> SignalResult<(Self, mpsc::UnboundedReceiver>)> { - let connect_fut = Self::connect_inner(url, token); - livekit_runtime::timeout(connect_timeout, connect_fut) - .await - .map_err(|_| SignalError::Timeout("signal connection timed out".into()))? - } - - async fn connect_inner( - url: url::Url, - token: &str, ) -> SignalResult<(Self, mpsc::UnboundedReceiver>)> { log::info!("connecting to {}", url); - let mut request = url.clone().into_client_request()?; - let auth_header = HeaderValue::from_str(&format!("Bearer {token}")) - .map_err(|_| SignalError::TokenFormat)?; - request.headers_mut().insert(AUTHORIZATION, auth_header); - - #[cfg(feature = "signal-client-tokio")] - let ws_stream = { - // Check for HTTP_PROXY or HTTPS_PROXY environment variables - let proxy_env = if url.scheme() == "wss" { - env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy")) - } else { - env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy")) - }; - - // Connect directly or through proxy - let ws_stream = if let Ok(proxy_url) = proxy_env { - if !proxy_url.is_empty() { - log::info!("Using proxy: {}", proxy_url); - let proxy_url = url::Url::parse(&proxy_url).map_err(|e| { - WsError::Io(io::Error::new( - io::ErrorKind::InvalidInput, - format!("Invalid proxy URL: {}", e), - )) - })?; - - let host = url.host_str().ok_or_else(|| { - WsError::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "Target URL has no host", - )) - })?; - - let port = url.port_or_known_default().ok_or_else(|| { - WsError::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "Target URL has no port and no default for scheme", - )) - })?; - - let proxy_host = proxy_url.host_str().ok_or_else(|| { - WsError::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "Proxy URL has no host", - )) - })?; - - let proxy_port = proxy_url.port_or_known_default().unwrap_or(80); - let proxy_addr = format!("{}:{}", proxy_host, proxy_port); - - let mut proxy_stream = - TokioTcpStream::connect(proxy_addr).await.map_err(WsError::Io)?; - - let mut proxy_auth_header = None; - if let Some(password) = proxy_url.password() { - let auth = format!("{}:{}", proxy_url.username(), password); - let auth = format!("Basic {}", base64::encode(auth)); - proxy_auth_header = Some(auth); - } - - // Send CONNECT request - let target = format!("{}:{}", host, port); - let mut connect_req = - format!("CONNECT {} HTTP/1.1\r\nHost: {}\r\n", target, target); - - // Add proxy authorization if needed - if let Some(auth) = proxy_auth_header { - connect_req.push_str(&format!("Proxy-Authorization: {}\r\n", auth)); - } - - // Finalize request - connect_req.push_str("\r\n"); - - log::debug!("Sending CONNECT request to proxy"); - proxy_stream.write_all(connect_req.as_bytes()).await.map_err(WsError::Io)?; - - // Read and parse response - let mut response = Vec::new(); - let mut buf = [0u8; 4096]; - let mut headers_complete = false; - - while !headers_complete { - let n = proxy_stream.read(&mut buf).await.map_err(WsError::Io)?; - if n == 0 { - return Err(WsError::Io(io::Error::new( - io::ErrorKind::UnexpectedEof, - "Proxy connection closed while reading response", - )) - .into()); - } - - response.extend_from_slice(&buf[..n]); - - // Check if we've received the end of headers (double CRLF) - if response.windows(4).any(|w| w == b"\r\n\r\n") { - headers_complete = true; - } - } - - // Parse status line - let response_str = String::from_utf8_lossy(&response); - let status_line = response_str.lines().next().ok_or_else(|| { - WsError::Io(io::Error::new( - io::ErrorKind::InvalidData, - "Invalid proxy response", - )) - })?; - - // Check status code - if !status_line.contains("200") { - return Err(WsError::Io(io::Error::new( - io::ErrorKind::ConnectionRefused, - format!("Proxy connection failed: {}", status_line), - )) - .into()); - } - - log::debug!("Proxy connection established to {}", target); - - // Create MaybeTlsStream based on original URL scheme - let stream = if url.scheme() == "wss" { - // Only enable proxy TLS support when rustls-tls-native-roots is enabled - #[cfg(feature = "rustls-tls-native-roots")] - { - // For WSS, we need to establish TLS over the proxy connection - use std::sync::Arc; - use tokio_rustls::{ - rustls::{self, pki_types::ServerName}, - TlsConnector, - }; - - // Load native root certificates - let mut root_store = rustls::RootCertStore::empty(); - let cert_result = rustls_native_certs::load_native_certs(); - if !cert_result.errors.is_empty() { - log::warn!( - "Native root CA certificate loading errors: {:?}", - cert_result.errors - ); - } - if cert_result.certs.is_empty() { - return Err(WsError::Io(io::Error::new( - io::ErrorKind::Other, - format!( - "Could not load any native root certificates: {:?}", - cert_result.errors - ), - )) - .into()); - } - let total = cert_result.certs.len(); - let (added, ignored) = - root_store.add_parsable_certificates(cert_result.certs); - log::debug!( - "Added {added}/{total} native root certificates ({ignored} ignored)" - ); - - let tls_config = rustls::ClientConfig::builder() - .with_root_certificates(root_store) - .with_no_client_auth(); - - let server_name = - ServerName::try_from(host.to_owned()).map_err(|_| { - WsError::Io(io::Error::new( - io::ErrorKind::InvalidInput, - format!("Invalid DNS name: {}", host), - )) - })?; - let connector = TlsConnector::from(Arc::new(tls_config)); - let tls_stream = connector - .connect(server_name, proxy_stream) - .await - .map_err(|e| { - WsError::Io(io::Error::new( - io::ErrorKind::Other, - format!("TLS connection error: {}", e), - )) - })?; + let transport = livekit_net::transport() + .ok_or_else(|| SignalError::Timeout("no platform transport registered".into()))?; - MaybeTlsStream::Rustls(tls_stream) - } - - #[cfg(not(feature = "rustls-tls-native-roots"))] - { - // For non-rustls-tls-native-roots builds, don't support proxy for WSS - return Err(WsError::Io(io::Error::new( - io::ErrorKind::Other, - "WSS over proxy requires rustls-tls-native-roots feature", - )) - .into()); - } - } else { - // For plain WS, just use the proxy stream directly - MaybeTlsStream::Plain(proxy_stream) - }; + let headers = super::bearer_headers(token); - // Now perform WebSocket handshake over the established connection - let (ws_stream, _) = - tokio_tungstenite::client_async_with_config(request, stream, None).await?; - ws_stream - } else { - // No proxy specified, connect directly - let (ws_stream, _) = connect_async(request).await?; - ws_stream - } - } else { - // Non-tokio build or no proxy - connect directly - let (ws_stream, _) = connect_async(request).await?; - ws_stream - }; - - ws_stream - }; - - #[cfg(not(feature = "signal-client-tokio"))] - let (ws_stream, _) = connect_async(request).await?; - let (ws_writer, ws_reader) = ws_stream.split(); + let conn = transport + .connect(url.to_string(), headers, connect_timeout.as_millis() as u64) + .await + .map_err(super::map_transport_err)? + .connection; let (emitter, events) = mpsc::unbounded_channel(); let (internal_tx, internal_rx) = mpsc::channel::(8); - let write_handle = livekit_runtime::spawn(Self::write_task(internal_rx, ws_writer)); + let write_handle = + livekit_runtime::spawn(Self::write_task(internal_rx, conn.clone())); let read_handle = - livekit_runtime::spawn(Self::read_task(internal_tx.clone(), ws_reader, emitter)); + livekit_runtime::spawn(Self::read_task(internal_tx.clone(), conn, emitter)); Ok((Self { internal_tx, read_handle, write_handle }, events)) } - /// Close the websocket - /// It sends a CloseFrame to the server before closing + /// Close the websocket. + /// It sends a Close message before closing. pub async fn close(self, notify_close: bool) { if notify_close { let _ = self.internal_tx.send(InternalMessage::Close).await; @@ -339,8 +83,8 @@ impl SignalStream { let _ = self.read_handle.await; } - /// Send a SignalRequest to the websocket - /// It also waits for the message to be sent + /// Send a SignalRequest to the websocket. + /// It also waits for the message to be sent. pub async fn send(&self, signal: proto::signal_request::Message) -> SignalResult<()> { let (send, recv) = oneshot::channel(); let msg = InternalMessage::Signal { signal, response_chn: send }; @@ -348,74 +92,66 @@ impl SignalStream { recv.await.map_err(|_| SignalError::SendError)? } - /// This task is used to send messages to the websocket - /// It is also responsible for closing the connection + /// This task is used to send messages to the websocket. + /// It is also responsible for closing the connection. async fn write_task( mut internal_rx: mpsc::Receiver, - mut ws_writer: SplitSink, + conn: Arc, ) { while let Some(msg) = internal_rx.recv().await { match msg { InternalMessage::Signal { signal, response_chn } => { let data = proto::SignalRequest { message: Some(signal) }.encode_to_vec(); - if let Err(err) = ws_writer.send(Message::Binary(data.into())).await { - let _ = response_chn.send(Err(err.into())); + if let Err(err) = conn.send(data).await { + let _ = response_chn + .send(Err(SignalError::Timeout(err.to_string()))); break; } let _ = response_chn.send(Ok(())); } - InternalMessage::Pong { ping_data } => { - if let Err(err) = ws_writer.send(Message::Pong(ping_data)).await { - log::error!("failed to send pong message: {:?}", err); - } - } InternalMessage::Close => break, } } - let _ = ws_writer.close().await; + conn.close().await; } /// This task is used to read incoming messages from the websocket /// and dispatch them through the EventEmitter. - /// - /// It can also send messages to [handle_write] task ( Used e.g. answer to pings ) async fn read_task( internal_tx: mpsc::Sender, - mut ws_reader: SplitStream, + conn: Arc, emitter: mpsc::UnboundedSender>, ) { - while let Some(msg) = ws_reader.next().await { - match msg { - Ok(Message::Binary(data)) => { - let res = proto::SignalResponse::decode(data.as_ref()) - .expect("failed to decode SignalResponse"); - - if let Some(msg) = res.message { - let _ = emitter.send(Box::new(msg)); + loop { + match conn.recv().await { + Ok(Some(bytes)) => { + match proto::SignalResponse::decode(bytes.as_slice()) { + Ok(res) => { + if let Some(msg) = res.message { + let _ = emitter.send(Box::new(msg)); + } + } + Err(e) => { + log::error!("failed to decode SignalResponse: {:?}", e); + // continue on decode error — don't tear down the connection + } } } - Ok(Message::Ping(data)) => { - let _ = internal_tx.send(InternalMessage::Pong { ping_data: data }).await; - continue; - } - Ok(Message::Close(close)) => { - log::debug!("server closed the connection: {:?}", close); + Ok(None) => { + // Peer/transport closed gracefully + let _ = internal_tx.send(InternalMessage::Close).await; break; } - Ok(Message::Frame(_)) => {} - Err(WsError::Protocol(ProtocolError::ResetWithoutClosingHandshake)) => { - break; // Ignore - } - _ => { - log::error!("unhandled websocket message {:?}", msg); + Err(e) => { + log::error!("websocket recv error: {:?}", e); + let _ = internal_tx.send(InternalMessage::Close).await; break; } } } - - let _ = internal_tx.send(InternalMessage::Close).await; } } + diff --git a/livekit-api/src/signal_client/test_transport.rs b/livekit-api/src/signal_client/test_transport.rs new file mode 100644 index 000000000..d43bb4187 --- /dev/null +++ b/livekit-api/src/signal_client/test_transport.rs @@ -0,0 +1,141 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Shared mock transport for livekit-api unit tests. +//! +//! `install_mock_transport()` registers the mock exactly once (process-global +//! OnceLock), so it is safe to call from multiple tests in the same binary. +//! All tests share the same deterministic mock. +//! +//! MockConn::recv() yields one canned `proto::SignalResponse { Pong }` frame +//! and then returns `Ok(None)`. +//! +//! MockTransport::http_get dispatches deterministically on the request inputs: +//! +//! 1. If the request has NO `Authorization: Bearer ` header → +//! returns 401. This means any test that expects `Ok` from `validate()` or +//! `fetch_from_endpoint()` implicitly proves the Bearer token was forwarded. +//! 2. Else dispatches on the URL path: +//! - path contains `/settings/regions` → 200 + canned RegionUrlResponse JSON +//! - URL contains the marker `connrefused` → `TransportError::Connection` +//! (exercises the `error_with_chain` mapping in `fetch_from_endpoint`) +//! - URL contains the marker `badjson` → 200 + non-JSON body +//! (exercises the serde parse-error path) +//! - otherwise (validate or any other endpoint) → 200 + empty body + +use livekit_net::{ + Header, HttpResponse, PlatformConnectResult, PlatformConnection, PlatformTransport, + TransportError, +}; +use livekit_protocol as proto; +use prost::Message as _; +use std::sync::{Arc, Once}; +use tokio::sync::Mutex as AsyncMutex; + +// --------------------------------------------------------------------------- +// MockConn: yields one canned Pong frame then None +// --------------------------------------------------------------------------- + +pub struct MockConn { + outbound: AsyncMutex>>, +} + +#[async_trait::async_trait] +impl PlatformConnection for MockConn { + async fn send(&self, _frame: Vec) -> Result<(), TransportError> { + Ok(()) + } + + async fn recv(&self) -> Result>, TransportError> { + Ok(self.outbound.lock().await.pop()) + } + + async fn close(&self) {} +} + +// --------------------------------------------------------------------------- +// MockTransport: creates a MockConn pre-loaded with one Pong frame +// --------------------------------------------------------------------------- + +pub struct MockTransport; + +#[async_trait::async_trait] +impl PlatformTransport for MockTransport { + async fn connect( + &self, + _url: String, + _headers: Vec
, + _timeout_ms: u64, + ) -> Result { + let pong = proto::SignalResponse { + message: Some(proto::signal_response::Message::PongResp(proto::Pong::default())), + }; + // vec is popped from the end, so push the last frame first + let frames = vec![pong.encode_to_vec()]; + Ok(PlatformConnectResult { + connection: Arc::new(MockConn { outbound: AsyncMutex::new(frames) }), + }) + } + + async fn http_get( + &self, + url: String, + headers: Vec
, + ) -> Result { + // Rule 1: require a non-empty Bearer token. + // Any test that expects Ok() from validate() or fetch_from_endpoint() + // therefore implicitly proves the Bearer header was forwarded. + let has_bearer = headers.iter().any(|h| { + h.name.eq_ignore_ascii_case("Authorization") + && h.value.starts_with("Bearer ") + && h.value.len() > "Bearer ".len() + }); + if !has_bearer { + return Ok(HttpResponse { status: 401, headers: vec![], body: b"missing bearer".to_vec() }); + } + + // Rule 2: dispatch on URL. + if url.contains("connrefused") { + // Simulate a connection-refused transport error to exercise error_with_chain. + return Err(TransportError::Connection( + "error trying to connect: connection refused".into(), + )); + } + if url.contains("badjson") { + // Return non-JSON body to exercise the serde parse-error path. + return Ok(HttpResponse { status: 200, headers: vec![], body: b"this is not json".to_vec() }); + } + if url.contains("/settings/regions") { + // Serve a canned RegionUrlResponse JSON for region discovery tests. + let body = br#"{"regions":[{"region":"us-mock-1","url":"wss://us-mock.livekit.cloud","distance":"10"}]}"#; + return Ok(HttpResponse { status: 200, headers: vec![], body: body.to_vec() }); + } + // Default: validate or any other endpoint — return 200 with empty body. + Ok(HttpResponse { status: 200, headers: vec![], body: vec![] }) + } +} + +// --------------------------------------------------------------------------- +// Once-guarded registration +// --------------------------------------------------------------------------- + +static INSTALL: Once = Once::new(); + +/// Register the shared MockTransport exactly once. +/// Safe to call from multiple tests — subsequent calls are no-ops. +pub fn install_mock_transport() { + INSTALL.call_once(|| { + livekit_net::set_transport(Arc::new(MockTransport)); + }); +} From 346e9b9b5255bd4c201521555b0c3e7cb4ce73d4 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 17:42:45 +0100 Subject: [PATCH 3/7] feat(livekit): add foreign transport backend feature Add a `foreign` feature that pairs the blind `livekit-api/signal-client` with `livekit-net/foreign`, letting a host-provided transport drive signalling. `livekit-net` becomes an optional dep (pulled only by `foreign`), so default (tokio) builds are unaffected. TLS feature comments updated to reflect that TLS now delegates through livekit-net. --- livekit/Cargo.toml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/livekit/Cargo.toml b/livekit/Cargo.toml index a97fa4171..83f3ac4d7 100644 --- a/livekit/Cargo.toml +++ b/livekit/Cargo.toml @@ -14,6 +14,10 @@ default = ["tokio"] async = ["livekit-api/signal-client-async"] tokio = ["livekit-api/signal-client-tokio"] dispatcher = ["livekit-api/signal-client-dispatcher"] +# Foreign (host-provided) transport backend. The runtime is tokio in practice +# (libwebrtc pins it), so this pairs the foreign transport with the existing +# tokio runtime; consumers needing a different runtime combine with livekit-net/. +foreign = ["dep:livekit-net", "livekit-api/signal-client", "livekit-net/foreign"] # On Wayland, libwebrtc uses GDBus to communicate with the XDG Desktop Portal. # GDBus requires a GLib event loop to be running. Enable this feature if you @@ -21,8 +25,8 @@ dispatcher = ["livekit-api/signal-client-dispatcher"] # running in your application. glib-main-loop = [ "libwebrtc/glib-main-loop" ] -# Note that the following features only change the behavior of tokio-tungstenite. -# It doesn't change the behavior of libwebrtc/webrtc-sys +# TLS features delegate to livekit-api and from there to livekit-net (tokio-tungstenite / reqwest / rustls). +# They do not change the behavior of libwebrtc/webrtc-sys. native-tls = ["livekit-api/native-tls"] native-tls-vendored = ["livekit-api/native-tls-vendored"] rustls-tls-native-roots = ["livekit-api/rustls-tls-native-roots"] @@ -34,6 +38,7 @@ __lk-e2e-test = [] # end-to-end testing with a LiveKit server [dependencies] livekit-runtime = { workspace = true, features = ["tokio"] } livekit-api = { workspace = true } +livekit-net = { workspace = true, optional = true } libwebrtc = { workspace = true } livekit-protocol = { workspace = true } livekit-datatrack = { workspace = true } From 9f2988e70d7388548455df98670fd13f6d0b29f7 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 17:42:57 +0100 Subject: [PATCH 4/7] feat(livekit-uniffi): host-injectable transport via setPlatformTransport Activate the foreign backend in the cdylib (livekit-net foreign + tokio, livekit-api blind signal-client) and export `set_platform_transport` so a Dart/Swift/Kotlin host can register its own `PlatformTransport` across the UniFFI boundary. Depend on livekit-api directly with default-features off to drop the services (reqwest) stack from the cdylib. --- livekit-uniffi/Cargo.toml | 6 ++++- livekit-uniffi/src/lib.rs | 4 +++ livekit-uniffi/src/platform_transport.rs | 24 ++++++++++++++++++ livekit-uniffi/tests/foreign_path.rs | 32 ++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 livekit-uniffi/src/platform_transport.rs create mode 100644 livekit-uniffi/tests/foreign_path.rs diff --git a/livekit-uniffi/Cargo.toml b/livekit-uniffi/Cargo.toml index aa876af05..39c803a12 100644 --- a/livekit-uniffi/Cargo.toml +++ b/livekit-uniffi/Cargo.toml @@ -13,7 +13,8 @@ publish = false [dependencies] livekit-protocol = { workspace = true } -livekit-api = { workspace = true } +livekit-api = { path = "../livekit-api", version = "0.5.1", default-features = false, features = ["signal-client", "access-token"] } +livekit-net = { workspace = true, features = ["foreign", "tokio"] } uniffi = { version = "0.31", features = ["cli", "scaffolding-ffi-buffer-fns"] } log = { workspace = true } tokio = { workspace = true, features = ["sync"] } @@ -28,6 +29,9 @@ camino = { version = "1", optional = true } # Enables the `uniffi-bindgen-dart` bin. Not enabled for library builds. dart-bindgen = ["dep:uniffi-dart", "dep:camino"] +[dev-dependencies] +async-trait = "0.1" + [build-dependencies] uniffi = { version = "0.31", features = ["build", "scaffolding-ffi-buffer-fns"] } diff --git a/livekit-uniffi/src/lib.rs b/livekit-uniffi/src/lib.rs index 698bd4d0b..f4fab209b 100644 --- a/livekit-uniffi/src/lib.rs +++ b/livekit-uniffi/src/lib.rs @@ -21,4 +21,8 @@ pub mod log_forward; /// Information about the build such as version. pub mod build_info; +/// Register a host-provided network transport for signalling. +pub mod platform_transport; +pub use platform_transport::set_platform_transport; + uniffi::setup_scaffolding!(); diff --git a/livekit-uniffi/src/platform_transport.rs b/livekit-uniffi/src/platform_transport.rs new file mode 100644 index 000000000..8ec033f41 --- /dev/null +++ b/livekit-uniffi/src/platform_transport.rs @@ -0,0 +1,24 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use livekit_net::PlatformTransport; +use std::sync::Arc; + +/// Register the host-provided network transport. The host implements +/// `PlatformTransport` (and `PlatformConnection`) in Swift/Kotlin/Dart and calls +/// this once at startup, before the first connection. +#[uniffi::export] +pub fn set_platform_transport(transport: Arc) { + livekit_net::set_transport(transport); +} diff --git a/livekit-uniffi/tests/foreign_path.rs b/livekit-uniffi/tests/foreign_path.rs new file mode 100644 index 000000000..00b241025 --- /dev/null +++ b/livekit-uniffi/tests/foreign_path.rs @@ -0,0 +1,32 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use livekit_net::{Header, HttpResponse, PlatformConnectResult, PlatformTransport, TransportError}; +use std::sync::Arc; + +struct T; + +#[async_trait::async_trait] +impl PlatformTransport for T { + async fn connect(&self, _u: String, _h: Vec
, _timeout_ms: u64) + -> Result { Err(TransportError::Closed) } + async fn http_get(&self, _u: String, _h: Vec
) + -> Result { Err(TransportError::Closed) } +} + +#[test] +fn set_platform_transport_registers() { + livekit_uniffi::set_platform_transport(Arc::new(T)); + assert!(livekit_net::transport().is_some()); +} From cf7a81df477c34a79ad0ea848aebfc66e85946a1 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 17:42:57 +0100 Subject: [PATCH 5/7] ci: cover the transport seam; document workspace runtime invariant Run the new livekit-net / signal-client / livekit-uniffi test suites in CI (they need no dev server), and note in builds.yml that livekit-uniffi must stay on the tokio runtime while it shares --workspace with the desktop livekit build. --- .github/workflows/builds.yml | 7 +++++++ .github/workflows/tests.yml | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 06fa9a297..65b7c63ea 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -154,6 +154,13 @@ jobs: restore-keys: | ${{ runner.os }}-cargo-target-${{ matrix.target }}- + # Runtime-invariant: livekit (desktop) is tokio-only; livekit-uniffi uses + # the foreign+tokio feature pair, which also resolves to tokio at the + # livekit-runtime layer. Both crates therefore satisfy the one-runtime + # guard when built together via --workspace. + # IMPORTANT: a future cdylib that uses foreign+dispatcher/async-std MUST + # NOT share this --workspace invocation; use its own build job (like the + # uniffi-cdylib.yml `-p livekit-uniffi` job) so the runtimes do not clash. - name: Build (Cargo) if: ${{ !contains(matrix.target, 'android') }} run: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 53e15722a..4b5e0b061 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -162,4 +162,12 @@ jobs: --features __lk-e2e-test \ -- \ --nocapture \ - --test-threads=1 \ No newline at end of file + --test-threads=1 + + - name: Test transport seam (livekit-net / signal-client / uniffi) + shell: bash + run: | + cargo test --verbose --target ${{ matrix.target }} -p livekit-net --features native-tokio + cargo test --verbose --target ${{ matrix.target }} -p livekit-net --features foreign + cargo test --verbose --target ${{ matrix.target }} -p livekit-api --features signal-client-tokio + cargo test --verbose --target ${{ matrix.target }} -p livekit-uniffi \ No newline at end of file From 37585c3fc11c64d75a1146545e390eb61fb7d42b Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 18:10:49 +0100 Subject: [PATCH 6/7] refactor(livekit-api): richer, non_exhaustive SignalError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the lossy "everything → Timeout" transport-error mapping with dedicated variants and future-proof the enum: - `TransportError::Connection`/`Other` → `SignalError::Connection(String)` - `TransportError::Closed` → `SignalError::Closed` - (`Timeout`/`Http` unchanged) `SignalError` is now `#[non_exhaustive]`, so future variant additions are non-breaking. Reconnect behavior is unchanged: the engine escalates every non-`LeaveRequest` signal error to a full reconnect, so the new variants follow the same path `Timeout` did. --- livekit-api/src/signal_client/mod.rs | 41 ++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/livekit-api/src/signal_client/mod.rs b/livekit-api/src/signal_client/mod.rs index 0fc20b1e0..fd79d0913 100644 --- a/livekit-api/src/signal_client/mod.rs +++ b/livekit-api/src/signal_client/mod.rs @@ -68,6 +68,7 @@ pub const CLIENT_PROTOCOL_DATA_STREAM_RPC: i32 = 1; const CLIENT_PROTOCOL_VERSION: i32 = CLIENT_PROTOCOL_DATA_STREAM_RPC; #[derive(Error, Debug)] +#[non_exhaustive] pub enum SignalError { #[error("failed to parse the url: {0}")] UrlParse(String), @@ -83,6 +84,10 @@ pub enum SignalError { Timeout(String), #[error("failed to send message to the server")] SendError, + #[error("transport connection error: {0}")] + Connection(String), + #[error("transport closed")] + Closed, /// Failed to retrieve region information from LiveKit Cloud. /// /// This error occurs when the SDK cannot fetch the `/settings/regions` endpoint @@ -928,8 +933,10 @@ pub(super) fn bearer_headers(token: &str) -> Vec { /// - `Timeout` → `SignalError::Timeout` (timed out at transport layer) /// - `Http { status }` → `Client` for 4xx, `Server` for 5xx (empty body — caller /// may have already read the body separately) -/// - `Closed` / `Connection` / `Other` → `SignalError::Timeout` (drives the -/// reconnect path; no dedicated closed/connection variant exists on `SignalError`) +/// - `Connection` / `Other` → `SignalError::Connection` (network/TLS/transport failure) +/// - `Closed` → `SignalError::Closed` (peer/transport closed) +/// +/// Every variant except `LeaveRequest` drives the engine's full-reconnect path. pub(super) fn map_transport_err(e: livekit_net::TransportError) -> SignalError { use livekit_net::TransportError as TE; match e { @@ -943,9 +950,9 @@ pub(super) fn map_transport_err(e: livekit_net::TransportError) -> SignalError { SignalError::Server(code, String::new()) } } - TE::Closed => SignalError::Timeout("transport closed".into()), - TE::Connection(m) => SignalError::Timeout(m), - TE::Other(m) => SignalError::Timeout(m), + TE::Closed => SignalError::Closed, + TE::Connection(m) => SignalError::Connection(m), + TE::Other(m) => SignalError::Connection(m), } } @@ -1271,24 +1278,36 @@ mod tests { } #[test] - fn map_transport_err_connection_yields_timeout() { + fn map_transport_err_connection_yields_connection() { use livekit_net::TransportError; let err = map_transport_err(TransportError::Connection("conn failed".into())); - assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + assert!( + matches!(&err, SignalError::Connection(m) if m == "conn failed"), + "expected Connection, got {:?}", + err + ); } #[test] - fn map_transport_err_other_yields_timeout() { + fn map_transport_err_other_yields_connection() { use livekit_net::TransportError; let err = map_transport_err(TransportError::Other("something went wrong".into())); - assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + assert!( + matches!(&err, SignalError::Connection(m) if m == "something went wrong"), + "expected Connection, got {:?}", + err + ); } #[test] - fn map_transport_err_closed_yields_timeout() { + fn map_transport_err_closed_yields_closed() { use livekit_net::TransportError; let err = map_transport_err(TransportError::Closed); - assert!(matches!(err, SignalError::Timeout(_)), "expected Timeout, got {:?}", err); + assert!( + matches!(err, SignalError::Closed), + "expected Closed, got {:?}", + err + ); } // Region + validate + stream behaviour, driven by the shared mock transport. From 2ba17ded9844582ee0d36306263bdcfcc6e3ed75 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 2 Jul 2026 18:45:52 +0100 Subject: [PATCH 7/7] chore(release): changeset for transport seam; register livekit-net with knope Mark livekit-api and livekit as breaking (major) and livekit-uniffi as a feature (minor); on these 0.x crates knope bumps the minor (livekit-api 0.5.1 -> 0.6.0, livekit 0.7.45 -> 0.8.0). Register the new livekit-net crate as a knope package so its version and the workspace dependency entry are managed at release time; it ships at its initial 0.1.0. --- .changeset/livekit-net-transport-seam.md | 19 +++++++++++++++++++ knope.toml | 9 +++++++++ 2 files changed, 28 insertions(+) create mode 100644 .changeset/livekit-net-transport-seam.md diff --git a/.changeset/livekit-net-transport-seam.md b/.changeset/livekit-net-transport-seam.md new file mode 100644 index 000000000..64bbc9c47 --- /dev/null +++ b/.changeset/livekit-net-transport-seam.md @@ -0,0 +1,19 @@ +--- +livekit-api: major +livekit: major +livekit-uniffi: minor +--- + +Route LiveKit signalling through a pluggable, host-providable transport (new `livekit-net` crate). + +The signalling WebSocket and the two pre-connect HTTP GETs (validate, region discovery) now go through a transport trait resolved from a process-global registry, so mobile/UniFFI builds can drop the Rust TLS/WebSocket/HTTP stack and use the platform's network stack. Native desktop builds are unchanged in behavior. + +**Breaking (`livekit-api`, and `livekit` via `EngineError::Signal`):** + +- `SignalError::WsError` is removed — `tungstenite` is no longer part of the public API. A failed WebSocket handshake now surfaces its HTTP status as `SignalError::Client`/`Server`; transport connection and close failures surface as the new `SignalError::Connection(String)` / `SignalError::Closed` variants (previously all collapsed into `Timeout`). +- `SignalError` is now `#[non_exhaustive]`. +- The signalling WebSocket/HTTP/TLS crates are no longer transitive dependencies of `livekit-api`; TLS features delegate to `livekit-net`. Existing `signal-client-tokio` / `-async` / `-dispatcher` and TLS feature names are unchanged. + +`livekit` adds a `foreign` feature that pairs the backend-agnostic signalling client with a host-provided transport. + +`livekit-uniffi` gains `setPlatformTransport` so a Dart/Swift/Kotlin host can inject its own transport across the UniFFI boundary, and drops the services (reqwest) stack from the cdylib. diff --git a/knope.toml b/knope.toml index 75a9418a2..03885be84 100644 --- a/knope.toml +++ b/knope.toml @@ -76,6 +76,15 @@ versioned_files = [ changelog = "livekit-api/CHANGELOG.md" scopes = ["livekit-api"] +[packages.livekit-net] +versioned_files = [ + "livekit-net/Cargo.toml", + "Cargo.lock", + { path = "Cargo.toml", dependency = "livekit-net" }, +] +changelog = "livekit-net/CHANGELOG.md" +scopes = ["livekit-net"] + [packages.libwebrtc] versioned_files = [ "libwebrtc/Cargo.toml",