From 78e8aad6a8f983c664e1c68439981350e3521b0d Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 17:24:23 -0800 Subject: [PATCH 01/39] I have no idea why this doesn't work. --- .vscode/settings.json | 6 + webrtc/webrtc-rs-server/Cargo.lock | 2631 +++++++++++++++++++++++++++ webrtc/webrtc-rs-server/Cargo.toml | 15 + webrtc/webrtc-rs-server/src/main.rs | 191 ++ 4 files changed, 2843 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 webrtc/webrtc-rs-server/Cargo.lock create mode 100644 webrtc/webrtc-rs-server/Cargo.toml create mode 100644 webrtc/webrtc-rs-server/src/main.rs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b091363 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.linkedProjects": [ + "webrtc\\webrtc-rs-server\\Cargo.toml", + "websocket\\tungstenite-server\\Cargo.toml" + ] +} \ No newline at end of file diff --git a/webrtc/webrtc-rs-server/Cargo.lock b/webrtc/webrtc-rs-server/Cargo.lock new file mode 100644 index 0000000..1be1ee1 --- /dev/null +++ b/webrtc/webrtc-rs-server/Cargo.lock @@ -0,0 +1,2631 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "ccm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847" +dependencies = [ + "aead", + "cipher", + "ctr", + "subtle", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dtls" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f531dd7c181beaf3cebab3716afa4d0d41ab888be85232583f56bbaf07ca208a" +dependencies = [ + "aes", + "aes-gcm", + "async-trait", + "bincode", + "byteorder", + "cbc", + "ccm", + "chacha20poly1305", + "der-parser", + "hmac", + "log", + "p256", + "p384", + "portable-atomic", + "rand", + "rand_core 0.6.4", + "rcgen", + "ring", + "rustls", + "sec1", + "serde", + "sha1", + "sha2", + "thiserror 1.0.69", + "tokio", + "webrtc-util", + "x25519-dalek", + "x509-parser", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "interceptor" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea51375727680dc15f06e8ad90fa31df75d79dd030100e8ad60eef1c27fe2c98" +dependencies = [ + "async-trait", + "bytes", + "futures", + "log", + "portable-atomic", + "rand", + "rtcp", + "rtp", + "thiserror 1.0.69", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rtcp" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d30d1c4091644431c22acf9f8be6191b56805e0e977f15ca7104b4a6d6eaec" +dependencies = [ + "bytes", + "thiserror 1.0.69", + "webrtc-util", +] + +[[package]] +name = "rtp" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f126f38ea84c02480e32e547c1459a939052f74fb92117ac3eef23fdac6b023" +dependencies = [ + "bytes", + "memchr", + "portable-atomic", + "rand", + "serde", + "thiserror 1.0.69", + "webrtc-util", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c374dceda16965d541c8800ce9cc4e1c14acfd661ddf7952feeedc3411e5c6" +dependencies = [ + "rand", + "substring", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stun" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a512c5d501e3e3b5a4bb3e8e31462d56d54a66b95a28b8596e14422bf21c32b" +dependencies = [ + "base64", + "crc", + "lazy_static", + "md-5", + "rand", + "ring", + "subtle", + "thiserror 1.0.69", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.1", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + +[[package]] +name = "turn" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ed995882f66ab94238de77c62e5e778389698ab700afa4696f4754da8f457cb" +dependencies = [ + "async-trait", + "base64", + "futures", + "log", + "md-5", + "portable-atomic", + "rand", + "ring", + "stun", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webrtc" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08fd686c0920ac08f3a57eacc48e31f0e4ca1ffefba4478784606f78c14e83ad" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "dtls", + "hex", + "interceptor", + "lazy_static", + "log", + "portable-atomic", + "rand", + "rcgen", + "regex", + "ring", + "rtcp", + "rtp", + "sdp", + "serde", + "serde_json", + "sha2", + "smol_str", + "stun", + "thiserror 1.0.69", + "tokio", + "turn", + "unicase", + "url", + "waitgroup", + "webrtc-data", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062a5438d63bb0756a221693d76cc0dd6119affee1dfdfe57abe3a2a8c8b3eea" +dependencies = [ + "bytes", + "log", + "portable-atomic", + "thiserror 1.0.69", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-ice" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cb13fd1a373e68addc4bba0c8ca058627518e54342583d024bdcbb8ae5d97d" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "portable-atomic", + "rand", + "serde", + "serde_json", + "stun", + "thiserror 1.0.69", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17279a067e75df72ce923fdeb7f04cd808f6f5aa4910dc6bcb4fbe66b396ace" +dependencies = [ + "log", + "socket2 0.5.10", + "thiserror 1.0.69", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a84c910fec0848fd5a0d8a5651e0ddbdedaf25a7d3ae3f0b15f71ac73a1773" +dependencies = [ + "byteorder", + "bytes", + "rand", + "rtp", + "thiserror 1.0.69", +] + +[[package]] +name = "webrtc-rs-server" +version = "0.1.0" +dependencies = [ + "anyhow", + "interceptor", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "tokio", + "tungstenite", + "webrtc", +] + +[[package]] +name = "webrtc-sctp" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f985465467d8910c1f8ac4382cd64f83b1f6a1a75021a82b221546f6fb3b856f" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "portable-atomic", + "rand", + "thiserror 1.0.69", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d8cdc33413f1d0192670a80ce93d17cb78d57fe3a2414be30d6f6dff121123" +dependencies = [ + "aead", + "aes", + "aes-gcm", + "byteorder", + "bytes", + "ctr", + "hmac", + "log", + "rtcp", + "rtp", + "sha1", + "subtle", + "thiserror 1.0.69", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c0c7e0c8f280f2bbfae442701465777ac07adaf46ce0c5863cd58e13fe472a" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes", + "ipnet", + "lazy_static", + "log", + "nix", + "portable-atomic", + "rand", + "thiserror 1.0.69", + "tokio", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/webrtc/webrtc-rs-server/Cargo.toml b/webrtc/webrtc-rs-server/Cargo.toml new file mode 100644 index 0000000..2bee66f --- /dev/null +++ b/webrtc/webrtc-rs-server/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "webrtc-rs-server" +version = "0.1.0" +edition = "2024" + +[dependencies] +webrtc = "0.14.0" +interceptor = "0.15.0" +tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "full"] } +tungstenite = "0.28" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +rustls = {version = "0.23", features = ["ring"]} +rustls-pki-types = "1.13.0" +anyhow = "1.0.100" diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs new file mode 100644 index 0000000..c875707 --- /dev/null +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -0,0 +1,191 @@ +use std::net::TcpListener; +use std::path::Path; +use std::sync::{Arc, LazyLock, OnceLock}; +use std::thread::spawn; + +use rustls::crypto::CryptoProvider; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use rustls::{ServerConfig, ServerConnection, StreamOwned}; +use tungstenite::accept; + +use tokio::time::Duration; +use webrtc::api::APIBuilder; +use webrtc::api::interceptor_registry::register_default_interceptors; +use webrtc::api::media_engine::MediaEngine; +use webrtc::data_channel::RTCDataChannel; +use webrtc::data_channel::data_channel_message::DataChannelMessage; +use webrtc::ice_transport::ice_candidate::RTCIceCandidate; +use webrtc::ice_transport::ice_server::RTCIceServer; +use webrtc::interceptor::registry::Registry; +use webrtc::peer_connection::configuration::RTCConfiguration; +use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState; +use webrtc::peer_connection::sdp::session_description::RTCSessionDescription; +use webrtc::peer_connection::{RTCPeerConnection, math_rand_alpha}; + +static PEER_CONNECTION: OnceLock> = OnceLock::new(); + +/// A WebSocket echo server over TLS (wss://) +#[tokio::main] +async fn main() -> anyhow::Result<()> { + CryptoProvider::install_default(rustls::crypto::ring::default_provider()).unwrap(); + // Use fixed relative paths from this crate to the repo certs + let cert_path = Path::new("../../certs/localhost.pem"); + let key_path = Path::new("../../certs/localhost-key.pem"); + + eprintln!("Using certificate: {}", cert_path.display()); + eprintln!("Using private key: {}", key_path.display()); + + let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); + let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + + // Build rustls server config (no client auth) + let config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], key) + .expect("invalid cert/key"); + let config = Arc::new(config); + + let listener = TcpListener::bind("127.0.0.1:8002").expect("bind 127.0.0.1:8002"); + eprintln!("tungstenite server listening on wss://127.0.0.1:8002"); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + eprintln!( + "incoming TCP connection from {}", + stream.peer_addr().unwrap() + ); + let cfg = Arc::clone(&config); + tokio::spawn(async move { + // Wrap TCP in a rustls TLS stream. + let conn = match ServerConnection::new(cfg) { + Ok(c) => c, + Err(e) => { + eprintln!("TLS ServerConnection error: {e}"); + return; + } + }; + let tls_stream = StreamOwned::new(conn, stream); + + match accept(tls_stream) { + Ok(mut websocket) => { + // Create a MediaEngine object to configure the supported codec + let mut m = MediaEngine::default(); + + // Register default codecs + m.register_default_codecs().unwrap(); + + // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. + // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` + // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry + // for each PeerConnection. + let mut registry = Registry::new(); + + // Use the default set of Interceptors + registry = register_default_interceptors(registry, &mut m).unwrap(); + + // Create the API object with the MediaEngine + let api = APIBuilder::new() + .with_media_engine(m) + .with_interceptor_registry(registry) + .build(); + + // Prepare the configuration + let config = RTCConfiguration { + ice_servers: vec![RTCIceServer { + urls: vec!["stun:stun.l.google.com:19302".to_owned()], + ..Default::default() + }], + ..Default::default() + }; + + // Create a new RTCPeerConnection + let peer_connection = + Arc::new(api.new_peer_connection(config).await.unwrap()); + + // Set the handler for Peer connection state + // This will notify you when the peer has connected/disconnected + peer_connection.on_peer_connection_state_change(Box::new( + move |s: RTCPeerConnectionState| { + println!("Peer Connection State has changed: {s}"); + + if s == RTCPeerConnectionState::Failed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. + // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. + println!("Peer Connection has gone to failed exiting"); + } + + Box::pin(async {}) + }, + )); + + peer_connection.on_ice_candidate(Box::new( + move |candidate: Option| { + Box::pin(async move { + if let Some(c) = candidate { + println!( + "New ICE candidate: {}", + c.to_json().unwrap().candidate + ); + } else { + println!("ICE gathering complete"); + } + }) + }, + )); + + peer_connection.on_data_channel(Box::new( + move |data_channel: Arc| { + println!( + "New DataChannel {}-{}", + data_channel.label(), + data_channel.id() + ); + Box::pin(async move {}) + }, + )); + + let msg = websocket.read_message().expect("read message"); + let sdp_bytes = msg.into_text().expect("msg to text"); + let offer: RTCSessionDescription = + serde_json::from_str(&sdp_bytes).expect("unmarshal SDP"); + println!("Received offer: {offer:?}"); + + // Apply the offer as the remote description + peer_connection.set_remote_description(offer).await.unwrap(); + + // Create an answer to send to the browser + let answer = peer_connection.create_answer(None).await.unwrap(); + + // Sets the LocalDescription, and starts our UDP listeners + peer_connection.set_local_description(answer).await.unwrap(); + + // Create channel that is blocked until ICE Gathering is complete + let mut gather_complete = + peer_connection.gathering_complete_promise().await; + + // Block until ICE Gathering is complete, disabling trickle ICE + // we do this because we only can exchange one signaling message + // in a production application you should exchange ICE Candidates via OnICECandidate + let _ = gather_complete.recv().await; + + println!("Connection established, waiting for messages..."); + + PEER_CONNECTION.set(peer_connection).unwrap(); + } + Err(e) => { + eprintln!("websocket handshake failed: {e}"); + eprintln!( + "Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/." + ); + } + } + }); + } + Err(e) => return Err(anyhow::anyhow!("TCP accept error: {e}")), + } + } + Ok(()) +} From 792b0d4a47e1c3457e926b6fd7543de3619f425d Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 18:20:56 -0800 Subject: [PATCH 02/39] Move to axum webserver --- webrtc/webrtc-rs-server/Cargo.lock | 496 +++++++++++++++++++++++++++- webrtc/webrtc-rs-server/Cargo.toml | 9 +- webrtc/webrtc-rs-server/src/main.rs | 411 +++++++++++++---------- 3 files changed, 739 insertions(+), 177 deletions(-) diff --git a/webrtc/webrtc-rs-server/Cargo.lock b/webrtc/webrtc-rs-server/Cargo.lock index 1be1ee1..ffd8a72 100644 --- a/webrtc/webrtc-rs-server/Cargo.lock +++ b/webrtc/webrtc-rs-server/Cargo.lock @@ -143,6 +143,105 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "axum" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +dependencies = [ + "axum-core", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5136e6c5e7e7978fe23e9876fb924af2c0f84c72127ac6ac17e7c46f457d362c" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-core", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-server" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" +dependencies = [ + "arc-swap", + "bytes", + "fs-err", + "http", + "http-body", + "hyper", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -575,6 +674,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "ff" version = "0.13.1" @@ -612,6 +717,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad492b2cf1d89d568a43508ab24f98501fe03f2f31c01e1d0fe7366a71745d2" +dependencies = [ + "autocfg", + "tokio", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -768,6 +883,55 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + [[package]] name = "hex" version = "0.4.3" @@ -803,12 +967,85 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -911,6 +1148,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inout" version = "0.1.4" @@ -1026,6 +1273,21 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -1051,6 +1313,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1091,6 +1369,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1516,6 +1803,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.13.0" @@ -1630,6 +1926,29 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1652,6 +1971,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1779,6 +2107,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.2" @@ -1830,6 +2164,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "time" version = "0.3.44" @@ -1899,6 +2242,28 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -1912,6 +2277,122 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "tungstenite" version = "0.28.0" @@ -2019,6 +2500,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -2207,13 +2694,20 @@ name = "webrtc-rs-server" version = "0.1.0" dependencies = [ "anyhow", + "axum", + "axum-extra", + "axum-server", + "futures-util", "interceptor", "rustls", "rustls-pki-types", "serde", "serde_json", "tokio", - "tungstenite", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", "webrtc", ] diff --git a/webrtc/webrtc-rs-server/Cargo.toml b/webrtc/webrtc-rs-server/Cargo.toml index 2bee66f..5a58e42 100644 --- a/webrtc/webrtc-rs-server/Cargo.toml +++ b/webrtc/webrtc-rs-server/Cargo.toml @@ -7,9 +7,16 @@ edition = "2024" webrtc = "0.14.0" interceptor = "0.15.0" tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "full"] } -tungstenite = "0.28" serde = { version = "1", features = ["derive"] } serde_json = "1" rustls = {version = "0.23", features = ["ring"]} rustls-pki-types = "1.13.0" anyhow = "1.0.100" +axum = { version = "0.8.6", features = ["ws"] } +tower = "0.5.2" +tower-http = { version = "0.6.6", features = ["fs", "trace"] } +axum-extra = { version = "0.12.1", features = ["typed-header"] } +tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } +futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } +tracing = "0.1.41" +axum-server = { version = "0.7.2", features = ["tls-rustls"] } diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs index c875707..249c2a4 100644 --- a/webrtc/webrtc-rs-server/src/main.rs +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -1,191 +1,252 @@ -use std::net::TcpListener; -use std::path::Path; -use std::sync::{Arc, LazyLock, OnceLock}; -use std::thread::spawn; - +//! Example websocket server. +//! +//! Run the server with +//! ```not_rust +//! cargo run -p example-websockets --bin example-websockets +//! ``` +//! +//! Run a browser client with +//! ```not_rust +//! firefox http://localhost:3000 +//! ``` +//! +//! Alternatively you can run the rust client (showing two +//! concurrent websocket connections being established) with +//! ```not_rust +//! cargo run -p example-websockets --bin example-client +//! ``` + +use axum::{ + Router, + body::Bytes, + extract::ws::{Message, Utf8Bytes, WebSocket, WebSocketUpgrade}, + response::IntoResponse, + routing::any, +}; +use axum_extra::TypedHeader; use rustls::crypto::CryptoProvider; -use rustls::pki_types::pem::PemObject; -use rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use rustls::{ServerConfig, ServerConnection, StreamOwned}; -use tungstenite::accept; - -use tokio::time::Duration; -use webrtc::api::APIBuilder; -use webrtc::api::interceptor_registry::register_default_interceptors; -use webrtc::api::media_engine::MediaEngine; -use webrtc::data_channel::RTCDataChannel; -use webrtc::data_channel::data_channel_message::DataChannelMessage; -use webrtc::ice_transport::ice_candidate::RTCIceCandidate; -use webrtc::ice_transport::ice_server::RTCIceServer; -use webrtc::interceptor::registry::Registry; -use webrtc::peer_connection::configuration::RTCConfiguration; -use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState; -use webrtc::peer_connection::sdp::session_description::RTCSessionDescription; -use webrtc::peer_connection::{RTCPeerConnection, math_rand_alpha}; - -static PEER_CONNECTION: OnceLock> = OnceLock::new(); - -/// A WebSocket echo server over TLS (wss://) + +use std::{net::SocketAddr, path::PathBuf}; +use std::{ops::ControlFlow, path::Path}; +use tower_http::{ + services::ServeDir, + trace::{DefaultMakeSpan, TraceLayer}, +}; + +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +//allows to extract the IP of connecting user +use axum::extract::connect_info::ConnectInfo; +use axum::extract::ws::CloseFrame; +use axum_extra::headers; + +//allows to split the websocket stream into separate TX and RX branches +use futures_util::{sink::SinkExt, stream::StreamExt}; + #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { + format!("{}=debug,tower_http=debug", env!("CARGO_CRATE_NAME")).into() + }), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + CryptoProvider::install_default(rustls::crypto::ring::default_provider()).unwrap(); + + let assets_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("assets"); + // Use fixed relative paths from this crate to the repo certs let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); - eprintln!("Using certificate: {}", cert_path.display()); - eprintln!("Using private key: {}", key_path.display()); + // build our application with some routes + let app = Router::new() + .fallback_service(ServeDir::new(assets_dir).append_index_html_on_directories(true)) + .route("/", any(ws_handler)) + // logging so we can see what's going on + .layer( + TraceLayer::new_for_http() + .make_span_with(DefaultMakeSpan::default().include_headers(true)), + ); + // run it with hyper + let bind_addr = SocketAddr::from(([127, 0, 0, 1], 8002)); + tracing::debug!("listening on {}", bind_addr); + let tls_config = axum_server::tls_rustls::RustlsConfig::from_pem_file(cert_path, key_path) + .await + .unwrap(); + let server = axum_server::bind_rustls(bind_addr, tls_config); + server.serve(app.into_make_service()).await.unwrap(); +} + +/// The handler for the HTTP request (this gets called when the HTTP request lands at the start +/// of websocket negotiation). After this completes, the actual switching from HTTP to +/// websocket protocol will occur. +/// This is the last point where we can extract TCP/IP metadata such as IP address of the client +/// as well as things from HTTP headers such as user-agent of the browser etc. +async fn ws_handler( + ws: WebSocketUpgrade, + user_agent: Option>, + ConnectInfo(addr): ConnectInfo, +) -> impl IntoResponse { + let user_agent = if let Some(TypedHeader(user_agent)) = user_agent { + user_agent.to_string() + } else { + String::from("Unknown browser") + }; + println!("`{user_agent}` at {addr} connected."); + // finalize the upgrade process by returning upgrade callback. + // we can customize the callback by sending additional info such as address. + ws.on_upgrade(move |socket| handle_socket(socket, addr)) +} + +/// Actual websocket statemachine (one will be spawned per connection) +async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { + // send a ping (unsupported by some browsers) just to kick things off and get a response + if socket + .send(Message::Ping(Bytes::from_static(&[1, 2, 3]))) + .await + .is_ok() + { + println!("Pinged {who}..."); + } else { + println!("Could not send ping {who}!"); + // no Error here since the only thing we can do is to close the connection. + // If we can not send messages, there is no way to salvage the statemachine anyway. + return; + } + + // receive single message from a client (we can either receive or send with socket). + // this will likely be the Pong for our Ping or a hello message from client. + // waiting for message from a client will block this task, but will not block other client's + // connections. + if let Some(msg) = socket.recv().await { + if let Ok(msg) = msg { + if process_message(msg, who).is_break() { + return; + } + } else { + println!("client {who} abruptly disconnected"); + return; + } + } + + // Since each client gets individual statemachine, we can pause handling + // when necessary to wait for some external event (in this case illustrated by sleeping). + // Waiting for this client to finish getting its greetings does not prevent other clients from + // connecting to server and receiving their greetings. + for i in 1..5 { + if socket + .send(Message::Text(format!("Hi {i} times!").into())) + .await + .is_err() + { + println!("client {who} abruptly disconnected"); + return; + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } - let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); - let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + // By splitting socket we can send and receive at the same time. In this example we will send + // unsolicited messages to client based on some sort of server's internal event (i.e .timer). + let (mut sender, mut receiver) = socket.split(); + + // Spawn a task that will push several messages to the client (does not matter what client does) + let mut send_task = tokio::spawn(async move { + let n_msg = 20; + for i in 0..n_msg { + // In case of any websocket error, we exit. + if sender + .send(Message::Text(format!("Server message {i} ...").into())) + .await + .is_err() + { + return i; + } + + tokio::time::sleep(std::time::Duration::from_millis(300)).await; + } - // Build rustls server config (no client auth) - let config = ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(vec![cert], key) - .expect("invalid cert/key"); - let config = Arc::new(config); + println!("Sending close to {who}..."); + if let Err(e) = sender + .send(Message::Close(Some(CloseFrame { + code: axum::extract::ws::close_code::NORMAL, + reason: Utf8Bytes::from_static("Goodbye"), + }))) + .await + { + println!("Could not send Close due to {e}, probably it is ok?"); + } + n_msg + }); + + // This second task will receive messages from client and print them on server console + let mut recv_task = tokio::spawn(async move { + let mut cnt = 0; + while let Some(Ok(msg)) = receiver.next().await { + cnt += 1; + // print message and break if instructed to do so + if process_message(msg, who).is_break() { + break; + } + } + cnt + }); + + // If any one of the tasks exit, abort the other. + tokio::select! { + rv_a = (&mut send_task) => { + match rv_a { + Ok(a) => println!("{a} messages sent to {who}"), + Err(a) => println!("Error sending messages {a:?}") + } + recv_task.abort(); + }, + rv_b = (&mut recv_task) => { + match rv_b { + Ok(b) => println!("Received {b} messages"), + Err(b) => println!("Error receiving messages {b:?}") + } + send_task.abort(); + } + } - let listener = TcpListener::bind("127.0.0.1:8002").expect("bind 127.0.0.1:8002"); - eprintln!("tungstenite server listening on wss://127.0.0.1:8002"); + // returning from the handler closes the websocket connection + println!("Websocket context {who} destroyed"); +} - for stream in listener.incoming() { - match stream { - Ok(stream) => { - eprintln!( - "incoming TCP connection from {}", - stream.peer_addr().unwrap() +/// helper to print contents of messages to stdout. Has special treatment for Close. +fn process_message(msg: Message, who: SocketAddr) -> ControlFlow<(), ()> { + match msg { + Message::Text(t) => { + println!(">>> {who} sent str: {t:?}"); + } + Message::Binary(d) => { + println!(">>> {who} sent {} bytes: {d:?}", d.len()); + } + Message::Close(c) => { + if let Some(cf) = c { + println!( + ">>> {who} sent close with code {} and reason `{}`", + cf.code, cf.reason ); - let cfg = Arc::clone(&config); - tokio::spawn(async move { - // Wrap TCP in a rustls TLS stream. - let conn = match ServerConnection::new(cfg) { - Ok(c) => c, - Err(e) => { - eprintln!("TLS ServerConnection error: {e}"); - return; - } - }; - let tls_stream = StreamOwned::new(conn, stream); - - match accept(tls_stream) { - Ok(mut websocket) => { - // Create a MediaEngine object to configure the supported codec - let mut m = MediaEngine::default(); - - // Register default codecs - m.register_default_codecs().unwrap(); - - // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. - // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` - // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry - // for each PeerConnection. - let mut registry = Registry::new(); - - // Use the default set of Interceptors - registry = register_default_interceptors(registry, &mut m).unwrap(); - - // Create the API object with the MediaEngine - let api = APIBuilder::new() - .with_media_engine(m) - .with_interceptor_registry(registry) - .build(); - - // Prepare the configuration - let config = RTCConfiguration { - ice_servers: vec![RTCIceServer { - urls: vec!["stun:stun.l.google.com:19302".to_owned()], - ..Default::default() - }], - ..Default::default() - }; - - // Create a new RTCPeerConnection - let peer_connection = - Arc::new(api.new_peer_connection(config).await.unwrap()); - - // Set the handler for Peer connection state - // This will notify you when the peer has connected/disconnected - peer_connection.on_peer_connection_state_change(Box::new( - move |s: RTCPeerConnectionState| { - println!("Peer Connection State has changed: {s}"); - - if s == RTCPeerConnectionState::Failed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. - // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. - // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. - println!("Peer Connection has gone to failed exiting"); - } - - Box::pin(async {}) - }, - )); - - peer_connection.on_ice_candidate(Box::new( - move |candidate: Option| { - Box::pin(async move { - if let Some(c) = candidate { - println!( - "New ICE candidate: {}", - c.to_json().unwrap().candidate - ); - } else { - println!("ICE gathering complete"); - } - }) - }, - )); - - peer_connection.on_data_channel(Box::new( - move |data_channel: Arc| { - println!( - "New DataChannel {}-{}", - data_channel.label(), - data_channel.id() - ); - Box::pin(async move {}) - }, - )); - - let msg = websocket.read_message().expect("read message"); - let sdp_bytes = msg.into_text().expect("msg to text"); - let offer: RTCSessionDescription = - serde_json::from_str(&sdp_bytes).expect("unmarshal SDP"); - println!("Received offer: {offer:?}"); - - // Apply the offer as the remote description - peer_connection.set_remote_description(offer).await.unwrap(); - - // Create an answer to send to the browser - let answer = peer_connection.create_answer(None).await.unwrap(); - - // Sets the LocalDescription, and starts our UDP listeners - peer_connection.set_local_description(answer).await.unwrap(); - - // Create channel that is blocked until ICE Gathering is complete - let mut gather_complete = - peer_connection.gathering_complete_promise().await; - - // Block until ICE Gathering is complete, disabling trickle ICE - // we do this because we only can exchange one signaling message - // in a production application you should exchange ICE Candidates via OnICECandidate - let _ = gather_complete.recv().await; - - println!("Connection established, waiting for messages..."); - - PEER_CONNECTION.set(peer_connection).unwrap(); - } - Err(e) => { - eprintln!("websocket handshake failed: {e}"); - eprintln!( - "Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/." - ); - } - } - }); + } else { + println!(">>> {who} somehow sent close message without CloseFrame"); } - Err(e) => return Err(anyhow::anyhow!("TCP accept error: {e}")), + return ControlFlow::Break(()); + } + + Message::Pong(v) => { + println!(">>> {who} sent pong with {v:?}"); + } + // You should never need to manually handle Message::Ping, as axum's websocket library + // will do so for you automagically by replying with Pong and copying the v according to + // spec. But if you need the contents of the pings you can see them here. + Message::Ping(v) => { + println!(">>> {who} sent ping with {v:?}"); } } - Ok(()) + ControlFlow::Continue(()) } From e5c5a82d95c45b6ee7de222bc4002b4fbc655625 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 19:53:00 -0800 Subject: [PATCH 03/39] Well, we have a connection at least. --- webrtc/webrtc-rs-server/src/main.rs | 258 +++++++++++++++------------- 1 file changed, 134 insertions(+), 124 deletions(-) diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs index 249c2a4..d189664 100644 --- a/webrtc/webrtc-rs-server/src/main.rs +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -24,9 +24,28 @@ use axum::{ routing::any, }; use axum_extra::TypedHeader; +use rustls::ServerConfig as RustlsServerConfig; use rustls::crypto::CryptoProvider; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use webrtc::{ + api::{ + APIBuilder, interceptor_registry::register_default_interceptors, media_engine::MediaEngine, + }, + data_channel::RTCDataChannel, + ice_transport::{ice_candidate::RTCIceCandidate, ice_server::RTCIceServer}, + peer_connection::{ + RTCPeerConnection, configuration::RTCConfiguration, + peer_connection_state::RTCPeerConnectionState, + sdp::session_description::RTCSessionDescription, + }, +}; -use std::{net::SocketAddr, path::PathBuf}; +use std::{ + net::SocketAddr, + path::PathBuf, + sync::{Arc, OnceLock}, +}; use std::{ops::ControlFlow, path::Path}; use tower_http::{ services::ServeDir, @@ -43,6 +62,8 @@ use axum_extra::headers; //allows to split the websocket stream into separate TX and RX branches use futures_util::{sink::SinkExt, stream::StreamExt}; +static PEER_CONNECTION: OnceLock> = OnceLock::new(); + #[tokio::main] async fn main() { tracing_subscriber::registry() @@ -53,7 +74,7 @@ async fn main() { ) .with(tracing_subscriber::fmt::layer()) .init(); - + CryptoProvider::install_default(rustls::crypto::ring::default_provider()).unwrap(); let assets_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("assets"); @@ -74,11 +95,22 @@ async fn main() { // run it with hyper let bind_addr = SocketAddr::from(([127, 0, 0, 1], 8002)); tracing::debug!("listening on {}", bind_addr); - let tls_config = axum_server::tls_rustls::RustlsConfig::from_pem_file(cert_path, key_path) + // Build a Rustls ServerConfig and force ALPN to http/1.1 so WebSocket upgrade works over TLS + let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); + let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + let mut tls_config = RustlsServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], key) + .expect("invalid cert/key"); + tls_config.alpn_protocols = vec![b"http/1.1".to_vec()]; + let tls_config = + axum_server::tls_rustls::RustlsConfig::from_config(std::sync::Arc::new(tls_config)); + let server = axum_server::bind_rustls(bind_addr, tls_config); + // Provide peer SocketAddr to handlers so ConnectInfo extractor works (prevents 500s on WS upgrade) + server + .serve(app.into_make_service_with_connect_info::()) .await .unwrap(); - let server = axum_server::bind_rustls(bind_addr, tls_config); - server.serve(app.into_make_service()).await.unwrap(); } /// The handler for the HTTP request (this gets called when the HTTP request lands at the start @@ -118,135 +150,113 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { return; } - // receive single message from a client (we can either receive or send with socket). - // this will likely be the Pong for our Ping or a hello message from client. - // waiting for message from a client will block this task, but will not block other client's - // connections. - if let Some(msg) = socket.recv().await { - if let Ok(msg) = msg { - if process_message(msg, who).is_break() { - return; - } - } else { - println!("client {who} abruptly disconnected"); - return; - } - } + // Create a MediaEngine object to configure the supported codec + let mut m = MediaEngine::default(); - // Since each client gets individual statemachine, we can pause handling - // when necessary to wait for some external event (in this case illustrated by sleeping). - // Waiting for this client to finish getting its greetings does not prevent other clients from - // connecting to server and receiving their greetings. - for i in 1..5 { - if socket - .send(Message::Text(format!("Hi {i} times!").into())) - .await - .is_err() - { - println!("client {who} abruptly disconnected"); - return; - } - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - } + // Register default codecs + m.register_default_codecs().unwrap(); - // By splitting socket we can send and receive at the same time. In this example we will send - // unsolicited messages to client based on some sort of server's internal event (i.e .timer). - let (mut sender, mut receiver) = socket.split(); - - // Spawn a task that will push several messages to the client (does not matter what client does) - let mut send_task = tokio::spawn(async move { - let n_msg = 20; - for i in 0..n_msg { - // In case of any websocket error, we exit. - if sender - .send(Message::Text(format!("Server message {i} ...").into())) - .await - .is_err() - { - return i; - } + // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. + // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` + // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry + // for each PeerConnection. + let mut registry = interceptor::registry::Registry::new(); - tokio::time::sleep(std::time::Duration::from_millis(300)).await; - } + // Use the default set of Interceptors + registry = register_default_interceptors(registry, &mut m).unwrap(); - println!("Sending close to {who}..."); - if let Err(e) = sender - .send(Message::Close(Some(CloseFrame { - code: axum::extract::ws::close_code::NORMAL, - reason: Utf8Bytes::from_static("Goodbye"), - }))) - .await - { - println!("Could not send Close due to {e}, probably it is ok?"); - } - n_msg - }); - - // This second task will receive messages from client and print them on server console - let mut recv_task = tokio::spawn(async move { - let mut cnt = 0; - while let Some(Ok(msg)) = receiver.next().await { - cnt += 1; - // print message and break if instructed to do so - if process_message(msg, who).is_break() { - break; - } - } - cnt - }); - - // If any one of the tasks exit, abort the other. - tokio::select! { - rv_a = (&mut send_task) => { - match rv_a { - Ok(a) => println!("{a} messages sent to {who}"), - Err(a) => println!("Error sending messages {a:?}") - } - recv_task.abort(); - }, - rv_b = (&mut recv_task) => { - match rv_b { - Ok(b) => println!("Received {b} messages"), - Err(b) => println!("Error receiving messages {b:?}") - } - send_task.abort(); - } - } + // Create the API object with the MediaEngine + let api = APIBuilder::new() + .with_media_engine(m) + .with_interceptor_registry(registry) + .build(); - // returning from the handler closes the websocket connection - println!("Websocket context {who} destroyed"); -} + // Prepare the configuration + let config = RTCConfiguration { + ice_servers: vec![RTCIceServer { + urls: vec!["stun:stun.l.google.com:19302".to_owned()], + ..Default::default() + }], + ..Default::default() + }; -/// helper to print contents of messages to stdout. Has special treatment for Close. -fn process_message(msg: Message, who: SocketAddr) -> ControlFlow<(), ()> { - match msg { - Message::Text(t) => { - println!(">>> {who} sent str: {t:?}"); - } - Message::Binary(d) => { - println!(">>> {who} sent {} bytes: {d:?}", d.len()); + // Create a new RTCPeerConnection + let peer_connection = Arc::new(api.new_peer_connection(config).await.unwrap()); + + // Set the handler for Peer connection state + // This will notify you when the peer has connected/disconnected + peer_connection.on_peer_connection_state_change(Box::new(move |s: RTCPeerConnectionState| { + println!("Peer Connection State has changed: {s}"); + + if s == RTCPeerConnectionState::Failed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. + // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. + println!("Peer Connection has gone to failed exiting"); } - Message::Close(c) => { - if let Some(cf) = c { - println!( - ">>> {who} sent close with code {} and reason `{}`", - cf.code, cf.reason - ); + + Box::pin(async {}) + })); + + peer_connection.on_ice_candidate(Box::new(move |candidate: Option| { + Box::pin(async move { + if let Some(c) = candidate { + println!("New ICE candidate: {}", c.to_json().unwrap().candidate); } else { - println!(">>> {who} somehow sent close message without CloseFrame"); + println!("ICE gathering complete"); } - return ControlFlow::Break(()); - } + }) + })); - Message::Pong(v) => { - println!(">>> {who} sent pong with {v:?}"); - } - // You should never need to manually handle Message::Ping, as axum's websocket library - // will do so for you automagically by replying with Pong and copying the v according to - // spec. But if you need the contents of the pings you can see them here. - Message::Ping(v) => { - println!(">>> {who} sent ping with {v:?}"); + peer_connection.on_data_channel(Box::new(move |data_channel: Arc| { + println!( + "New DataChannel {}-{}", + data_channel.label(), + data_channel.id() + ); + Box::pin(async move {}) + })); + + while let Some(msg) = socket.recv().await { + if let Ok(msg) = msg { + let sdp_bytes = msg.into_text().expect("msg to text"); + println!("Received SDP offer from client {sdp_bytes}"); + if let Some(offer) = serde_json::from_str::(&sdp_bytes).ok() { + println!("Received offer: {offer:?}"); + + let pc = peer_connection.clone(); + + // Apply the offer as the remote description + pc.set_remote_description(offer).await.unwrap(); + + // Create an answer to send to the browser + let answer = pc.create_answer(None).await.unwrap(); + + // Sets the LocalDescription, and starts our UDP listeners + pc.set_local_description(answer).await.unwrap(); + + // Create channel that is blocked until ICE Gathering is complete + let mut gather_complete = pc.gathering_complete_promise().await; + + // Block until ICE Gathering is complete, disabling trickle ICE + // we do this because we only can exchange one signaling message + // in a production application you should exchange ICE Candidates via OnICECandidate + let _ = gather_complete.recv().await; + + println!("Connection established, waiting for messages..."); + + PEER_CONNECTION.set(pc).unwrap(); + return; + } else { + println!("Could not parse SDP from client {who}"); + continue; + } + } else { + println!("client {who} abruptly disconnected"); + return; } } - ControlFlow::Continue(()) + + // returning from the handler closes the websocket connection + println!("Websocket context {who} destroyed"); } From fe6fa779361f1dc91e19d70bd7e9c7e70db09233 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 21:29:55 -0800 Subject: [PATCH 04/39] Stopping here, will wait for review. --- webrtc/webrtc-rs-server/Cargo.lock | 4 ++ webrtc/webrtc-rs-server/Cargo.toml | 1 + webrtc/webrtc-rs-server/src/main.rs | 84 ++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/webrtc/webrtc-rs-server/Cargo.lock b/webrtc/webrtc-rs-server/Cargo.lock index ffd8a72..07c08dd 100644 --- a/webrtc/webrtc-rs-server/Cargo.lock +++ b/webrtc/webrtc-rs-server/Cargo.lock @@ -336,6 +336,9 @@ name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] [[package]] name = "cbc" @@ -2697,6 +2700,7 @@ dependencies = [ "axum", "axum-extra", "axum-server", + "bytes", "futures-util", "interceptor", "rustls", diff --git a/webrtc/webrtc-rs-server/Cargo.toml b/webrtc/webrtc-rs-server/Cargo.toml index 5a58e42..603feb1 100644 --- a/webrtc/webrtc-rs-server/Cargo.toml +++ b/webrtc/webrtc-rs-server/Cargo.toml @@ -20,3 +20,4 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } tracing = "0.1.41" axum-server = { version = "0.7.2", features = ["tls-rustls"] } +bytes = { version = "1.10.1", features = ["serde"] } diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs index d189664..4c49ae1 100644 --- a/webrtc/webrtc-rs-server/src/main.rs +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -15,6 +15,23 @@ //! ```not_rust //! cargo run -p example-websockets --bin example-client //! ``` +//! Example websocket server. +//! +//! Run the server with +//! ```not_rust +//! cargo run -p example-websockets --bin example-websockets +//! ``` +//! +//! Run a browser client with +//! ```not_rust +//! firefox http://localhost:3000 +//! ``` +//! +//! Alternatively you can run the rust client (showing two +//! concurrent websocket connections being established) with +//! ```not_rust +//! cargo run -p example-websockets --bin example-client +//! ``` use axum::{ Router, @@ -31,6 +48,7 @@ use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use webrtc::{ api::{ APIBuilder, interceptor_registry::register_default_interceptors, media_engine::MediaEngine, + setting_engine::SettingEngine, }, data_channel::RTCDataChannel, ice_transport::{ice_candidate::RTCIceCandidate, ice_server::RTCIceServer}, @@ -156,11 +174,18 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { // Register default codecs m.register_default_codecs().unwrap(); + // Create a SettingEngine to configure network settings + let mut s = SettingEngine::default(); + + // Include loopback candidates so the browser can connect via 127.0.0.1 when running locally + // This mirrors typical local testing behavior and avoids hairpin NAT issues on the same host + s.set_include_loopback_candidate(true); + // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - let mut registry = interceptor::registry::Registry::new(); + let mut registry = webrtc::interceptor::registry::Registry::new(); // Use the default set of Interceptors registry = register_default_interceptors(registry, &mut m).unwrap(); @@ -169,9 +194,12 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { let api = APIBuilder::new() .with_media_engine(m) .with_interceptor_registry(registry) + .with_setting_engine(s) .build(); // Prepare the configuration + // When using ICE-Lite, we must not specify ICE servers (STUN/TURN) + // The client will perform all connectivity checks to our host candidates let config = RTCConfiguration { ice_servers: vec![RTCIceServer { urls: vec!["stun:stun.l.google.com:19302".to_owned()], @@ -208,12 +236,38 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { }) })); + // Log ICE connection state transitions for deeper diagnostics + peer_connection.on_ice_connection_state_change(Box::new(move |s| { + println!("ICE Connection State changed: {:?}", s); + Box::pin(async {}) + })); + + // Add signaling state change handler + peer_connection.on_signaling_state_change(Box::new(move |s| { + println!("Signaling State changed: {:?}", s); + Box::pin(async {}) + })); + peer_connection.on_data_channel(Box::new(move |data_channel: Arc| { - println!( - "New DataChannel {}-{}", - data_channel.label(), - data_channel.id() - ); + let d1 = Arc::clone(&data_channel); + let d2 = Arc::clone(&data_channel); + println!("Data channel '{}'-'{}' received from peer", data_channel.label(), data_channel.id()); + d1.on_open(Box::new(move || { + println!("Data channel '{}'-'{}' open. Random messages will now be sent to any connected DataChannels every 5 seconds", data_channel.label(), data_channel.id()); + Box::pin(async move { + let mut interval = tokio::time::interval(std::time::Duration::from_secs(5)); + loop { + interval.tick().await; + let msg = String::from("Hello from webrtc-rs server!"); + if d2.send_text(msg).await.is_ok() { + println!("Sent message to DataChannel '{}'-'{}'", d2.label(), d2.id()); + } else { + println!("Failed to send message to DataChannel '{}'-'{}'", d2.label(), d2.id()); + break; + } + } + }) + })); Box::pin(async move {}) })); @@ -243,9 +297,25 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { // in a production application you should exchange ICE Candidates via OnICECandidate let _ = gather_complete.recv().await; + // Send the SDP answer back to the client before closing the websocket + if let Some(local) = pc.local_description().await { + let answer_json = serde_json::to_string(&local).expect("marshal SDP answer"); + if socket.send(Message::Text(answer_json.into())).await.is_ok() { + println!("Sent SDP answer to client"); + } else { + println!("Failed to send SDP answer to client"); + } + } else { + println!("No local description available to send as answer"); + } + println!("Connection established, waiting for messages..."); - PEER_CONNECTION.set(pc).unwrap(); + PEER_CONNECTION.set(pc.clone()).unwrap(); + + // Close the WebSocket after sending the answer, like the Go version + // The peer connection will stay alive via the global PEER_CONNECTION + drop(socket); return; } else { println!("Could not parse SDP from client {who}"); From da9c33d3cbebdc72b960ccbbf1a5fab23388c890 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 22:38:13 -0800 Subject: [PATCH 05/39] Works, but it's a bit jank --- client/static/index.html | 2 ++ client/static/js/common.js | 6 ++++ client/static/js/sse.js | 44 +++++++++++++++++++++++ server-sent-events/go-sse/go.mod | 5 +++ server-sent-events/go-sse/go.sum | 2 ++ server-sent-events/go-sse/main.go | 60 +++++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+) create mode 100644 client/static/js/sse.js create mode 100644 server-sent-events/go-sse/go.mod create mode 100644 server-sent-events/go-sse/go.sum create mode 100644 server-sent-events/go-sse/main.go diff --git a/client/static/index.html b/client/static/index.html index 5191e47..a78cd5b 100644 --- a/client/static/index.html +++ b/client/static/index.html @@ -20,6 +20,7 @@ + @@ -41,4 +42,5 @@
Stats
+ diff --git a/client/static/js/common.js b/client/static/js/common.js index 10e4937..01929db 100644 --- a/client/static/js/common.js +++ b/client/static/js/common.js @@ -26,6 +26,12 @@ export let chart = new Chart(chartContext, { showLine: true, borderDash: [2, 5], }, + { + data: [], + label: "Server Sent Events", + borderColor: "#000000ff", + showLine: true, + } ] }, options: { diff --git a/client/static/js/sse.js b/client/static/js/sse.js new file mode 100644 index 0000000..f1fbc53 --- /dev/null +++ b/client/static/js/sse.js @@ -0,0 +1,44 @@ +import {chart, initCanvas, visualizePacket} from "./common.js"; + +const sseBtn = document.getElementById("sse"); +const serverUrl = "http://localhost:7999"; + +sseBtn.onclick = (_) => { + initCanvas() + console.info(`Connecting to Server Sent Events server at ${serverUrl} ...`); + + let t0 = new Date(); + let messageCount = 0; + const eventSource = new EventSource(serverUrl); + + eventSource.onopen = (_) => { + console.info(`Connection established in ${new Date() - t0} ms.`); + sseBtn.disabled = true + t0 = new Date(); + chart.data.datasets[3].data.push({x: 0, y: 0}); + } + + eventSource.onmessage = (e) => { + // If event source messages are null, assume connection has been closed + if (!e) { + eventSource.close(); + chart.data.datasets[3].data.push({x: new Date() - t0, y: messageCount}); + chart.update(); + console.info(`${messageCount} message(s) were received within ${new Date() - t0} ms.`) + console.info('Disconnected from Server Sent Events server.'); + return; + } + else { + messageCount += 1; + visualizePacket(e.data); + if (new Date() - t0 - chart.data.datasets[3].data.at(-1).x > 200) { + chart.data.datasets[3].data.push({x: new Date() - t0, y: messageCount}); + chart.update(); + } + } + } + + eventSource.onerror = (_) => { + console.error('Failed to connect to Server Sent Events server'); + } +} diff --git a/server-sent-events/go-sse/go.mod b/server-sent-events/go-sse/go.mod new file mode 100644 index 0000000..6d173e5 --- /dev/null +++ b/server-sent-events/go-sse/go.mod @@ -0,0 +1,5 @@ +module eventsource + +go 1.25.3 + +require github.com/tmaxmax/go-sse v0.11.0 // indirect diff --git a/server-sent-events/go-sse/go.sum b/server-sent-events/go-sse/go.sum new file mode 100644 index 0000000..bbc5a77 --- /dev/null +++ b/server-sent-events/go-sse/go.sum @@ -0,0 +1,2 @@ +github.com/tmaxmax/go-sse v0.11.0 h1:nogmJM6rJUoOLoAwEKeQe5XlVpt9l7N82SS1jI7lWFg= +github.com/tmaxmax/go-sse v0.11.0/go.mod h1:u/2kZQR1tyngo1lKaNCj1mJmhXGZWS1Zs5yiSOD+Eg8= diff --git a/server-sent-events/go-sse/main.go b/server-sent-events/go-sse/main.go new file mode 100644 index 0000000..08f157c --- /dev/null +++ b/server-sent-events/go-sse/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "time" + + "github.com/tmaxmax/go-sse" +) + +func main() { + mux := http.NewServeMux() + sseHandler := &sse.Server{} + + sseHandler.OnSession = func(w http.ResponseWriter, r *http.Request) (topics []string, allowed bool) { + log.Printf("Client Connected\n") + + // Set CORS headers to allow requests from localhost:3000 + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + + // Start a goroutine to send messages after the connection is established + go func() { + // Give the connection a moment to fully establish + time.Sleep(10 * time.Millisecond) + + for i := 10; i < 510; i += 10 { + for j := 10; j < 510; j += 10 { + ev := &sse.Message{} + message := fmt.Sprintf("%03d,%03d", j, i) + ev.AppendData(message) + var err = sseHandler.Publish(ev) + if err != nil { + log.Printf("Error publishing message: %v\n", err) + return + } + time.Sleep(1 * time.Millisecond) + } + } + log.Printf("Finished sending all messages\n") + }() + + // Return empty topics to subscribe to default/all messages + return []string{}, true + } + + server := &http.Server{ + Addr: ":7999", + Handler: mux, + } + + mux.Handle("/", sseHandler) + + //nolint:gosec // Use http.Server in your code instead, to be able to set timeouts. + if err := server.ListenAndServe(); err != nil { + log.Fatalln(err) + } +} From c0ded15a5415802f685970445efd7bf7b680dc00 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 22:40:19 -0800 Subject: [PATCH 06/39] Remove unnecessary sleep --- server-sent-events/go-sse/main.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server-sent-events/go-sse/main.go b/server-sent-events/go-sse/main.go index 08f157c..159d5c9 100644 --- a/server-sent-events/go-sse/main.go +++ b/server-sent-events/go-sse/main.go @@ -21,11 +21,8 @@ func main() { w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - // Start a goroutine to send messages after the connection is established + // Start a goroutine to send messages go func() { - // Give the connection a moment to fully establish - time.Sleep(10 * time.Millisecond) - for i := 10; i < 510; i += 10 { for j := 10; j < 510; j += 10 { ev := &sse.Message{} From 83f9efd319988017fe4c7a6e193ac546e46d4f8c Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 22:46:38 -0800 Subject: [PATCH 07/39] Better error handling from SSE side --- client/static/js/sse.js | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/client/static/js/sse.js b/client/static/js/sse.js index f1fbc53..c057b88 100644 --- a/client/static/js/sse.js +++ b/client/static/js/sse.js @@ -19,26 +19,32 @@ sseBtn.onclick = (_) => { } eventSource.onmessage = (e) => { - // If event source messages are null, assume connection has been closed - if (!e) { - eventSource.close(); + messageCount += 1; + visualizePacket(e.data); + if (new Date() - t0 - chart.data.datasets[3].data.at(-1).x > 200) { chart.data.datasets[3].data.push({x: new Date() - t0, y: messageCount}); chart.update(); - console.info(`${messageCount} message(s) were received within ${new Date() - t0} ms.`) - console.info('Disconnected from Server Sent Events server.'); - return; } - else { - messageCount += 1; - visualizePacket(e.data); - if (new Date() - t0 - chart.data.datasets[3].data.at(-1).x > 200) { - chart.data.datasets[3].data.push({x: new Date() - t0, y: messageCount}); - chart.update(); - } + + // Check if we've received all messages (2500 total: 50x50) + if (messageCount >= 2500) { + eventSource.close(); + console.info(`${messageCount} message(s) were received within ${new Date() - t0} ms.`) + console.info('All messages received. Disconnected from Server Sent Events server.'); + sseBtn.disabled = false; } } - eventSource.onerror = (_) => { - console.error('Failed to connect to Server Sent Events server'); + eventSource.onerror = (err) => { + console.error('SSE connection error:', err); + eventSource.close(); + sseBtn.disabled = false; + + if (messageCount > 0) { + // Connection dropped mid-stream + chart.data.datasets[3].data.push({x: new Date() - t0, y: messageCount}); + chart.update(); + console.info(`Connection interrupted. ${messageCount} message(s) were received within ${new Date() - t0} ms.`) + } } } From 510f7db4a6a59e79911b88885a75f78fb5ecf06a Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 23:00:40 -0800 Subject: [PATCH 08/39] Add axum SSE example --- server-sent-events/axum-sse/Cargo.lock | 730 ++++++++++++++++++++++++ server-sent-events/axum-sse/Cargo.toml | 10 + server-sent-events/axum-sse/src/main.rs | 38 ++ 3 files changed, 778 insertions(+) create mode 100644 server-sent-events/axum-sse/Cargo.lock create mode 100644 server-sent-events/axum-sse/Cargo.toml create mode 100644 server-sent-events/axum-sse/src/main.rs diff --git a/server-sent-events/axum-sse/Cargo.lock b/server-sent-events/axum-sse/Cargo.lock new file mode 100644 index 0000000..01fc454 --- /dev/null +++ b/server-sent-events/axum-sse/Cargo.lock @@ -0,0 +1,730 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "axum" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-sse" +version = "0.1.0" +dependencies = [ + "async-stream", + "axum", + "futures", + "tokio", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" diff --git a/server-sent-events/axum-sse/Cargo.toml b/server-sent-events/axum-sse/Cargo.toml new file mode 100644 index 0000000..d110aa5 --- /dev/null +++ b/server-sent-events/axum-sse/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "axum-sse" +version = "0.1.0" +edition = "2024" + +[dependencies] +async-stream = "0.3.6" +axum = "0.8.6" +futures = "0.3.31" +tokio = { version = "1.48.0", features = ["full"] } diff --git a/server-sent-events/axum-sse/src/main.rs b/server-sent-events/axum-sse/src/main.rs new file mode 100644 index 0000000..2a9de7f --- /dev/null +++ b/server-sent-events/axum-sse/src/main.rs @@ -0,0 +1,38 @@ +use async_stream::stream; +use axum::{ + Router, + extract::State, + response::sse::{Event, Sse}, + routing::get, +}; +use std::convert::Infallible; + +use futures::stream::Stream; +use tokio::sync::broadcast; +use tokio::sync::broadcast::Sender; + +#[tokio::main] +async fn main() { + let (tx, _rx) = broadcast::channel::(100); + let app = Router::new().route("/", get(sse_handler)).with_state(tx); + let listener = tokio::net::TcpListener::bind("0.0.0.0:7999").await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +async fn sse_handler( + State(tx): State>, +) -> Sse>> { + // broadcast::Receiver + let mut rx = tx.subscribe(); + Sse::new(stream! { + // Send message so we can test the server performance + for i in (10..=510).step_by(10) { + for j in (10..=510).step_by(10) { + let message = format!("{j},{i}"); + yield Ok(Event::default().data::(message)); + std::thread::sleep(std::time::Duration::from_millis(1)); + } + } + println!("All messages sent."); + }) +} From 22c321addc0cfafb1228cfb5905b04584e99feac Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 23:02:02 -0800 Subject: [PATCH 09/39] Remove unneeded code --- server-sent-events/axum-sse/src/main.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/server-sent-events/axum-sse/src/main.rs b/server-sent-events/axum-sse/src/main.rs index 2a9de7f..6a6af38 100644 --- a/server-sent-events/axum-sse/src/main.rs +++ b/server-sent-events/axum-sse/src/main.rs @@ -1,29 +1,21 @@ use async_stream::stream; use axum::{ Router, - extract::State, response::sse::{Event, Sse}, routing::get, }; use std::convert::Infallible; use futures::stream::Stream; -use tokio::sync::broadcast; -use tokio::sync::broadcast::Sender; #[tokio::main] async fn main() { - let (tx, _rx) = broadcast::channel::(100); - let app = Router::new().route("/", get(sse_handler)).with_state(tx); + let app = Router::new().route("/", get(sse_handler)); let listener = tokio::net::TcpListener::bind("0.0.0.0:7999").await.unwrap(); axum::serve(listener, app).await.unwrap(); } -async fn sse_handler( - State(tx): State>, -) -> Sse>> { - // broadcast::Receiver - let mut rx = tx.subscribe(); +async fn sse_handler() -> Sse>> { Sse::new(stream! { // Send message so we can test the server performance for i in (10..=510).step_by(10) { From 0d891fe31bbb6b559cf3c25adf22f6f7ce275837 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 23:11:24 -0800 Subject: [PATCH 10/39] Update README with SSE information --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2b60fc..43d52d5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # realtime-web-comparison -Experimenting with WebSocket, WebRTC, and WebTransport by streaming 2500 coordinates from server to client to visualize. +Experimenting with Server Sent Events, WebSocket, WebRTC, and WebTransport by streaming 2500 coordinates from server to client to visualize. # NOTE: This repository is currently in flux. -- The WebTransport server currently does not work, however WebSockets and the WebRTC datachannels do +- The WebTransport server currently does not work - We're doing our best to update all code to the latest versions of their dependencies. **Additional notes:** @@ -11,9 +11,27 @@ Experimenting with WebSocket, WebRTC, and WebTransport by streaming 2500 coordin - No limits were specified on packet size or how protocols buffer packets. +The pseudo code of what each test is doing looks somewhat like this: + +```go +for i := 10; i < 510; i += 10 { + for j := 10; j < 510; j += 10 { + message := fmt.Sprintf("%d,%d", j, i) + if err := conn.WriteMessage(websocket.TextMessage, []byte(message)); err != nil { + log.Fatal(err) + } + time.Sleep(1 * time.Millisecond) + } +} +``` + ## Dependencies +- Server Sent Events + - [tmaxmax/go-sse](https://github.com/tmaxmax/go-sse) + - [tokio-rs/axum](https://github.com/tokio-rs/axum) - WebSockets - [gorilla/websocket](https://github.com/gorilla/websocket) + - [snapview/tungstenite-rs](https://github.com/snapview/tungstenite-rs) - WebRTC - [pion/webrtc](https://github.com/pion/webrtc) - WebTransport From 5fd3e0d96fa95fda95dd920d947989e1233de721 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 10:13:14 -0800 Subject: [PATCH 11/39] Add basic signaling server --- webrtc/str0m-server/Cargo.lock | 1049 +++++++++++++++++++++++++++++++ webrtc/str0m-server/Cargo.toml | 12 + webrtc/str0m-server/src/main.rs | 71 +++ 3 files changed, 1132 insertions(+) create mode 100644 webrtc/str0m-server/Cargo.lock create mode 100644 webrtc/str0m-server/Cargo.toml create mode 100644 webrtc/str0m-server/src/main.rs diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock new file mode 100644 index 0000000..7fc9f03 --- /dev/null +++ b/webrtc/str0m-server/Cargo.lock @@ -0,0 +1,1049 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "aws-lc-rs" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "sctp-proto" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "423139d8cca3021b9d800f084a711ba2d23b508ae71b33dba167f11ca33e54c7" +dependencies = [ + "bytes", + "crc", + "log", + "rand", + "rustc-hash", + "slab", + "thiserror", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "str0m" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26890ff5b60e33eb8bedcf44792fc459c8f348ecbf2658edb19477571e547ac2" +dependencies = [ + "combine", + "crc", + "fastrand", + "hmac", + "once_cell", + "sctp-proto", + "serde", + "str0m-wincrypto", + "tracing", +] + +[[package]] +name = "str0m-server" +version = "0.1.0" +dependencies = [ + "rustls", + "rustls-pki-types", + "str0m", + "tracing", + "tracing-subscriber", + "tungstenite", +] + +[[package]] +name = "str0m-wincrypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457d36c2642d30dc703e84a1eadd04d62af7d46c821da3937040eea8a52395fd" +dependencies = [ + "tracing", + "windows", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", + "webpki-roots 0.26.11", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml new file mode 100644 index 0000000..2e0280f --- /dev/null +++ b/webrtc/str0m-server/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "str0m-server" +version = "0.1.0" +edition = "2024" + +[dependencies] +rustls = "0.23.35" +rustls-pki-types = "1.13.0" +str0m = { version = "0.11.1", default-features = false, features = ["wincrypto"] } +tracing = { version = "0.1.41", features = ["async-await"] } +tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } +tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs new file mode 100644 index 0000000..9286692 --- /dev/null +++ b/webrtc/str0m-server/src/main.rs @@ -0,0 +1,71 @@ +use std::net::TcpListener; +use std::path::Path; +use std::sync::Arc; +use std::thread::spawn; + +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use rustls::{ServerConfig, ServerConnection, StreamOwned}; +use tungstenite::accept; + +/// A WebSocket echo server over TLS (wss://) +fn main() { + // Use fixed relative paths from this crate to the repo certs + let cert_path = Path::new("../../certs/localhost.pem"); + let key_path = Path::new("../../certs/localhost-key.pem"); + + eprintln!("Using certificate: {}", cert_path.display()); + eprintln!("Using private key: {}", key_path.display()); + + let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); + let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + + // Build rustls server config (no client auth) + let config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], key) + .expect("invalid cert/key"); + let config = Arc::new(config); + + let listener = TcpListener::bind("127.0.0.1:8002").expect("bind 127.0.0.1:8002"); + eprintln!("tungstenite server listening on wss://127.0.0.1:8002"); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + eprintln!( + "incoming TCP connection from {}", + stream.peer_addr().unwrap() + ); + let cfg = Arc::clone(&config); + spawn(move || { + // Wrap TCP in a rustls TLS stream. + let conn = match ServerConnection::new(cfg) { + Ok(c) => c, + Err(e) => { + eprintln!("TLS ServerConnection error: {e}"); + return; + } + }; + let tls_stream = StreamOwned::new(conn, stream); + + match accept(tls_stream) { + Ok(mut websocket) => loop { + if let Ok(msg) = websocket.read_message() { + eprintln!("Received message: {}", msg); + } else { + eprintln!("Client disconnected"); + break; + } + }, + Err(e) => { + eprintln!("websocket handshake failed: {e}"); + eprintln!("Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/."); + } + } + }); + } + Err(e) => eprintln!("incoming connection error: {e}"), + } + } +} From b7cb09f7a61365ab2695692f4d3aff284a86f8a4 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 10:42:12 -0800 Subject: [PATCH 12/39] Won't build due to linker errors --- webrtc/str0m-server/Cargo.lock | 22 ++++++++++++++++++++++ webrtc/str0m-server/Cargo.toml | 3 +++ webrtc/str0m-server/src/main.rs | 31 +++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 7fc9f03..54f0792 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -546,6 +546,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "sctp-proto" version = "0.5.0" @@ -591,6 +597,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + [[package]] name = "sha1" version = "0.10.6" @@ -650,8 +669,11 @@ dependencies = [ name = "str0m-server" version = "0.1.0" dependencies = [ + "bytes", "rustls", "rustls-pki-types", + "serde", + "serde_json", "str0m", "tracing", "tracing-subscriber", diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index 2e0280f..803b904 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -4,8 +4,11 @@ version = "0.1.0" edition = "2024" [dependencies] +bytes = "1.10.1" rustls = "0.23.35" rustls-pki-types = "1.13.0" +serde = "1.0.228" +serde_json = "1.0.145" str0m = { version = "0.11.1", default-features = false, features = ["wincrypto"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 9286692..8c9098e 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -6,6 +6,8 @@ use std::thread::spawn; use rustls::pki_types::pem::PemObject; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use rustls::{ServerConfig, ServerConnection, StreamOwned}; +use str0m::Rtc; +use str0m::change::SdpOffer; use tungstenite::accept; /// A WebSocket echo server over TLS (wss://) @@ -51,8 +53,31 @@ fn main() { match accept(tls_stream) { Ok(mut websocket) => loop { - if let Ok(msg) = websocket.read_message() { + if let Ok(msg) = websocket.read() { eprintln!("Received message: {}", msg); + // create RTC client + let offer: SdpOffer = + serde_json::from_slice(&msg.into_data()).expect("serialized offer"); + let mut rtc = Rtc::builder() + // Uncomment this to see statistics + // .set_stats_interval(Some(Duration::from_secs(1))) + // .set_ice_lite(true) + .build(); + + // Add the shared UDP socket as a host candidate + /* + let candidate = + Candidate::host(addr, "udp").expect("a host candidate"); + rtc.add_local_candidate(candidate).unwrap(); + */ + + // Create an SDP Answer. + let answer = rtc + .sdp_api() + .accept_offer(offer) + .expect("offer to be accepted"); + + websocket.write(answer.to_sdp_string().into()).expect("send answer"); } else { eprintln!("Client disconnected"); break; @@ -60,7 +85,9 @@ fn main() { }, Err(e) => { eprintln!("websocket handshake failed: {e}"); - eprintln!("Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/."); + eprintln!( + "Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/." + ); } } }); From 6a0ad717be22f17a2a1d468fc491a70ce10b4f1a Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 10:47:55 -0800 Subject: [PATCH 13/39] Switching to dimpl doesn't help --- webrtc/str0m-server/Cargo.lock | 586 ++++++++++++++++++++++++++++----- webrtc/str0m-server/Cargo.toml | 2 +- 2 files changed, 507 insertions(+), 81 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 54f0792..96fe60e 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -2,6 +2,41 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -34,6 +69,18 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bindgen" version = "0.72.1" @@ -102,6 +149,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -132,6 +189,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -156,6 +219,18 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -163,15 +238,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "data-encoding" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -179,34 +297,115 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dimpl" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2044cebfec76b1fa473b0ff38e5d8522d8ece8e2ca87d62ef45189d18838b03" +dependencies = [ + "aes-gcm", + "der", + "ecdsa", + "elliptic-curve", + "hmac", + "log", + "nom", + "once_cell", + "p256", + "p384", + "pkcs8", + "rand", + "sec1", + "self_cell", + "sha2", + "signature", + "spki", + "time", + "tinyvec", + "x509-cert", + "zeroize", +] + [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "find-msvc-tools" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "fnv" version = "1.0.7" @@ -227,6 +426,7 @@ checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -252,12 +452,42 @@ dependencies = [ "wasip2", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -284,6 +514,15 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + [[package]] name = "itertools" version = "0.13.0" @@ -377,18 +616,91 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -408,6 +720,15 @@ dependencies = [ "syn", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -439,7 +760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -449,7 +770,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", ] [[package]] @@ -490,6 +820,16 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -567,6 +907,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "self_cell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" + [[package]] name = "serde" version = "1.0.228" @@ -615,6 +975,27 @@ name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", + "sha1-asm", +] + +[[package]] +name = "sha1-asm" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "286acebaf8b67c1130aedffad26f594eff0c1292389158135327d2e23aed582b" +dependencies = [ + "cc", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -636,6 +1017,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "slab" version = "0.4.11" @@ -649,19 +1040,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "str0m" -version = "0.11.1" +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26890ff5b60e33eb8bedcf44792fc459c8f348ecbf2658edb19477571e547ac2" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "str0m" +version = "0.11.0" +source = "git+https://github.com/algesten/str0m.git?branch=fix%2Fdimpl#91d8e7eb40685ea9e25cee9e6d08c8e0f8465a9c" dependencies = [ + "aes", + "aes-gcm", "combine", "crc", + "ctr", + "dimpl", "fastrand", "hmac", "once_cell", "sctp-proto", "serde", - "str0m-wincrypto", + "sha1", "tracing", ] @@ -680,16 +1084,6 @@ dependencies = [ "tungstenite", ] -[[package]] -name = "str0m-wincrypto" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457d36c2642d30dc703e84a1eadd04d62af7d46c821da3937040eea8a52395fd" -dependencies = [ - "tracing", - "windows", -] - [[package]] name = "subtle" version = "2.6.1" @@ -736,6 +1130,64 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" + +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing" version = "0.1.41" @@ -829,6 +1281,16 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -886,76 +1348,12 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core", - "windows-targets", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -1044,6 +1442,20 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "sha1", + "signature", + "spki", + "tls_codec", +] + [[package]] name = "zerocopy" version = "0.8.27" @@ -1069,3 +1481,17 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index 803b904..7de0747 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -9,7 +9,7 @@ rustls = "0.23.35" rustls-pki-types = "1.13.0" serde = "1.0.228" serde_json = "1.0.145" -str0m = { version = "0.11.1", default-features = false, features = ["wincrypto"] } +str0m = { git = "https://github.com/algesten/str0m.git", branch = "fix/dimpl", default-features = false, features = ["dimpl"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } From f0670fb54d0dabd41b97f4b297d7d7fee3d9e2a0 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 17:38:06 -0800 Subject: [PATCH 14/39] We have an rtc connection going --- webrtc/str0m-server/.cargo/config.toml | 3 + webrtc/str0m-server/Cargo.lock | 596 ++++--------------------- webrtc/str0m-server/Cargo.toml | 2 +- webrtc/str0m-server/src/main.rs | 39 +- 4 files changed, 113 insertions(+), 527 deletions(-) create mode 100644 webrtc/str0m-server/.cargo/config.toml diff --git a/webrtc/str0m-server/.cargo/config.toml b/webrtc/str0m-server/.cargo/config.toml new file mode 100644 index 0000000..d707ff2 --- /dev/null +++ b/webrtc/str0m-server/.cargo/config.toml @@ -0,0 +1,3 @@ +[target.x86_64-pc-windows-msvc] +linker = "rust-lld" +rustflags = ["-C", "link-arg=-fuse-ld=lld"] diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 96fe60e..9acf675 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -2,41 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -69,18 +34,6 @@ dependencies = [ "fs_extra", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - [[package]] name = "bindgen" version = "0.72.1" @@ -149,16 +102,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -189,12 +132,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -219,18 +156,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -238,58 +163,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", "typenum", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - [[package]] name = "data-encoding" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - [[package]] name = "digest" version = "0.10.7" @@ -297,115 +179,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", "crypto-common", "subtle", ] -[[package]] -name = "dimpl" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2044cebfec76b1fa473b0ff38e5d8522d8ece8e2ca87d62ef45189d18838b03" -dependencies = [ - "aes-gcm", - "der", - "ecdsa", - "elliptic-curve", - "hmac", - "log", - "nom", - "once_cell", - "p256", - "p384", - "pkcs8", - "rand", - "sec1", - "self_cell", - "sha2", - "signature", - "spki", - "time", - "tinyvec", - "x509-cert", - "zeroize", -] - [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "find-msvc-tools" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" -[[package]] -name = "flagset" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" - [[package]] name = "fnv" version = "1.0.7" @@ -426,7 +227,6 @@ checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -452,42 +252,12 @@ dependencies = [ "wasip2", ] -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - [[package]] name = "hmac" version = "0.12.1" @@ -514,15 +284,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array", -] - [[package]] name = "itertools" version = "0.13.0" @@ -616,91 +377,18 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -720,15 +408,6 @@ dependencies = [ "syn", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "proc-macro2" version = "1.0.103" @@ -760,7 +439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core 0.9.3", + "rand_core", ] [[package]] @@ -770,16 +449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", + "rand_core", ] [[package]] @@ -820,16 +490,6 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "ring" version = "0.17.14" @@ -907,26 +567,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "self_cell" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" - [[package]] name = "serde" version = "1.0.228" @@ -975,27 +615,6 @@ name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", - "sha1-asm", -] - -[[package]] -name = "sha1-asm" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286acebaf8b67c1130aedffad26f594eff0c1292389158135327d2e23aed582b" -dependencies = [ - "cc", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -1017,16 +636,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - [[package]] name = "slab" version = "0.4.11" @@ -1039,33 +648,20 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "str0m" -version = "0.11.0" -source = "git+https://github.com/algesten/str0m.git?branch=fix%2Fdimpl#91d8e7eb40685ea9e25cee9e6d08c8e0f8465a9c" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26890ff5b60e33eb8bedcf44792fc459c8f348ecbf2658edb19477571e547ac2" dependencies = [ - "aes", - "aes-gcm", "combine", "crc", - "ctr", - "dimpl", "fastrand", "hmac", "once_cell", "sctp-proto", "serde", - "sha1", + "str0m-wincrypto", "tracing", ] @@ -1084,6 +680,16 @@ dependencies = [ "tungstenite", ] +[[package]] +name = "str0m-wincrypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457d36c2642d30dc703e84a1eadd04d62af7d46c821da3937040eea8a52395fd" +dependencies = [ + "tracing", + "windows", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1092,9 +698,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -1130,64 +736,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" - -[[package]] -name = "tls_codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing" version = "0.1.41" @@ -1281,16 +829,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "untrusted" version = "0.9.0" @@ -1336,24 +874,88 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -1442,20 +1044,6 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "sha1", - "signature", - "spki", - "tls_codec", -] - [[package]] name = "zerocopy" version = "0.8.27" @@ -1481,17 +1069,3 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index 7de0747..803b904 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -9,7 +9,7 @@ rustls = "0.23.35" rustls-pki-types = "1.13.0" serde = "1.0.228" serde_json = "1.0.145" -str0m = { git = "https://github.com/algesten/str0m.git", branch = "fix/dimpl", default-features = false, features = ["dimpl"] } +str0m = { version = "0.11.1", default-features = false, features = ["wincrypto"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 8c9098e..85a0566 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -6,8 +6,8 @@ use std::thread::spawn; use rustls::pki_types::pem::PemObject; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use rustls::{ServerConfig, ServerConnection, StreamOwned}; -use str0m::Rtc; use str0m::change::SdpOffer; +use str0m::{Candidate, Rtc}; use tungstenite::accept; /// A WebSocket echo server over TLS (wss://) @@ -56,20 +56,25 @@ fn main() { if let Ok(msg) = websocket.read() { eprintln!("Received message: {}", msg); // create RTC client - let offer: SdpOffer = - serde_json::from_slice(&msg.into_data()).expect("serialized offer"); - let mut rtc = Rtc::builder() - // Uncomment this to see statistics - // .set_stats_interval(Some(Duration::from_secs(1))) - // .set_ice_lite(true) - .build(); + let offer: SdpOffer = serde_json::from_slice(&msg.into_data()) + .expect("serialized offer"); + let cert_options = str0m::config::DtlsCertOptions { + pkey_type: str0m::config::DtlsPKeyType::EcDsaP256, + ..Default::default() + }; + let crypto_provider = if cfg!(windows) { + str0m::config::CryptoProvider::WinCrypto + } else { + str0m::config::CryptoProvider::OpenSsl + }; + let dtls_cert = + str0m::config::DtlsCert::new(crypto_provider, cert_options); - // Add the shared UDP socket as a host candidate - /* - let candidate = - Candidate::host(addr, "udp").expect("a host candidate"); - rtc.add_local_candidate(candidate).unwrap(); - */ + let str0m_config = str0m::RtcConfig::new().set_dtls_cert_config( + str0m::DtlsCertConfig::PregeneratedCert(dtls_cert), + ); + + let mut rtc = str0m_config.build(); // Create an SDP Answer. let answer = rtc @@ -77,7 +82,11 @@ fn main() { .accept_offer(offer) .expect("offer to be accepted"); - websocket.write(answer.to_sdp_string().into()).expect("send answer"); + websocket + .write(answer.to_sdp_string().into()) + .expect("send answer"); + + eprintln!("Sent SDP answer"); } else { eprintln!("Client disconnected"); break; From 1fbd97dcc42ff9f0b8ed20101d162023b6aec9c2 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 18:22:30 -0800 Subject: [PATCH 15/39] ICE gets stuck in checking --- webrtc/str0m-server/src/main.rs | 106 ++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 85a0566..d381a21 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -1,17 +1,28 @@ -use std::net::TcpListener; +use std::io::ErrorKind; +use std::net::{TcpListener, UdpSocket}; use std::path::Path; use std::sync::Arc; use std::thread::spawn; +use std::time::{Duration, Instant}; use rustls::pki_types::pem::PemObject; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use rustls::{ServerConfig, ServerConnection, StreamOwned}; use str0m::change::SdpOffer; -use str0m::{Candidate, Rtc}; -use tungstenite::accept; +use str0m::config::CryptoProvider; +use str0m::net::{Protocol, Receive}; +use str0m::{Candidate, Event, IceConnectionState, Input, Output, Rtc}; +use tungstenite::{accept, buffer}; /// A WebSocket echo server over TLS (wss://) fn main() { + // Select crypto backend explicitly (required by str0m on Windows). + // Safe to call once at process start; will panic if called twice. + #[cfg(windows)] + { + CryptoProvider::WinCrypto.install_process_default(); + } + // Use fixed relative paths from this crate to the repo certs let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); @@ -55,9 +66,17 @@ fn main() { Ok(mut websocket) => loop { if let Ok(msg) = websocket.read() { eprintln!("Received message: {}", msg); - // create RTC client + + // Parse remote SDP offer. let offer: SdpOffer = serde_json::from_slice(&msg.into_data()) .expect("serialized offer"); + + // Prepare UDP socket to carry ICE/DTLS/SCTP traffic. + let udp = UdpSocket::bind("0.0.0.0:0").expect("bind UDP"); + let local_addr = udp.local_addr().expect("local addr"); + eprintln!("UDP bound on {}", local_addr); + + // Build Rtc instance. let cert_options = str0m::config::DtlsCertOptions { pkey_type: str0m::config::DtlsPKeyType::EcDsaP256, ..Default::default() @@ -76,17 +95,92 @@ fn main() { let mut rtc = str0m_config.build(); - // Create an SDP Answer. + // Accept offer and create answer. let answer = rtc .sdp_api() .accept_offer(offer) .expect("offer to be accepted"); + // Send SDP answer back to the client. websocket .write(answer.to_sdp_string().into()) .expect("send answer"); - eprintln!("Sent SDP answer"); + + let socket = udp; + let buffer_size = 2000; + let mut buf = vec![0u8; buffer_size]; + loop { + // Poll outputs until timeout is returned. + let timeout_instant = loop { + match rtc.poll_output().expect("poll output") { + Output::Timeout(when) => break when, + Output::Transmit(tx) => { + let _ = + socket.send_to(&tx.contents, tx.destination); + } + Output::Event(ev) => { + eprintln!("RTC event: {:?}", ev); + if let Event::IceConnectionStateChange(state) = ev { + if state == IceConnectionState::Disconnected { + eprintln!("ICE disconnected"); + return; + } + } + } + } + }; + + // Compute duration until timeout. + let now = Instant::now(); + let duration = if timeout_instant > now { + timeout_instant - now + } else { + Duration::from_millis(0) + }; + + if duration.is_zero() { + // Drive timers immediately. + rtc.handle_input(Input::Timeout(Instant::now())) + .expect("timeout input"); + continue; + } + + // run RTC input + let _ = socket.set_read_timeout(Some(duration)); + buf.resize(buffer_size, 0); + match socket.recv_from(&mut buf) { + Ok((n, src)) => { + buf.truncate(n); + let input = Input::Receive( + Instant::now(), + Receive { + proto: Protocol::Udp, + source: src, + destination: socket + .local_addr() + .expect("local addr"), + contents: buf + .as_slice() + .try_into() + .expect("slice"), + }, + ); + rtc.handle_input(input).expect("handle input"); + } + Err(e) + if e.kind() == ErrorKind::WouldBlock + || e.kind() == ErrorKind::TimedOut => + { + rtc.handle_input(Input::Timeout(Instant::now())) + .expect("timeout input"); + } + Err(e) => { + eprintln!("UDP error: {e}"); + return; + } + } + } } else { eprintln!("Client disconnected"); break; From c2031b7f22ce5bbd74c1782bd048c97426067777 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 18:25:45 -0800 Subject: [PATCH 16/39] Remove unnecessary configure --- webrtc/str0m-server/src/main.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index d381a21..2c69e02 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -16,13 +16,6 @@ use tungstenite::{accept, buffer}; /// A WebSocket echo server over TLS (wss://) fn main() { - // Select crypto backend explicitly (required by str0m on Windows). - // Safe to call once at process start; will panic if called twice. - #[cfg(windows)] - { - CryptoProvider::WinCrypto.install_process_default(); - } - // Use fixed relative paths from this crate to the repo certs let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); From 5e532196de1164efe21877c308bd8cb4abac7295 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 04:55:14 +0200 Subject: [PATCH 17/39] Add nix flakes --- flake.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..dddda94 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1762111121, + "narHash": "sha256-4vhDuZ7OZaZmKKrnDpxLZZpGIJvAeMtK6FKLJYUtAdw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b3d51a0365f6695e7dd5cdf3e180604530ed33b4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..71913f5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,38 @@ +{ + description = "Real-time web comparison project with SSE, WebSocket, WebRTC, and WebTransport"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + }; + in + { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + rustc + cargo + rustfmt + clippy + rust-analyzer + go + openssl + pkg-config + git + ]; + + OPENSSL_DIR = "${pkgs.openssl.dev}"; + OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib"; + OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include"; + + PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig"; + }; + }); +} + From 7591f93bf5f8cb91ae9db3f324e72c68495ba823 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 04:55:33 +0200 Subject: [PATCH 18/39] Update gitignore --- .gitignore | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 133625a..52b2ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,8 @@ -.idea/ -certs/ -*.pem -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib +# Nix +result +result-* -# Test binary, built with `go test -c` -*.test -# Generated by Cargo -# will have compiled files and executables -debug -target - -# These are backup files generated by rustfmt +# Rust +target/ **/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information *.pdb From 735158dd63a919c087dd4750c0537bc57803134c Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 04:55:53 +0200 Subject: [PATCH 19/39] Use openssl on non-windows --- webrtc/str0m-server/Cargo.lock | 68 ++++++++++++++++++++++++++++++++++ webrtc/str0m-server/Cargo.toml | 7 +++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 9acf675..c63cf30 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -213,6 +213,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "fs_extra" version = "1.3.0" @@ -383,12 +398,56 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -658,7 +717,10 @@ dependencies = [ "crc", "fastrand", "hmac", + "libc", "once_cell", + "openssl", + "openssl-sys", "sctp-proto", "serde", "str0m-wincrypto", @@ -847,6 +909,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index 803b904..d0ca534 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -9,7 +9,12 @@ rustls = "0.23.35" rustls-pki-types = "1.13.0" serde = "1.0.228" serde_json = "1.0.145" -str0m = { version = "0.11.1", default-features = false, features = ["wincrypto"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } + +[target.'cfg(not(target_os = "windows"))'.dependencies] +str0m = { version = "0.11.1", default-features = false, features = ["openssl"] } + +[target.'cfg(target_os = "windows")'.dependencies] +str0m = { version = "0.11.1", default-features = false, features = ["wincrypto"] } From 5bf6a76655cbe41e04912ffab2983c297bf2891f Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 05:03:19 +0200 Subject: [PATCH 20/39] Update gitignore --- .gitignore | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.gitignore b/.gitignore index 52b2ac7..87c1668 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,36 @@ result-* target/ **/*.rs.bk *.pdb +debug/ + +.idea/ +certs/ +*.pem +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +bin/ +vendor/ +node_modules/ + +cover.out +*.wasm + +*~ From 9ab8e1d2e164fa2cabdf676c06378e5dc72dac8b Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 05:12:35 +0200 Subject: [PATCH 21/39] Add setup-certs for flakes --- README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++--------- flake.nix | 34 +++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 43d52d5..348b778 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # realtime-web-comparison Experimenting with Server Sent Events, WebSocket, WebRTC, and WebTransport by streaming 2500 coordinates from server to client to visualize. -# NOTE: This repository is currently in flux. +# NOTE: This repository is currently in flux. - The WebTransport server currently does not work - We're doing our best to update all code to the latest versions of their dependencies. @@ -36,7 +36,7 @@ for i := 10; i < 510; i += 10 { - [pion/webrtc](https://github.com/pion/webrtc) - WebTransport - [adriancable/webtransport-go](https://github.com/adriancable/webtransport-go) -- Client is written in pure HTML/CSS/JS. +- Client is written in pure HTML/CSS/JS. - For CSS: [Bootstrap](https://getbootstrap.com/) - The chart visualization is done using [Chart.js](https://www.chartjs.org/). @@ -48,12 +48,16 @@ for i := 10; i < 510; i += 10 { cd realtime-web-comparison ``` -2. Create locally trusted certs using [mkcert](https://github.com/FiloSottile/mkcert) - ```bash - mkdir certs && cd certs - mkcert -install - mkcert localhost - ``` +2. Create locally trusted certs using [mkcert](https://github.com/FiloSottile/mkcert) + ```bash + mkdir certs && cd certs + mkcert -install + mkcert localhost + ``` + If using Nix flakes (see [Nix Setup Flakes](#nix-setup) section above): + ```bash + nix run .#setup-certs + ``` 3. Run a server (use similar commands for `webtransport` and `webrtc`) ```bash @@ -64,10 +68,45 @@ for i := 10; i < 510; i += 10 { ```bash sudo tc qdisc add dev lo root netem loss 15% ``` - + 5. Run client ```bash ./run.sh client chromium --origin-to-force-quic-on=localhost:8001 http://localhost:3000 ``` +## Nix Setup + +This repository includes a Nix flake for reproducible development environments. The flake provides all necessary dependencies including Rust, Go, OpenSSL, and mkcert. + +### Development Shell + +Enter the development shell to get all tools in your PATH: + +```bash +nix develop +``` + +This provides: +- Rust toolchain (rustc, cargo, rustfmt, clippy, rust-analyzer) +- Go toolchain +- OpenSSL with proper environment variables configured +- mkcert for certificate generation +- NSS tools (for Firefox support on Linux) + +### Setting Up Certificates + +Use the Nix task to automatically set up locally trusted certificates: + +```bash +nix run .#setup-certs +``` + +The `setup-certs` task will: +- Create the `certs/` directory if it doesn't exist +- Install the local CA certificate in your system trust store +- Generate a certificate for `localhost` + +### Available Tasks + +- `nix run .#setup-certs` - Set up development certificates using mkcert diff --git a/flake.nix b/flake.nix index 71913f5..ce7a281 100644 --- a/flake.nix +++ b/flake.nix @@ -12,6 +12,28 @@ pkgs = import nixpkgs { inherit system; }; + setup-certs = pkgs.writeShellScriptBin "setup-certs" '' + set -e + echo "Setting up locally trusted certificates..." + + REPO_ROOT="$(pwd)" + CERT_DIR="$REPO_ROOT/certs" + + # Create certs directory if it doesn't exist + mkdir -p "$CERT_DIR" + + echo "Installing local CA..." + ${pkgs.mkcert}/bin/mkcert -install + + echo "Generating certificate for localhost in $CERT_DIR..." + cd "$CERT_DIR" + ${pkgs.mkcert}/bin/mkcert localhost + cd "$REPO_ROOT" + + echo "ertificates created successfully!" + echo "Certificate files are in: $CERT_DIR" + ls -la "$CERT_DIR"/*.pem 2>/dev/null || echo " (checking certificate files...)" + ''; in { devShells.default = pkgs.mkShell { @@ -25,6 +47,8 @@ openssl pkg-config git + mkcert + nss.tools ]; OPENSSL_DIR = "${pkgs.openssl.dev}"; @@ -33,6 +57,16 @@ PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig"; }; + + apps.default = { + type = "app"; + program = "${setup-certs}/bin/setup-certs"; + }; + + apps.setup-certs = { + type = "app"; + program = "${setup-certs}/bin/setup-certs"; + }; }); } From 79be45c7f49441eb9cb1b1b33021de7539d04be4 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 05:20:27 +0200 Subject: [PATCH 22/39] Add a note about --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 348b778..405b6fb 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,5 @@ The `setup-certs` task will: ### Available Tasks - `nix run .#setup-certs` - Set up development certificates using mkcert + +Note: you might need to run `nix develop -c mkcert -install` and restart your browsers. \ No newline at end of file From cb8ab80978f39d07d1d369596336d9025aad3b2a Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 07:53:12 +0200 Subject: [PATCH 23/39] Make str0m work --- webrtc/str0m-server/Cargo.lock | 307 ++++++++++++++-- webrtc/str0m-server/Cargo.toml | 15 +- webrtc/str0m-server/src/main.rs | 600 +++++++++++++++++++++++--------- 3 files changed, 722 insertions(+), 200 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index c63cf30..e895b1a 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "aws-lc-rs" version = "1.14.1" @@ -156,6 +162,21 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -234,6 +255,50 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.9" @@ -299,6 +364,16 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "if-addrs" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf39cc0423ee66021dc5eccface85580e4a001e0c5288bae8bea7ecb69225e90" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "itertools" version = "0.13.0" @@ -373,6 +448,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "nom" version = "7.1.3" @@ -442,6 +528,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.32" @@ -584,6 +676,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.13.0" @@ -707,6 +808,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "str0m" version = "0.11.1" @@ -731,15 +842,24 @@ dependencies = [ name = "str0m-server" version = "0.1.0" dependencies = [ + "anyhow", "bytes", + "crossbeam-channel", + "futures-util", + "if-addrs", "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "str0m", + "tokio", + "tokio-rustls", + "tokio-tungstenite", "tracing", "tracing-subscriber", - "tungstenite", + "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -798,6 +918,55 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tokio-rustls", + "tungstenite", +] + [[package]] name = "tracing" version = "0.1.41" @@ -871,12 +1040,9 @@ dependencies = [ "httparse", "log", "rand", - "rustls", - "rustls-pki-types", "sha1", "thiserror", "utf-8", - "webpki-roots 0.26.11", ] [[package]] @@ -937,22 +1103,26 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.26.11" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "webpki-roots 1.0.4", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] -name = "webpki-roots" -version = "1.0.4" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" -dependencies = [ - "rustls-pki-types", -] +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" @@ -961,7 +1131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -974,7 +1144,7 @@ dependencies = [ "windows-interface", "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1011,7 +1181,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1021,7 +1191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1030,7 +1200,25 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -1048,14 +1236,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -1064,48 +1269,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "wit-bindgen" version = "0.46.0" diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index d0ca534..0ce96ea 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -4,14 +4,25 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.100" bytes = "1.10.1" +crossbeam-channel = "0.5.15" +futures-util = "0.3.31" +if-addrs = "0.14.0" rustls = "0.23.35" +rustls-pemfile = "2.2.0" rustls-pki-types = "1.13.0" -serde = "1.0.228" +serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" +tokio = { version = "1.48.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] } +tokio-rustls = "0.26.4" +tokio-tungstenite = { version = "0.28.0", features = ["tokio-rustls"] } +windows-sys = { version = "0.61.2", features = ["Win32_Media_Multimedia"] } + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["winbase"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } -tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } [target.'cfg(not(target_os = "windows"))'.dependencies] str0m = { version = "0.11.1", default-features = false, features = ["openssl"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 2c69e02..970449a 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -1,194 +1,452 @@ -use std::io::ErrorKind; -use std::net::{TcpListener, UdpSocket}; +// Cargo.toml (dependencies omitted for brevity)... + +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::Path; use std::sync::Arc; -use std::thread::spawn; use std::time::{Duration, Instant}; -use rustls::pki_types::pem::PemObject; +use crossbeam_channel as cb; +use futures_util::{SinkExt, StreamExt}; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use rustls::{ServerConfig, ServerConnection, StreamOwned}; -use str0m::change::SdpOffer; -use str0m::config::CryptoProvider; +use serde::{Deserialize, Serialize}; +use tokio::net::{TcpListener, UdpSocket}; +use tokio_rustls::{server::TlsStream, TlsAcceptor}; +use tokio_tungstenite::{accept_async, tungstenite::Message}; + use str0m::net::{Protocol, Receive}; -use str0m::{Candidate, Event, IceConnectionState, Input, Output, Rtc}; -use tungstenite::{accept, buffer}; +use str0m::{Candidate, Event, IceConnectionState, Input, Output}; + +#[derive(Deserialize, Debug)] +#[serde(tag = "type")] +enum WsInbound { + #[serde(rename = "offer")] + Offer { sdp: String }, + #[serde(rename = "candidate")] + Candidate { + candidate: String, + #[serde(rename = "sdpMid")] + #[allow(dead_code)] + sdp_mid: Option, + #[serde(rename = "sdpMLineIndex")] + #[allow(dead_code)] + sdp_m_line_index: Option, + }, + #[serde(rename = "endOfCandidates")] + EndOfCandidates {}, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +#[allow(dead_code)] +enum WsOutbound { + #[serde(rename = "answer")] + Answer { sdp: String }, + #[serde(rename = "candidate")] + Candidate { + candidate: String, + #[serde(rename = "sdpMid")] + sdp_mid: Option, + #[serde(rename = "sdpMLineIndex")] + sdp_m_line_index: Option, + }, + #[serde(rename = "endOfCandidates")] + EndOfCandidates {}, +} -/// A WebSocket echo server over TLS (wss://) -fn main() { - // Use fixed relative paths from this crate to the repo certs +// Enumerate non-loopback IPv4 addresse +#[allow(dead_code)] +fn enumerate_ipv4_non_loopback() -> Vec { + let mut v = Vec::new(); + if let Ok(ifs) = if_addrs::get_if_addrs() { + for i in ifs { + if let IpAddr::V4(ip) = i.ip() { + if !ip.is_loopback() && !ip.is_link_local() { + v.push(ip); + } + } + } + } + if v.is_empty() { + v.push(Ipv4Addr::UNSPECIFIED); + } + v +} + +#[cfg(target_os = "windows")] +struct TimerPeriodGuard; +#[cfg(target_os = "windows")] +impl TimerPeriodGuard { + fn new_1ms() -> Self { + unsafe { windows_sys::Win32::Media::Multimedia::timeBeginPeriod(1); } + TimerPeriodGuard + } +} +#[cfg(target_os = "windows")] +impl Drop for TimerPeriodGuard { + fn drop(&mut self) { + unsafe { windows_sys::Win32::Media::Multimedia::timeEndPeriod(1); } + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + #[cfg(target_os = "windows")] + let _timer_guard = TimerPeriodGuard::new_1ms(); + + // TLS setup omitted for brevity... let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); + let cert_pem = std::fs::read(cert_path)?; + let key_pem = std::fs::read(key_path)?; + let cert = rustls_pemfile::certs(&mut &cert_pem[..]).next() + .ok_or_else(|| anyhow::anyhow!("no certificate found"))? + .map(CertificateDer::from)?; + let key = rustls_pemfile::pkcs8_private_keys(&mut &key_pem[..]).next() + .ok_or_else(|| anyhow::anyhow!("no private key found"))? + .map(PrivateKeyDer::from)?; + let rustls_cfg = rustls::ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], key)?; + let acceptor = TlsAcceptor::from(Arc::new(rustls_cfg)); - eprintln!("Using certificate: {}", cert_path.display()); - eprintln!("Using private key: {}", key_path.display()); + // Choose a bind address: first non-loopback IPv4, or localhost if none + let bind_ip = match enumerate_ipv4_non_loopback().into_iter().find(|&ip| ip != Ipv4Addr::UNSPECIFIED) { + Some(ip) => ip, + None => Ipv4Addr::LOCALHOST, + }; + // Bind UDP on chosen address and an ephemeral port + let udp = UdpSocket::bind(SocketAddr::new(IpAddr::V4(bind_ip), 0)).await?; + let local_udp = udp.local_addr()?; + eprintln!("UDP bound to: {}", local_udp); - let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); - let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + // Shared UDP socket for use in str0m loop + let std_udp = udp.into_std()?; + let std_udp_arc = Arc::new(std_udp); - // Build rustls server config (no client auth) - let config = ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(vec![cert], key) - .expect("invalid cert/key"); - let config = Arc::new(config); - - let listener = TcpListener::bind("127.0.0.1:8002").expect("bind 127.0.0.1:8002"); - eprintln!("tungstenite server listening on wss://127.0.0.1:8002"); - - for stream in listener.incoming() { - match stream { - Ok(stream) => { - eprintln!( - "incoming TCP connection from {}", - stream.peer_addr().unwrap() - ); - let cfg = Arc::clone(&config); - spawn(move || { - // Wrap TCP in a rustls TLS stream. - let conn = match ServerConnection::new(cfg) { - Ok(c) => c, - Err(e) => { - eprintln!("TLS ServerConnection error: {e}"); - return; + // TCP listener for WSS + let listener = TcpListener::bind("127.0.0.1:8002").await?; + eprintln!("Listening on wss://127.0.0.1:8002"); + + loop { + let (tcp, _) = listener.accept().await?; + let acceptor = acceptor.clone(); + let std_udp_arc_clone = Arc::clone(&std_udp_arc); + + tokio::spawn(async move { + if let Err(e) = handle_client(acceptor, tcp, std_udp_arc_clone, local_udp).await { + eprintln!("Client error: {:?}", e); + } + }); + } +} + +// Handle one WebSocket client +async fn handle_client( + acceptor: TlsAcceptor, + tcp: tokio::net::TcpStream, + std_udp_arc: Arc, + local_udp: SocketAddr, +) -> anyhow::Result<()> { + // Upgrade to TLS and WebSocket + let tls: TlsStream<_> = acceptor.accept(tcp).await?; + let mut ws = accept_async(tls).await?; + + // Expect initial offer + let first_msg = ws.next().await.ok_or_else(|| anyhow::anyhow!("client closed"))??; + let offer = match first_msg { + Message::Text(t) => serde_json::from_str::(&t)?, + Message::Binary(b) => serde_json::from_slice::(&b)?, + _ => anyhow::bail!("unexpected first WS message"), + }; + let offer_sdp = match offer { + WsInbound::Offer { sdp } => sdp, + _ => anyhow::bail!("first message must be an offer"), + }; + + // Channels for inter-thread communication + let (ws_to_rtc_tx, ws_to_rtc_rx) = cb::unbounded::(); + let (udp_to_rtc_tx, udp_to_rtc_rx) = cb::unbounded::(); + let (rtc_to_udp_tx, rtc_to_udp_rx) = cb::unbounded::(); + let (answer_tx, answer_rx) = cb::unbounded::(); + + { + let std_udp_send = Arc::clone(&std_udp_arc); + tokio::task::spawn_blocking(move || { + for out in rtc_to_udp_rx.iter() { + if let Err(e) = std_udp_send.send_to(&out.buf, out.dst) { + eprintln!("UDP send error: {}", e); + } + } + }); + } + + // UDP receiver task + { + let std_udp_recv = Arc::clone(&std_udp_arc); + let local_addr_for_recv = local_udp; + tokio::spawn(async move { + let udp_recv = tokio::net::UdpSocket::from_std(std_udp_recv.as_ref().try_clone().unwrap()).unwrap(); + let mut buf = vec![0u8; 2000]; + loop { + match udp_recv.recv_from(&mut buf).await { + Ok((n, src)) => { + let data = buf[..n].to_vec(); + let _ = udp_to_rtc_tx.send(UdpIn { buf: data, src, dst: local_addr_for_recv }); + } + Err(e) => { + eprintln!("UDP receive error: {e}"); + break; + } + } + } + }); + } + + // spawn the blocking WebRTC (str0m) worker + let offer_clone = offer_sdp.clone(); + let local_ip_for_rtc = local_udp.ip(); + let local_port_for_rtc = local_udp.port(); + tokio::task::spawn_blocking(move || { + let _ = run_rtc_blocking( + offer_clone, + local_ip_for_rtc, + local_port_for_rtc, + ws_to_rtc_rx, + udp_to_rtc_rx, + rtc_to_udp_tx, + answer_tx, + ); + }); + + // Wait for SDP answer from str0m + let answer = answer_rx.recv().unwrap(); + + // Send the SDP answer back to client + let answer_obj = serde_json::json!({ "type": "answer", "sdp": answer }); + let _ = ws.send(Message::Text(serde_json::to_string(&answer_obj)?.into())).await; + // Send end-of-candidates as well + let end_msg = WsOutbound::EndOfCandidates {}; + let _ = ws.send(Message::Text(serde_json::to_string(&end_msg)?.into())).await; + + // read remaining ICE candidates from WebSocket (trickle ICE) + let ws_to_rtc_tx_clone = ws_to_rtc_tx.clone(); + let mut ws_reader = ws; + tokio::spawn(async move { + while let Some(msg) = ws_reader.next().await { + match msg { + Ok(Message::Text(t)) => { + if let Ok(inb) = serde_json::from_str::(&t) { + let _ = ws_to_rtc_tx_clone.send(inb); + } + } + Ok(Message::Binary(b)) => { + if let Ok(inb) = serde_json::from_slice::(&b) { + let _ = ws_to_rtc_tx_clone.send(inb); + } + } + Ok(Message::Close(_)) | Err(_) => break, + _ => {} + } + } + }); + + Ok(()) +} + +// Data structures for UDP channel (no change) +#[derive(Debug)] +struct UdpIn { buf: Vec, src: SocketAddr, dst: SocketAddr } +#[derive(Debug)] +struct UdpOut { buf: Vec, dst: SocketAddr } + +// The str0m run-loop (blocking). Receives the local IP and port. +fn run_rtc_blocking( + offer_sdp: String, + local_ip: IpAddr, + udp_port: u16, + ws_in: cb::Receiver, + udp_in: cb::Receiver, + udp_out: cb::Sender, + answer_tx: cb::Sender, +) -> anyhow::Result<()> { + // Build str0m RTC with DTLS cert (unchanged)... + let cert_opts = str0m::config::DtlsCertOptions { + pkey_type: str0m::config::DtlsPKeyType::EcDsaP256, + ..Default::default() + }; + let crypto_provider = if cfg!(windows) { + str0m::config::CryptoProvider::WinCrypto + } else { + str0m::config::CryptoProvider::OpenSsl + }; + let dtls_cert = str0m::config::DtlsCert::new(crypto_provider, cert_opts); + let str0m_config = str0m::RtcConfig::new().set_dtls_cert_config(str0m::DtlsCertConfig::PregeneratedCert(dtls_cert)); + let mut rtc = str0m_config.build(); + + // Add local host candidate using the actual local IP + let local_addr = SocketAddr::new(local_ip, udp_port); + let local_candidate = Candidate::host(local_addr, "udp")?; + rtc.add_local_candidate(local_candidate).ok_or(anyhow::anyhow!("Failed to add local candidate"))?; + eprintln!("Added local candidate: {}", local_addr); + + // Parse offer SDP + let offer = str0m::change::SdpOffer::from_sdp_string(&offer_sdp)?; + + // Extract and add remote candidates from SDP + let mut remote_count = 0; + for line in offer_sdp.lines() { + if let Some(cand_line) = line.strip_prefix("a=candidate:") { + let parts: Vec<&str> = cand_line.split_whitespace().collect(); + if parts.len() >= 8 && parts[2] == "UDP" { + let ip_str = parts[4]; + let port_str = parts[5]; + if !ip_str.ends_with(".local") { + if let (Ok(ip), Ok(port)) = (ip_str.parse::(), port_str.parse::()) { + if !ip.is_unspecified() { + let addr = SocketAddr::new(ip, port); + if let Ok(c) = Candidate::host(addr, "udp") { + rtc.add_remote_candidate(c); + remote_count += 1; + eprintln!("Added remote candidate from SDP: {}", addr); + } } - }; - let tls_stream = StreamOwned::new(conn, stream); - - match accept(tls_stream) { - Ok(mut websocket) => loop { - if let Ok(msg) = websocket.read() { - eprintln!("Received message: {}", msg); - - // Parse remote SDP offer. - let offer: SdpOffer = serde_json::from_slice(&msg.into_data()) - .expect("serialized offer"); - - // Prepare UDP socket to carry ICE/DTLS/SCTP traffic. - let udp = UdpSocket::bind("0.0.0.0:0").expect("bind UDP"); - let local_addr = udp.local_addr().expect("local addr"); - eprintln!("UDP bound on {}", local_addr); - - // Build Rtc instance. - let cert_options = str0m::config::DtlsCertOptions { - pkey_type: str0m::config::DtlsPKeyType::EcDsaP256, - ..Default::default() - }; - let crypto_provider = if cfg!(windows) { - str0m::config::CryptoProvider::WinCrypto - } else { - str0m::config::CryptoProvider::OpenSsl - }; - let dtls_cert = - str0m::config::DtlsCert::new(crypto_provider, cert_options); - - let str0m_config = str0m::RtcConfig::new().set_dtls_cert_config( - str0m::DtlsCertConfig::PregeneratedCert(dtls_cert), - ); - - let mut rtc = str0m_config.build(); - - // Accept offer and create answer. - let answer = rtc - .sdp_api() - .accept_offer(offer) - .expect("offer to be accepted"); - - // Send SDP answer back to the client. - websocket - .write(answer.to_sdp_string().into()) - .expect("send answer"); - eprintln!("Sent SDP answer"); - - let socket = udp; - let buffer_size = 2000; - let mut buf = vec![0u8; buffer_size]; - loop { - // Poll outputs until timeout is returned. - let timeout_instant = loop { - match rtc.poll_output().expect("poll output") { - Output::Timeout(when) => break when, - Output::Transmit(tx) => { - let _ = - socket.send_to(&tx.contents, tx.destination); - } - Output::Event(ev) => { - eprintln!("RTC event: {:?}", ev); - if let Event::IceConnectionStateChange(state) = ev { - if state == IceConnectionState::Disconnected { - eprintln!("ICE disconnected"); - return; - } - } - } - } - }; - - // Compute duration until timeout. - let now = Instant::now(); - let duration = if timeout_instant > now { - timeout_instant - now - } else { - Duration::from_millis(0) - }; - - if duration.is_zero() { - // Drive timers immediately. - rtc.handle_input(Input::Timeout(Instant::now())) - .expect("timeout input"); - continue; - } + } + } + } + } + } + eprintln!("Total remote candidates added from offer: {}", remote_count); + + // Accept the offer to create answer SDP + let answer = rtc.sdp_api().accept_offer(offer)?; + let answer_sdp = answer.to_sdp_string(); + eprintln!("SDP answer generated"); + + // Initial ICE processing (to kick-start connectivity checks) + let mut done_initial = false; + while !done_initial { + match rtc.poll_output()? { + Output::Timeout(t) => { + if Instant::now() < t { + done_initial = true; + } else { + rtc.handle_input(Input::Timeout(Instant::now()))?; + } + } + Output::Transmit(tx) => { + udp_out.send(UdpOut { buf: tx.contents.to_vec(), dst: tx.destination })?; + } + Output::Event(ev) => { + match ev { + Event::IceConnectionStateChange(state) => { + eprintln!("ICE state after accept: {:?}", state); + } + _ => {} + } + } + } + } + + // Send the answer SDP back to the main thread for WebSocket delivery + answer_tx.send(answer_sdp)?; + + // Main loop: handle ICE, DataChannel, and trickled candidates + let mut seen_ws_candidates = std::collections::HashSet::new(); + loop { + // First, drain any str0m output until a timeout + let next_time = loop { + match rtc.poll_output()? { + Output::Timeout(t) => break t, + Output::Transmit(tx) => { + udp_out.send(UdpOut { buf: tx.contents.to_vec(), dst: tx.destination })?; + } + Output::Event(ev) => { + match &ev { + Event::Connected => { + eprintln!("WebRTC CONNECTED! ICE and DTLS established."); + } + Event::IceConnectionStateChange(state) => { + eprintln!("ICE state changed: {:?}", state); + if matches!(state, IceConnectionState::Disconnected) { + eprintln!("ICE disconnected, stopping."); + return Ok(()); + } + } + Event::ChannelOpen(id, label) => { + eprintln!("DataChannel opened: id={:?}, label={}", id, label); + // (Example: send messages on the new channel) + if let Some(mut channel) = rtc.channel(*id) { + for i in (10..=100).step_by(10) { + let msg = format!("Hello {}", i); + channel.write(false, msg.as_bytes()).unwrap(); + } + } + } + Event::ChannelData(data) => { + eprintln!("DataChannel received: {:?}", data); + } + Event::ChannelClose(id) => { + eprintln!("DataChannel closed: id={:?}", id); + } + _ => {} + } + } + } + }; - // run RTC input - let _ = socket.set_read_timeout(Some(duration)); - buf.resize(buffer_size, 0); - match socket.recv_from(&mut buf) { - Ok((n, src)) => { - buf.truncate(n); - let input = Input::Receive( - Instant::now(), - Receive { - proto: Protocol::Udp, - source: src, - destination: socket - .local_addr() - .expect("local addr"), - contents: buf - .as_slice() - .try_into() - .expect("slice"), - }, - ); - rtc.handle_input(input).expect("handle input"); - } - Err(e) - if e.kind() == ErrorKind::WouldBlock - || e.kind() == ErrorKind::TimedOut => - { - rtc.handle_input(Input::Timeout(Instant::now())) - .expect("timeout input"); - } - Err(e) => { - eprintln!("UDP error: {e}"); - return; - } + // Handle any incoming WS (trickle ICE) messages + while let Ok(ws_msg) = ws_in.try_recv() { + match ws_msg { + WsInbound::Candidate { candidate, .. } => { + // Parse and add remote candidate from WebSocket + let candidate_line = candidate + .strip_prefix("a=") + .or_else(|| candidate.strip_prefix("candidate:")) + .unwrap_or(&candidate); + let parts: Vec<&str> = candidate_line.split_whitespace().collect(); + if parts.len() >= 8 && parts[2] == "UDP" { + if let (Ok(ip), Ok(port)) = (parts[4].parse::(), parts[5].parse::()) { + if !ip.is_unspecified() { + let addr = SocketAddr::new(ip, port); + if !seen_ws_candidates.contains(&addr) { + if let Ok(c) = Candidate::host(addr, "udp") { + rtc.add_remote_candidate(c); + seen_ws_candidates.insert(addr); + eprintln!("Added remote candidate from WS: {}", addr); } } - } else { - eprintln!("Client disconnected"); - break; } - }, - Err(e) => { - eprintln!("websocket handshake failed: {e}"); - eprintln!( - "Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/." - ); } } - }); + } + WsInbound::EndOfCandidates { .. } => { + // No action needed; str0m handles end-of-candidates internally + } + _ => {} } - Err(e) => eprintln!("incoming connection error: {e}"), + } + + // Handle any incoming UDP packets + while let Ok(UdpIn { buf, src, dst }) = udp_in.try_recv() { + let input = Input::Receive( + Instant::now(), + Receive { + proto: Protocol::Udp, + source: src, + destination: dst, + contents: buf.as_slice().try_into().unwrap(), + }, + ); + rtc.handle_input(input)?; + } + + // Wait until next timeout + let now = Instant::now(); + if now < next_time { + std::thread::sleep((next_time - now).min(Duration::from_millis(1))); + rtc.handle_input(Input::Timeout(Instant::now()))?; + } else { + rtc.handle_input(Input::Timeout(Instant::now()))?; } } } From 3879a448c19269488b298799e8188aee59ee17b8 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Wed, 5 Nov 2025 22:02:16 -0800 Subject: [PATCH 24/39] Fix windows dependency --- webrtc/str0m-server/Cargo.lock | 120 +++++++++++++++++++++++++++++--- webrtc/str0m-server/Cargo.toml | 2 +- webrtc/str0m-server/src/main.rs | 4 +- 3 files changed, 115 insertions(+), 11 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index e895b1a..4236fce 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -859,7 +859,7 @@ dependencies = [ "tracing", "tracing-subscriber", "winapi", - "windows-sys 0.61.2", + "windows 0.62.2", ] [[package]] @@ -869,7 +869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "457d36c2642d30dc703e84a1eadd04d62af7d46c821da3937040eea8a52395fd" dependencies = [ "tracing", - "windows", + "windows 0.58.0", ] [[package]] @@ -1130,23 +1130,68 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core", + "windows-core 0.58.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core 0.62.2", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -1158,6 +1203,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -1169,12 +1225,33 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -1184,16 +1261,34 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -1263,6 +1358,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index 0ce96ea..f60e7fc 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -17,7 +17,7 @@ serde_json = "1.0.145" tokio = { version = "1.48.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] } tokio-rustls = "0.26.4" tokio-tungstenite = { version = "0.28.0", features = ["tokio-rustls"] } -windows-sys = { version = "0.61.2", features = ["Win32_Media_Multimedia"] } +windows = { version = "0.62.2", features = ["Win32_Media_Multimedia"] } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 970449a..e95590d 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -77,14 +77,14 @@ struct TimerPeriodGuard; #[cfg(target_os = "windows")] impl TimerPeriodGuard { fn new_1ms() -> Self { - unsafe { windows_sys::Win32::Media::Multimedia::timeBeginPeriod(1); } + unsafe { windows::Win32::Media::timeBeginPeriod(1); } TimerPeriodGuard } } #[cfg(target_os = "windows")] impl Drop for TimerPeriodGuard { fn drop(&mut self) { - unsafe { windows_sys::Win32::Media::Multimedia::timeEndPeriod(1); } + unsafe { windows::Win32::Media::timeEndPeriod(1); } } } From dee5f5aafe9cb46ec3505480f5fa77fa3f003cd9 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 13:22:10 +0200 Subject: [PATCH 25/39] Fix str0m on windows --- webrtc/str0m-server/Cargo.lock | 20 ++ webrtc/str0m-server/Cargo.toml | 4 +- webrtc/str0m-server/src/main.rs | 332 +++++++++++++++----------------- 3 files changed, 173 insertions(+), 183 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 4236fce..357b9ea 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -421,6 +421,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libmimalloc-sys" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "log" version = "0.4.28" @@ -442,6 +452,15 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "mimalloc" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" +dependencies = [ + "libmimalloc-sys", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -847,6 +866,7 @@ dependencies = [ "crossbeam-channel", "futures-util", "if-addrs", + "mimalloc", "rustls", "rustls-pemfile", "rustls-pki-types", diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index f60e7fc..96af565 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -9,6 +9,7 @@ bytes = "1.10.1" crossbeam-channel = "0.5.15" futures-util = "0.3.31" if-addrs = "0.14.0" +mimalloc = { version = "0.1", default-features = false } rustls = "0.23.35" rustls-pemfile = "2.2.0" rustls-pki-types = "1.13.0" @@ -17,9 +18,10 @@ serde_json = "1.0.145" tokio = { version = "1.48.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] } tokio-rustls = "0.26.4" tokio-tungstenite = { version = "0.28.0", features = ["tokio-rustls"] } -windows = { version = "0.62.2", features = ["Win32_Media_Multimedia"] } + [target.'cfg(windows)'.dependencies] +windows = { version = "0.62.2", features = ["Win32_Media_Multimedia"] } winapi = { version = "0.3", features = ["winbase"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index e95590d..39b9f29 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -1,5 +1,6 @@ -// Cargo.toml (dependencies omitted for brevity)... - +// basic str0m implementation for testing purposes. +// uses host candidates, expected to be used in a local network. +// Should work on windows and linux. use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::Path; use std::sync::Arc; @@ -16,6 +17,9 @@ use tokio_tungstenite::{accept_async, tungstenite::Message}; use str0m::net::{Protocol, Receive}; use str0m::{Candidate, Event, IceConnectionState, Input, Output}; +#[global_allocator] +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + #[derive(Deserialize, Debug)] #[serde(tag = "type")] enum WsInbound { @@ -53,23 +57,11 @@ enum WsOutbound { EndOfCandidates {}, } -// Enumerate non-loopback IPv4 addresse -#[allow(dead_code)] -fn enumerate_ipv4_non_loopback() -> Vec { - let mut v = Vec::new(); - if let Ok(ifs) = if_addrs::get_if_addrs() { - for i in ifs { - if let IpAddr::V4(ip) = i.ip() { - if !ip.is_loopback() && !ip.is_link_local() { - v.push(ip); - } - } - } - } - if v.is_empty() { - v.push(Ipv4Addr::UNSPECIFIED); - } - v +#[derive(Debug)] +struct UdpIn { + buf: Vec, + src: SocketAddr, + dst: SocketAddr, } #[cfg(target_os = "windows")] @@ -88,20 +80,39 @@ impl Drop for TimerPeriodGuard { } } +// Enumerate non-loopback IPv4s +fn enumerate_ipv4_non_loopback() -> Vec { + let mut v = Vec::new(); + if let Ok(ifs) = if_addrs::get_if_addrs() { + for i in ifs { + if let IpAddr::V4(ip) = i.ip() { + if !ip.is_loopback() && !ip.is_link_local() { + v.push(ip); + } + } + } + } + if v.is_empty() { + v.push(Ipv4Addr::UNSPECIFIED); + } + v +} + #[tokio::main] async fn main() -> anyhow::Result<()> { #[cfg(target_os = "windows")] let _timer_guard = TimerPeriodGuard::new_1ms(); - // TLS setup omitted for brevity... let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); let cert_pem = std::fs::read(cert_path)?; let key_pem = std::fs::read(key_path)?; - let cert = rustls_pemfile::certs(&mut &cert_pem[..]).next() + let cert = rustls_pemfile::certs(&mut &cert_pem[..]) + .next() .ok_or_else(|| anyhow::anyhow!("no certificate found"))? .map(CertificateDer::from)?; - let key = rustls_pemfile::pkcs8_private_keys(&mut &key_pem[..]).next() + let key = rustls_pemfile::pkcs8_private_keys(&mut &key_pem[..]) + .next() .ok_or_else(|| anyhow::anyhow!("no private key found"))? .map(PrivateKeyDer::from)?; let rustls_cfg = rustls::ServerConfig::builder() @@ -109,92 +120,83 @@ async fn main() -> anyhow::Result<()> { .with_single_cert(vec![cert], key)?; let acceptor = TlsAcceptor::from(Arc::new(rustls_cfg)); - // Choose a bind address: first non-loopback IPv4, or localhost if none - let bind_ip = match enumerate_ipv4_non_loopback().into_iter().find(|&ip| ip != Ipv4Addr::UNSPECIFIED) { - Some(ip) => ip, - None => Ipv4Addr::LOCALHOST, - }; - // Bind UDP on chosen address and an ephemeral port - let udp = UdpSocket::bind(SocketAddr::new(IpAddr::V4(bind_ip), 0)).await?; - let local_udp = udp.local_addr()?; - eprintln!("UDP bound to: {}", local_udp); - - // Shared UDP socket for use in str0m loop - let std_udp = udp.into_std()?; - let std_udp_arc = Arc::new(std_udp); - - // TCP listener for WSS let listener = TcpListener::bind("127.0.0.1:8002").await?; eprintln!("Listening on wss://127.0.0.1:8002"); loop { - let (tcp, _) = listener.accept().await?; + let (tcp, peer_addr) = listener.accept().await?; let acceptor = acceptor.clone(); - let std_udp_arc_clone = Arc::clone(&std_udp_arc); + + let bind_ip = if peer_addr.ip().is_loopback() { + Ipv4Addr::LOCALHOST + } else { + enumerate_ipv4_non_loopback() + .into_iter() + .find(|ip| *ip != Ipv4Addr::UNSPECIFIED) + .unwrap_or(Ipv4Addr::LOCALHOST) + }; + + let udp = UdpSocket::bind(SocketAddr::new(IpAddr::V4(bind_ip), 0)).await?; + let local_udp = udp.local_addr()?; + eprintln!("UDP bound to: {}", local_udp); + + let std_udp = udp.into_std()?; + let std_udp_arc = Arc::new(std_udp); tokio::spawn(async move { - if let Err(e) = handle_client(acceptor, tcp, std_udp_arc_clone, local_udp).await { - eprintln!("Client error: {:?}", e); + if let Err(e) = handle_client(acceptor, tcp, std_udp_arc, local_udp).await { + eprintln!("client error: {e:?}"); } }); } } -// Handle one WebSocket client async fn handle_client( acceptor: TlsAcceptor, tcp: tokio::net::TcpStream, std_udp_arc: Arc, local_udp: SocketAddr, ) -> anyhow::Result<()> { - // Upgrade to TLS and WebSocket let tls: TlsStream<_> = acceptor.accept(tcp).await?; let mut ws = accept_async(tls).await?; // Expect initial offer - let first_msg = ws.next().await.ok_or_else(|| anyhow::anyhow!("client closed"))??; - let offer = match first_msg { + let first = ws.next().await.ok_or_else(|| anyhow::anyhow!("client closed"))??; + let offer = match first { Message::Text(t) => serde_json::from_str::(&t)?, Message::Binary(b) => serde_json::from_slice::(&b)?, - _ => anyhow::bail!("unexpected first WS message"), + _ => anyhow::bail!("unexpected first WS frame"), }; let offer_sdp = match offer { WsInbound::Offer { sdp } => sdp, - _ => anyhow::bail!("first message must be an offer"), + _ => anyhow::bail!("first WS message must be an offer"), }; - // Channels for inter-thread communication + // Channels let (ws_to_rtc_tx, ws_to_rtc_rx) = cb::unbounded::(); let (udp_to_rtc_tx, udp_to_rtc_rx) = cb::unbounded::(); - let (rtc_to_udp_tx, rtc_to_udp_rx) = cb::unbounded::(); let (answer_tx, answer_rx) = cb::unbounded::(); - { - let std_udp_send = Arc::clone(&std_udp_arc); - tokio::task::spawn_blocking(move || { - for out in rtc_to_udp_rx.iter() { - if let Err(e) = std_udp_send.send_to(&out.buf, out.dst) { - eprintln!("UDP send error: {}", e); - } - } - }); - } - // UDP receiver task { let std_udp_recv = Arc::clone(&std_udp_arc); - let local_addr_for_recv = local_udp; + let local_udp_for_recv = local_udp; tokio::spawn(async move { - let udp_recv = tokio::net::UdpSocket::from_std(std_udp_recv.as_ref().try_clone().unwrap()).unwrap(); + let udp_recv = + tokio::net::UdpSocket::from_std(std_udp_recv.as_ref().try_clone().unwrap()).unwrap(); let mut buf = vec![0u8; 2000]; loop { match udp_recv.recv_from(&mut buf).await { Ok((n, src)) => { - let data = buf[..n].to_vec(); - let _ = udp_to_rtc_tx.send(UdpIn { buf: data, src, dst: local_addr_for_recv }); + let v = buf[..n].to_vec(); + let _ = udp_to_rtc_tx.send(UdpIn { + buf: v, + src, + dst: local_udp_for_recv, + }); } Err(e) => { - eprintln!("UDP receive error: {e}"); + eprintln!("udp recv error: {e}"); break; } } @@ -202,46 +204,41 @@ async fn handle_client( }); } - // spawn the blocking WebRTC (str0m) worker - let offer_clone = offer_sdp.clone(); - let local_ip_for_rtc = local_udp.ip(); - let local_port_for_rtc = local_udp.port(); + // Spawn RTC blocking worker; pass the arced socket so it can send directly (no extra send task) + let offer = offer_sdp.clone(); + let std_udp_for_rtc = Arc::clone(&std_udp_arc); tokio::task::spawn_blocking(move || { let _ = run_rtc_blocking( - offer_clone, - local_ip_for_rtc, - local_port_for_rtc, + offer, + local_udp.ip(), + local_udp.port(), ws_to_rtc_rx, udp_to_rtc_rx, - rtc_to_udp_tx, + std_udp_for_rtc, // <— direct sender answer_tx, ); }); - // Wait for SDP answer from str0m + // Wait for answer let answer = answer_rx.recv().unwrap(); - // Send the SDP answer back to client + // Send the SDP answer back to the client let answer_obj = serde_json::json!({ "type": "answer", "sdp": answer }); let _ = ws.send(Message::Text(serde_json::to_string(&answer_obj)?.into())).await; - // Send end-of-candidates as well - let end_msg = WsOutbound::EndOfCandidates {}; - let _ = ws.send(Message::Text(serde_json::to_string(&end_msg)?.into())).await; - // read remaining ICE candidates from WebSocket (trickle ICE) let ws_to_rtc_tx_clone = ws_to_rtc_tx.clone(); - let mut ws_reader = ws; + let mut ws_read = ws; tokio::spawn(async move { - while let Some(msg) = ws_reader.next().await { + while let Some(msg) = ws_read.next().await { match msg { Ok(Message::Text(t)) => { - if let Ok(inb) = serde_json::from_str::(&t) { - let _ = ws_to_rtc_tx_clone.send(inb); + if let Ok(v) = serde_json::from_str::(&t) { + let _ = ws_to_rtc_tx_clone.send(v); } } Ok(Message::Binary(b)) => { - if let Ok(inb) = serde_json::from_slice::(&b) { - let _ = ws_to_rtc_tx_clone.send(inb); + if let Ok(v) = serde_json::from_slice::(&b) { + let _ = ws_to_rtc_tx_clone.send(v); } } Ok(Message::Close(_)) | Err(_) => break, @@ -253,24 +250,17 @@ async fn handle_client( Ok(()) } -// Data structures for UDP channel (no change) -#[derive(Debug)] -struct UdpIn { buf: Vec, src: SocketAddr, dst: SocketAddr } -#[derive(Debug)] -struct UdpOut { buf: Vec, dst: SocketAddr } - -// The str0m run-loop (blocking). Receives the local IP and port. fn run_rtc_blocking( offer_sdp: String, local_ip: IpAddr, udp_port: u16, ws_in: cb::Receiver, udp_in: cb::Receiver, - udp_out: cb::Sender, + udp_sock: Arc, // <— direct sender answer_tx: cb::Sender, ) -> anyhow::Result<()> { - // Build str0m RTC with DTLS cert (unchanged)... - let cert_opts = str0m::config::DtlsCertOptions { + use std::collections::HashSet; + let cert_options = str0m::config::DtlsCertOptions { pkey_type: str0m::config::DtlsPKeyType::EcDsaP256, ..Default::default() }; @@ -279,86 +269,63 @@ fn run_rtc_blocking( } else { str0m::config::CryptoProvider::OpenSsl }; - let dtls_cert = str0m::config::DtlsCert::new(crypto_provider, cert_opts); - let str0m_config = str0m::RtcConfig::new().set_dtls_cert_config(str0m::DtlsCertConfig::PregeneratedCert(dtls_cert)); + let dtls_cert = str0m::config::DtlsCert::new(crypto_provider, cert_options); + let str0m_config = str0m::RtcConfig::new() + .set_dtls_cert_config(str0m::DtlsCertConfig::PregeneratedCert(dtls_cert)); + let mut rtc = str0m_config.build(); - // Add local host candidate using the actual local IP let local_addr = SocketAddr::new(local_ip, udp_port); - let local_candidate = Candidate::host(local_addr, "udp")?; - rtc.add_local_candidate(local_candidate).ok_or(anyhow::anyhow!("Failed to add local candidate"))?; - eprintln!("Added local candidate: {}", local_addr); + let localhost_cand = Candidate::host(local_addr, "udp")?; + rtc + .add_local_candidate(localhost_cand) + .ok_or_else(|| anyhow::anyhow!("failed to add local candidate"))?; + eprintln!("Added local candidate: {local_addr}"); - // Parse offer SDP let offer = str0m::change::SdpOffer::from_sdp_string(&offer_sdp)?; - // Extract and add remote candidates from SDP - let mut remote_count = 0; - for line in offer_sdp.lines() { - if let Some(cand_line) = line.strip_prefix("a=candidate:") { - let parts: Vec<&str> = cand_line.split_whitespace().collect(); - if parts.len() >= 8 && parts[2] == "UDP" { - let ip_str = parts[4]; - let port_str = parts[5]; - if !ip_str.ends_with(".local") { - if let (Ok(ip), Ok(port)) = (ip_str.parse::(), port_str.parse::()) { - if !ip.is_unspecified() { - let addr = SocketAddr::new(ip, port); - if let Ok(c) = Candidate::host(addr, "udp") { - rtc.add_remote_candidate(c); - remote_count += 1; - eprintln!("Added remote candidate from SDP: {}", addr); - } - } - } - } - } - } - } - eprintln!("Total remote candidates added from offer: {}", remote_count); - - // Accept the offer to create answer SDP - let answer = rtc.sdp_api().accept_offer(offer)?; - let answer_sdp = answer.to_sdp_string(); + let answer = rtc.sdp_api().accept_offer(offer)?.to_sdp_string(); eprintln!("SDP answer generated"); - // Initial ICE processing (to kick-start connectivity checks) - let mut done_initial = false; - while !done_initial { + loop { match rtc.poll_output()? { - Output::Timeout(t) => { - if Instant::now() < t { - done_initial = true; + Output::Timeout(when) => { + let now = Instant::now(); + if now >= when { + rtc.handle_input(Input::Timeout(now))?; } else { - rtc.handle_input(Input::Timeout(Instant::now()))?; + break; } } Output::Transmit(tx) => { - udp_out.send(UdpOut { buf: tx.contents.to_vec(), dst: tx.destination })?; + // Send directly on the arced socket + let _ = udp_sock.send_to(&tx.contents, tx.destination); } Output::Event(ev) => { - match ev { - Event::IceConnectionStateChange(state) => { - eprintln!("ICE state after accept: {:?}", state); - } - _ => {} + if let Event::IceConnectionStateChange(st) = &ev { + eprintln!("ICE state after accept: {st:?}"); } } } } - // Send the answer SDP back to the main thread for WebSocket delivery - answer_tx.send(answer_sdp)?; + // Provide answer to WS task + let _ = answer_tx.send(answer); + + let mut seen_ws_candidates = HashSet::::new(); + + // Optional: track a “selected” remote addr once we see inbound packets + let mut likely_selected_remote: Option = None; - // Main loop: handle ICE, DataChannel, and trickled candidates - let mut seen_ws_candidates = std::collections::HashSet::new(); loop { - // First, drain any str0m output until a timeout - let next_time = loop { + // Drain str0m outputs until timeout + let next_when = loop { match rtc.poll_output()? { - Output::Timeout(t) => break t, + Output::Timeout(when) => break when, Output::Transmit(tx) => { - udp_out.send(UdpOut { buf: tx.contents.to_vec(), dst: tx.destination })?; + // During early ICE, sends to “other” pairs can legitimately fail on Windows. + // We best-effort send and ignore OS error 10051 noise. + let _ = udp_sock.send_to(&tx.contents, tx.destination); } Output::Event(ev) => { match &ev { @@ -366,27 +333,23 @@ fn run_rtc_blocking( eprintln!("WebRTC CONNECTED! ICE and DTLS established."); } Event::IceConnectionStateChange(state) => { - eprintln!("ICE state changed: {:?}", state); + eprintln!("ICE state changed: {state:?}"); if matches!(state, IceConnectionState::Disconnected) { - eprintln!("ICE disconnected, stopping."); + eprintln!("ICE DISCONNECTED - stopping."); return Ok(()); } } Event::ChannelOpen(id, label) => { - eprintln!("DataChannel opened: id={:?}, label={}", id, label); - // (Example: send messages on the new channel) - if let Some(mut channel) = rtc.channel(*id) { - for i in (10..=100).step_by(10) { - let msg = format!("Hello {}", i); - channel.write(false, msg.as_bytes()).unwrap(); + eprintln!("DataChannel opened: id={id:?}, label={label}"); + if let Some(mut ch) = rtc.channel(*id) { + // send a couple of probes + for i in 0..5 { + let _ = ch.write(true, format!("hi {i}").as_bytes()); } } } Event::ChannelData(data) => { - eprintln!("DataChannel received: {:?}", data); - } - Event::ChannelClose(id) => { - eprintln!("DataChannel closed: id={:?}", id); + eprintln!("DataChannel received {} bytes", data.data.len()); } _ => {} } @@ -394,40 +357,39 @@ fn run_rtc_blocking( } }; - // Handle any incoming WS (trickle ICE) messages - while let Ok(ws_msg) = ws_in.try_recv() { - match ws_msg { + // drain WS + while let Ok(msg) = ws_in.try_recv() { + match msg { WsInbound::Candidate { candidate, .. } => { - // Parse and add remote candidate from WebSocket let candidate_line = candidate .strip_prefix("a=") .or_else(|| candidate.strip_prefix("candidate:")) .unwrap_or(&candidate); let parts: Vec<&str> = candidate_line.split_whitespace().collect(); - if parts.len() >= 8 && parts[2] == "UDP" { - if let (Ok(ip), Ok(port)) = (parts[4].parse::(), parts[5].parse::()) { + if parts.len() >= 8 && parts[2].eq_ignore_ascii_case("udp") { + if let (Ok(ip), Ok(port)) = + (parts[4].parse::(), parts[5].parse::()) + { if !ip.is_unspecified() { let addr = SocketAddr::new(ip, port); - if !seen_ws_candidates.contains(&addr) { + if seen_ws_candidates.insert(addr) { if let Ok(c) = Candidate::host(addr, "udp") { - rtc.add_remote_candidate(c); - seen_ws_candidates.insert(addr); - eprintln!("Added remote candidate from WS: {}", addr); + let _ = rtc.add_remote_candidate(c); } } } } } } - WsInbound::EndOfCandidates { .. } => { - // No action needed; str0m handles end-of-candidates internally - } - _ => {} + WsInbound::EndOfCandidates {} => { } + WsInbound::Offer { .. } => { } } } - // Handle any incoming UDP packets + // drain UDP + let mut fed_any = false; while let Ok(UdpIn { buf, src, dst }) = udp_in.try_recv() { + likely_selected_remote.get_or_insert(src); let input = Input::Receive( Instant::now(), Receive { @@ -438,14 +400,20 @@ fn run_rtc_blocking( }, ); rtc.handle_input(input)?; + fed_any = true; + } + if fed_any { + // after receive bursts, we may get immediate outputs? + continue; } - // Wait until next timeout + // wait/sleep until next timeout let now = Instant::now(); - if now < next_time { - std::thread::sleep((next_time - now).min(Duration::from_millis(1))); - rtc.handle_input(Input::Timeout(Instant::now()))?; + if now >= next_when { + rtc.handle_input(Input::Timeout(now))?; } else { + let sleep = (next_when - now).min(Duration::from_millis(1)); + std::thread::sleep(sleep); rtc.handle_input(Input::Timeout(Instant::now()))?; } } From 67d9b092b9efed947175dcfaa0e5b4529c7b4fcd Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 13:45:20 +0200 Subject: [PATCH 26/39] Remove some hacks --- webrtc/str0m-server/Cargo.toml | 3 +++ webrtc/str0m-server/src/main.rs | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index 96af565..c9322d3 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -19,6 +19,9 @@ tokio = { version = "1.48.0", features = ["macros", "net", "rt", "rt-multi-threa tokio-rustls = "0.26.4" tokio-tungstenite = { version = "0.28.0", features = ["tokio-rustls"] } +[features] +mimalloc = [] + [target.'cfg(windows)'.dependencies] windows = { version = "0.62.2", features = ["Win32_Media_Multimedia"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 39b9f29..0aee9f2 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -17,8 +17,12 @@ use tokio_tungstenite::{accept_async, tungstenite::Message}; use str0m::net::{Protocol, Receive}; use str0m::{Candidate, Event, IceConnectionState, Input, Output}; +#[cfg(all(target_os = "windows", feature = "mimalloc"))] +use mimalloc::MiMalloc; + +#[cfg(all(target_os = "windows", feature = "mimalloc"))] #[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +static GLOBAL: MiMalloc = MiMalloc; #[derive(Deserialize, Debug)] #[serde(tag = "type")] @@ -314,9 +318,6 @@ fn run_rtc_blocking( let mut seen_ws_candidates = HashSet::::new(); - // Optional: track a “selected” remote addr once we see inbound packets - let mut likely_selected_remote: Option = None; - loop { // Drain str0m outputs until timeout let next_when = loop { @@ -389,7 +390,6 @@ fn run_rtc_blocking( // drain UDP let mut fed_any = false; while let Ok(UdpIn { buf, src, dst }) = udp_in.try_recv() { - likely_selected_remote.get_or_insert(src); let input = Input::Receive( Instant::now(), Receive { From 96db20a925ee3c274881ce6d4eb198202acdac61 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 18:50:38 +0200 Subject: [PATCH 27/39] Refactor and fixes --- webrtc/str0m-server/Cargo.lock | 1 + webrtc/str0m-server/Cargo.toml | 2 +- webrtc/str0m-server/src/main.rs | 650 +++++++++++++++++++------------- 3 files changed, 392 insertions(+), 261 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 357b9ea..009a13e 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -689,6 +689,7 @@ dependencies = [ "aws-lc-rs", "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index c9322d3..f5fdc9f 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -10,7 +10,7 @@ crossbeam-channel = "0.5.15" futures-util = "0.3.31" if-addrs = "0.14.0" mimalloc = { version = "0.1", default-features = false } -rustls = "0.23.35" +rustls = { version = "0.23.35", features = ["ring"] } rustls-pemfile = "2.2.0" rustls-pki-types = "1.13.0" serde = { version = "1.0.228", features = ["derive"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 0aee9f2..f765557 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -1,6 +1,7 @@ -// basic str0m implementation for testing purposes. -// uses host candidates, expected to be used in a local network. -// Should work on windows and linux. +// str0m test server with a single tokio::select! loop (WS, UDP, timer) +// should keeps the WebSocket/TLS flow unchanged as other clients. tested on Windows & Linux by. +// doesn't work on firefox on windows (dtls handshake fails). +use std::error::Error; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::Path; use std::sync::Arc; @@ -8,22 +9,21 @@ use std::time::{Duration, Instant}; use crossbeam_channel as cb; use futures_util::{SinkExt, StreamExt}; +use if_addrs::get_if_addrs; +use mimalloc::MiMalloc; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use serde::{Deserialize, Serialize}; +use str0m::net::{Protocol, Receive}; +use str0m::{Candidate, Event, Input, Output}; use tokio::net::{TcpListener, UdpSocket}; +use tokio::time::{sleep_until, Instant as TokioInstant}; use tokio_rustls::{server::TlsStream, TlsAcceptor}; use tokio_tungstenite::{accept_async, tungstenite::Message}; -use str0m::net::{Protocol, Receive}; -use str0m::{Candidate, Event, IceConnectionState, Input, Output}; - -#[cfg(all(target_os = "windows", feature = "mimalloc"))] -use mimalloc::MiMalloc; - -#[cfg(all(target_os = "windows", feature = "mimalloc"))] #[global_allocator] static GLOBAL: MiMalloc = MiMalloc; + #[derive(Deserialize, Debug)] #[serde(tag = "type")] enum WsInbound { @@ -61,51 +61,10 @@ enum WsOutbound { EndOfCandidates {}, } -#[derive(Debug)] -struct UdpIn { - buf: Vec, - src: SocketAddr, - dst: SocketAddr, -} - -#[cfg(target_os = "windows")] -struct TimerPeriodGuard; -#[cfg(target_os = "windows")] -impl TimerPeriodGuard { - fn new_1ms() -> Self { - unsafe { windows::Win32::Media::timeBeginPeriod(1); } - TimerPeriodGuard - } -} -#[cfg(target_os = "windows")] -impl Drop for TimerPeriodGuard { - fn drop(&mut self) { - unsafe { windows::Win32::Media::timeEndPeriod(1); } - } -} - -// Enumerate non-loopback IPv4s -fn enumerate_ipv4_non_loopback() -> Vec { - let mut v = Vec::new(); - if let Ok(ifs) = if_addrs::get_if_addrs() { - for i in ifs { - if let IpAddr::V4(ip) = i.ip() { - if !ip.is_loopback() && !ip.is_link_local() { - v.push(ip); - } - } - } - } - if v.is_empty() { - v.push(Ipv4Addr::UNSPECIFIED); - } - v -} - #[tokio::main] async fn main() -> anyhow::Result<()> { - #[cfg(target_os = "windows")] - let _timer_guard = TimerPeriodGuard::new_1ms(); + rustls::crypto::CryptoProvider::install_default(rustls::crypto::ring::default_provider()) + .map_err(|_| anyhow::anyhow!("Failed to install rustls ring provider"))?; let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); @@ -128,293 +87,464 @@ async fn main() -> anyhow::Result<()> { eprintln!("Listening on wss://127.0.0.1:8002"); loop { - let (tcp, peer_addr) = listener.accept().await?; + let (tcp, _) = listener.accept().await?; let acceptor = acceptor.clone(); - let bind_ip = if peer_addr.ip().is_loopback() { - Ipv4Addr::LOCALHOST - } else { - enumerate_ipv4_non_loopback() - .into_iter() - .find(|ip| *ip != Ipv4Addr::UNSPECIFIED) - .unwrap_or(Ipv4Addr::LOCALHOST) - }; - - let udp = UdpSocket::bind(SocketAddr::new(IpAddr::V4(bind_ip), 0)).await?; - let local_udp = udp.local_addr()?; - eprintln!("UDP bound to: {}", local_udp); - - let std_udp = udp.into_std()?; - let std_udp_arc = Arc::new(std_udp); - tokio::spawn(async move { - if let Err(e) = handle_client(acceptor, tcp, std_udp_arc, local_udp).await { - eprintln!("client error: {e:?}"); + if let Err(e) = handle_client(acceptor, tcp).await { + eprintln!("Client error: {e:?}"); } }); } } +// This function handles one WebRTC connection from start to finish async fn handle_client( acceptor: TlsAcceptor, tcp: tokio::net::TcpStream, - std_udp_arc: Arc, - local_udp: SocketAddr, ) -> anyhow::Result<()> { let tls: TlsStream<_> = acceptor.accept(tcp).await?; let mut ws = accept_async(tls).await?; - // Expect initial offer - let first = ws.next().await.ok_or_else(|| anyhow::anyhow!("client closed"))??; - let offer = match first { + let first_msg = ws.next().await.ok_or_else(|| anyhow::anyhow!("client closed"))??; + let offer = match first_msg { Message::Text(t) => serde_json::from_str::(&t)?, Message::Binary(b) => serde_json::from_slice::(&b)?, - _ => anyhow::bail!("unexpected first WS frame"), + _ => anyhow::bail!("unexpected first WS message"), }; let offer_sdp = match offer { WsInbound::Offer { sdp } => sdp, - _ => anyhow::bail!("first WS message must be an offer"), + _ => anyhow::bail!("first message must be an offer"), }; - // Channels + let bind_ip = pick_bind_v4_for_offer(&offer_sdp); + + let udp = UdpSocket::bind(SocketAddr::new(IpAddr::V4(bind_ip), 0)).await?; + let local_udp = udp.local_addr()?; + eprintln!("UDP bound to: {local_udp}"); + + let std_udp = udp.into_std()?; + let std_udp_arc = Arc::new(std_udp); + let (ws_to_rtc_tx, ws_to_rtc_rx) = cb::unbounded::(); let (udp_to_rtc_tx, udp_to_rtc_rx) = cb::unbounded::(); + let (timeout_tx, timeout_rx) = cb::unbounded::<()>(); + let (next_to_async_tx, next_to_async_rx) = cb::unbounded::(); let (answer_tx, answer_rx) = cb::unbounded::(); - // UDP receiver task - { - let std_udp_recv = Arc::clone(&std_udp_arc); - let local_udp_for_recv = local_udp; - tokio::spawn(async move { - let udp_recv = - tokio::net::UdpSocket::from_std(std_udp_recv.as_ref().try_clone().unwrap()).unwrap(); - let mut buf = vec![0u8; 2000]; - loop { - match udp_recv.recv_from(&mut buf).await { - Ok((n, src)) => { - let v = buf[..n].to_vec(); - let _ = udp_to_rtc_tx.send(UdpIn { - buf: v, - src, - dst: local_udp_for_recv, - }); - } - Err(e) => { - eprintln!("udp recv error: {e}"); - break; - } - } - } - }); - } - - // Spawn RTC blocking worker; pass the arced socket so it can send directly (no extra send task) - let offer = offer_sdp.clone(); let std_udp_for_rtc = Arc::clone(&std_udp_arc); + let offer_for_rtc = offer_sdp.clone(); tokio::task::spawn_blocking(move || { - let _ = run_rtc_blocking( - offer, - local_udp.ip(), + let _ = run_rtc_thread( + offer_for_rtc, + IpAddr::V4(bind_ip), local_udp.port(), ws_to_rtc_rx, udp_to_rtc_rx, - std_udp_for_rtc, // <— direct sender + timeout_rx, + next_to_async_tx, answer_tx, + std_udp_for_rtc, ); }); - // Wait for answer - let answer = answer_rx.recv().unwrap(); + let answer = tokio::task::spawn_blocking(move || answer_rx.recv()).await??; - // Send the SDP answer back to the client let answer_obj = serde_json::json!({ "type": "answer", "sdp": answer }); - let _ = ws.send(Message::Text(serde_json::to_string(&answer_obj)?.into())).await; - - let ws_to_rtc_tx_clone = ws_to_rtc_tx.clone(); - let mut ws_read = ws; - tokio::spawn(async move { - while let Some(msg) = ws_read.next().await { - match msg { - Ok(Message::Text(t)) => { - if let Ok(v) = serde_json::from_str::(&t) { - let _ = ws_to_rtc_tx_clone.send(v); + ws.send(Message::Text(serde_json::to_string(&answer_obj)?.into())) + .await?; + + let udp_recv = { + let cloned = std_udp_arc.as_ref().try_clone()?; + tokio::net::UdpSocket::from_std(cloned)? + }; + + let mut buf = vec![0u8; 2048]; + let local_addr_for_recv = SocketAddr::new(IpAddr::V4(bind_ip), local_udp.port()); + let mut next_deadline = Instant::now() + Duration::from_millis(50); + + loop { + if let Ok(t) = next_to_async_rx.try_recv() { + next_deadline = t; + } + + let mut ws_next = ws.next(); + let sleep_fut = sleep_until(TokioInstant::from_std(next_deadline)); + tokio::pin!(sleep_fut); + + tokio::select! { + // ws candidates + maybe_msg = &mut ws_next => { + match maybe_msg { + Some(Ok(Message::Text(t))) => { + if let Ok(inb) = serde_json::from_str::(&t) { let _ = ws_to_rtc_tx.send(inb); } + } + Some(Ok(Message::Binary(b))) => { + if let Ok(inb) = serde_json::from_slice::(&b) { let _ = ws_to_rtc_tx.send(inb); } } + Some(Ok(Message::Close(_))) | None | Some(Err(_)) => { break; } + _ => {} } - Ok(Message::Binary(b)) => { - if let Ok(v) = serde_json::from_slice::(&b) { - let _ = ws_to_rtc_tx_clone.send(v); + } + + // Handle incoming UDP packets (STUN, DTLS, media data) + recv = udp_recv.recv_from(&mut buf) => { + match recv { + Ok((n, src)) => { + if n > 0 { + let data = buf[..n].to_vec(); + let _ = udp_to_rtc_tx.send(UdpIn { buf: data, src, dst: local_addr_for_recv }); + } + } + Err(e) => { + if is_transient_udp_recv_err(&e) { /* ignore */ } + else { eprintln!("UDP receive error (continuing): {e}"); } } } - Ok(Message::Close(_)) | Err(_) => break, - _ => {} } + + // Timer tick - tell str0m to check for timeouts + _ = &mut sleep_fut => { let _ = timeout_tx.send(()); } } - }); + } Ok(()) } -fn run_rtc_blocking( +// Simple struct to hold information about incoming UDP packets +#[derive(Debug)] +struct UdpIn { + buf: Vec, + src: SocketAddr, + dst: SocketAddr, +} + +// Check if a UDP receive error is something we can safely ignore +// Windows has some quirks with UDP error handling +fn is_transient_udp_recv_err(_e: &std::io::Error) -> bool { + #[cfg(windows)] + { + _e.kind() == std::io::ErrorKind::ConnectionReset || _e.raw_os_error() == Some(10054) + } + #[cfg(not(windows))] + { + false + } +} + +// Check if this is the weird EINVAL error that happens on Linux +// when we try to send to a non-loopback address from a loopback-bound socket +fn is_send_einval_to_non_loopback(e: &std::io::Error, dest: SocketAddr) -> bool { + if e.raw_os_error() == Some(22) { + if let IpAddr::V4(v4) = dest.ip() { + return !v4.is_loopback(); + } + } + false +} + +// Extract IPv4 addresses from the browser's SDP offer +// We look for "a=candidate" lines and pull out the IP addresses +// This helps us decide which network interface to bind to +fn extract_offer_v4s(sdp: &str) -> Vec { + let mut ips = Vec::new(); + for line in sdp.lines() { + // SDP candidate lines look like: a=candidate:1 1 UDP 12345 192.168.1.100 12345 typ host + if let Some(rest) = line.strip_prefix("a=candidate:") { + let parts: Vec<&str> = rest.split_whitespace().collect(); + if parts.len() >= 6 { + // Skip mDNS hostnames (end with .local) + if !parts[4].ends_with(".local") { + if let Ok(IpAddr::V4(v4)) = parts[4].parse::() { + ips.push(v4); + } + } + } + } + } + ips +} + +// Check how many bits two IP addresses share in common +// This helps us find the best matching network interface +fn same_lan_prefix(a: Ipv4Addr, b: Ipv4Addr) -> u32 { + let ao = a.octets(); + let bo = b.octets(); + if ao == bo { + return 32; // Exact match + } + if ao[0] == bo[0] && ao[1] == bo[1] && ao[2] == bo[2] { + return 24; // Same /24 subnet + } + if ao[0] == bo[0] && ao[1] == bo[1] { + return 16; // Same /16 subnet + } + if ao[0] == bo[0] { + return 8; // Same /8 subnet + } + 0 // No common prefix +} + +// Check if a network interface name suggests it's a virtual/tunnel interface +// We prefer real network interfaces for WebRTC connections +fn if_is_virtual(name: &str) -> bool { + let n = name.to_ascii_lowercase(); + [ + "virtualbox", + "vmware", + "hyper-v", + "wintun", + "tailscale", + "zerotier", + "docker", + "vbox", + "bridge", + "hamachi", + "loopback", + "veth", + ] + .iter() + .any(|k| n.contains(k)) +} + +fn pick_bind_v4_for_offer(offer_sdp: &str) -> Ipv4Addr { + if let Ok(s) = std::env::var("BIND_UDP_IP") { + if let Ok(ip) = s.parse::() { + return ip; + } + } + + let offer_ips = extract_offer_v4s(offer_sdp); + if offer_ips.iter().any(|ip| ip.is_loopback()) { + return Ipv4Addr::LOCALHOST; + } + + let mut best: Option<(u32, Ipv4Addr)> = None; + if let Ok(ifs) = get_if_addrs() { + for i in &ifs { + if let IpAddr::V4(ip) = i.ip() { + if i.is_loopback() || ip.is_loopback() { + continue; + } + if if_is_virtual(&i.name) { + continue; + } + let o = ip.octets(); + let is_rfc1918 = (o[0] == 10) + || (o[0] == 172 && (16..=31).contains(&o[1])) + || (o[0] == 192 && o[1] == 168); + if !is_rfc1918 { + continue; + } + let mut rank = 0; + for off in &offer_ips { + rank = rank.max(same_lan_prefix(ip, *off)); + } + if best.as_ref().map_or(true, |(r, _)| rank > *r) { + best = Some((rank, ip)); + } + } + } + if let Some((_, ip)) = best { + return ip; + } + + for i in ifs { + if let IpAddr::V4(ip) = i.ip() { + if i.is_loopback() || ip.is_loopback() { + continue; + } + if if_is_virtual(&i.name) { + continue; + } + let o = ip.octets(); + let is_rfc1918 = (o[0] == 10) + || (o[0] == 172 && (16..=31).contains(&o[1])) + || (o[0] == 192 && o[1] == 168); + if is_rfc1918 { + return ip; + } + } + } + } + + Ipv4Addr::LOCALHOST +} + +fn run_rtc_thread( offer_sdp: String, local_ip: IpAddr, udp_port: u16, ws_in: cb::Receiver, udp_in: cb::Receiver, - udp_sock: Arc, // <— direct sender - answer_tx: cb::Sender, + timeout_in: cb::Receiver<()>, + next_out: cb::Sender, + answer_out: cb::Sender, + udp_socket: Arc, ) -> anyhow::Result<()> { - use std::collections::HashSet; - let cert_options = str0m::config::DtlsCertOptions { - pkey_type: str0m::config::DtlsPKeyType::EcDsaP256, - ..Default::default() - }; - let crypto_provider = if cfg!(windows) { - str0m::config::CryptoProvider::WinCrypto - } else { - str0m::config::CryptoProvider::OpenSsl + let mut rtc = { + #[cfg(windows)] + { + // trying to fix it for firefox on windows? + let cert_opts = str0m::config::DtlsCertOptions { + pkey_type: str0m::config::DtlsPKeyType::Rsa2048, + ..Default::default() + }; + let crypto_provider = str0m::config::CryptoProvider::WinCrypto; + let dtls_cert = str0m::config::DtlsCert::new(crypto_provider, cert_opts); + let str0m_config = str0m::RtcConfig::new() + .set_dtls_cert_config(str0m::DtlsCertConfig::PregeneratedCert(dtls_cert)); + str0m_config.build() + } + #[cfg(not(windows))] + { + str0m::RtcConfig::new().build() + } }; - let dtls_cert = str0m::config::DtlsCert::new(crypto_provider, cert_options); - let str0m_config = str0m::RtcConfig::new() - .set_dtls_cert_config(str0m::DtlsCertConfig::PregeneratedCert(dtls_cert)); - - let mut rtc = str0m_config.build(); let local_addr = SocketAddr::new(local_ip, udp_port); - let localhost_cand = Candidate::host(local_addr, "udp")?; - rtc - .add_local_candidate(localhost_cand) - .ok_or_else(|| anyhow::anyhow!("failed to add local candidate"))?; + let local_candidate = Candidate::host(local_addr, "udp")?; + rtc.add_local_candidate(local_candidate) + .ok_or(anyhow::anyhow!("Failed to add local candidate"))?; eprintln!("Added local candidate: {local_addr}"); let offer = str0m::change::SdpOffer::from_sdp_string(&offer_sdp)?; - - let answer = rtc.sdp_api().accept_offer(offer)?.to_sdp_string(); + let answer = rtc.sdp_api().accept_offer(offer)?; + let answer_sdp = answer.to_sdp_string(); eprintln!("SDP answer generated"); + let (mut next_deadline, _) = drain_outputs(&mut rtc, udp_socket.as_ref())?; + let _ = next_out.send(next_deadline); + + let _ = answer_out.send(answer_sdp); + loop { - match rtc.poll_output()? { - Output::Timeout(when) => { - let now = Instant::now(); - if now >= when { - rtc.handle_input(Input::Timeout(now))?; - } else { - break; + while let Ok(ws_msg) = ws_in.try_recv() { + match ws_msg { + WsInbound::Candidate { candidate, .. } => { + try_add_remote_candidate(&mut rtc, &candidate, local_ip) } + WsInbound::EndOfCandidates { .. } => eprintln!("EndOfCandidates received"), + _ => {} } - Output::Transmit(tx) => { - // Send directly on the arced socket - let _ = udp_sock.send_to(&tx.contents, tx.destination); - } - Output::Event(ev) => { - if let Event::IceConnectionStateChange(st) = &ev { - eprintln!("ICE state after accept: {st:?}"); + } + // Drain inbound UDP + while let Ok(UdpIn { buf, src, dst }) = udp_in.try_recv() { + let contents: &[u8] = &buf; + if let Ok(contents_slice) = contents.try_into() { + let input = Input::Receive( + Instant::now(), + Receive { + proto: Protocol::Udp, + source: src, + destination: dst, + contents: contents_slice, + }, + ); + if let Err(e) = rtc.handle_input(input) { + eprintln!("Error handling UDP input: {e:?}"); + if let Some(source) = e.source() { + eprintln!(" Source: {source:?}"); + } } } } - } + while let Ok(()) = timeout_in.try_recv() { + if let Err(e) = rtc.handle_input(Input::Timeout(Instant::now())) { + eprintln!("Error handling timeout: {e}"); + } + } - // Provide answer to WS task - let _ = answer_tx.send(answer); + let (nd, _) = drain_outputs(&mut rtc, udp_socket.as_ref())?; + if nd != next_deadline { + next_deadline = nd; + let _ = next_out.send(next_deadline); + } - let mut seen_ws_candidates = HashSet::::new(); + std::thread::sleep(Duration::from_millis(1)); + } +} +fn drain_outputs(rtc: &mut str0m::Rtc, udp_socket: &std::net::UdpSocket) -> anyhow::Result<(Instant, bool)> { + let mut connected = false; loop { - // Drain str0m outputs until timeout - let next_when = loop { - match rtc.poll_output()? { - Output::Timeout(when) => break when, - Output::Transmit(tx) => { - // During early ICE, sends to “other” pairs can legitimately fail on Windows. - // We best-effort send and ignore OS error 10051 noise. - let _ = udp_sock.send_to(&tx.contents, tx.destination); - } - Output::Event(ev) => { - match &ev { - Event::Connected => { - eprintln!("WebRTC CONNECTED! ICE and DTLS established."); - } - Event::IceConnectionStateChange(state) => { - eprintln!("ICE state changed: {state:?}"); - if matches!(state, IceConnectionState::Disconnected) { - eprintln!("ICE DISCONNECTED - stopping."); - return Ok(()); - } - } - Event::ChannelOpen(id, label) => { - eprintln!("DataChannel opened: id={id:?}, label={label}"); - if let Some(mut ch) = rtc.channel(*id) { - // send a couple of probes - for i in 0..5 { - let _ = ch.write(true, format!("hi {i}").as_bytes()); - } - } - } - Event::ChannelData(data) => { - eprintln!("DataChannel received {} bytes", data.data.len()); - } - _ => {} + match rtc.poll_output()? { + Output::Timeout(t) => break Ok((t, connected)), + Output::Transmit(tx) => { + if let Err(e) = udp_socket.send_to(&tx.contents, tx.destination) { + if is_send_einval_to_non_loopback(&e, tx.destination) { + continue; } + eprintln!("UDP send_to error to {}: {}", tx.destination, e); } } - }; - - // drain WS - while let Ok(msg) = ws_in.try_recv() { - match msg { - WsInbound::Candidate { candidate, .. } => { - let candidate_line = candidate - .strip_prefix("a=") - .or_else(|| candidate.strip_prefix("candidate:")) - .unwrap_or(&candidate); - let parts: Vec<&str> = candidate_line.split_whitespace().collect(); - if parts.len() >= 8 && parts[2].eq_ignore_ascii_case("udp") { - if let (Ok(ip), Ok(port)) = - (parts[4].parse::(), parts[5].parse::()) - { - if !ip.is_unspecified() { - let addr = SocketAddr::new(ip, port); - if seen_ws_candidates.insert(addr) { - if let Ok(c) = Candidate::host(addr, "udp") { - let _ = rtc.add_remote_candidate(c); - } - } - } - } + Output::Event(ev) => match &ev { + Event::IceConnectionStateChange(state) => eprintln!("ICE state: {state:?}"), + Event::Connected => { + eprintln!("WebRTC CONNECTED! ICE and DTLS established."); + connected = true; + } + Event::ChannelOpen(id, label) => { + eprintln!("DataChannel opened: id={id:?}, label={label}"); + if let Some(mut ch) = rtc.channel(*id) { + let _ = ch.write(true, b"hello from str0m on Windows+Linux"); } } - WsInbound::EndOfCandidates {} => { } - WsInbound::Offer { .. } => { } - } + Event::ChannelData(data) => eprintln!("DataChannel received: {data:?}"), + Event::ChannelClose(id) => eprintln!("DataChannel closed: id={id:?}"), + _ => {} + }, } + } +} - // drain UDP - let mut fed_any = false; - while let Ok(UdpIn { buf, src, dst }) = udp_in.try_recv() { - let input = Input::Receive( - Instant::now(), - Receive { - proto: Protocol::Udp, - source: src, - destination: dst, - contents: buf.as_slice().try_into().unwrap(), - }, - ); - rtc.handle_input(input)?; - fed_any = true; +fn try_add_remote_candidate(rtc: &mut str0m::Rtc, candidate: &str, local_ip: IpAddr) { + let line = candidate.trim_start_matches("a=").trim_start_matches("candidate:"); + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() < 6 { + return; + } + if !parts + .get(2) + .map(|s| s.eq_ignore_ascii_case("udp")) + .unwrap_or(false) + { + return; + } + + // ignore mDNS hostnames. + if parts[4].ends_with(".local") { + return; + } + + let ip = match parts.get(4).and_then(|s| s.parse::().ok()) { + Some(IpAddr::V4(v4)) => v4, + _ => return, + }; + let port = match parts.get(5).and_then(|s| s.parse::().ok()) { + Some(p) => p, + None => return, + }; + + match local_ip { + IpAddr::V4(v4) if v4.is_loopback() => { + if !ip.is_loopback() { + return; + } } - if fed_any { - // after receive bursts, we may get immediate outputs? - continue; + _ => { + let o = ip.octets(); + let is_rfc1918 = (o[0] == 10) + || (o[0] == 172 && (16..=31).contains(&o[1])) + || (o[0] == 192 && o[1] == 168); + if !(is_rfc1918 || ip.is_loopback()) { + return; + } } + } - // wait/sleep until next timeout - let now = Instant::now(); - if now >= next_when { - rtc.handle_input(Input::Timeout(now))?; - } else { - let sleep = (next_when - now).min(Duration::from_millis(1)); - std::thread::sleep(sleep); - rtc.handle_input(Input::Timeout(Instant::now()))?; - } + let addr = SocketAddr::new(IpAddr::V4(ip), port); + if let Ok(c) = Candidate::host(addr, "udp") { + rtc.add_remote_candidate(c); } } From 0682fc7393cd1c2aa82a25de4b2352477a5babed Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Thu, 6 Nov 2025 09:36:36 -0800 Subject: [PATCH 28/39] Stream data for graph --- webrtc/str0m-server/src/main.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index f765557..58200c3 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -487,7 +487,16 @@ fn drain_outputs(rtc: &mut str0m::Rtc, udp_socket: &std::net::UdpSocket) -> anyh Event::ChannelOpen(id, label) => { eprintln!("DataChannel opened: id={id:?}, label={label}"); if let Some(mut ch) = rtc.channel(*id) { - let _ = ch.write(true, b"hello from str0m on Windows+Linux"); + // Send message so we can test the server performance + for i in (10..=510).step_by(10) { + for j in (10..=510).step_by(10) { + let message = format!("{j},{i}"); + if let Err(e) = ch.write(false, message.as_bytes()) { + eprintln!("DataChannel send error: {e}"); + } + std::thread::sleep(std::time::Duration::from_millis(1)); + } + } } } Event::ChannelData(data) => eprintln!("DataChannel received: {data:?}"), From c176a392064c6b76d6ec4956c683d70791a932c7 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 20:02:47 +0200 Subject: [PATCH 29/39] Handle different types of data --- client/static/js/webrtc.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/static/js/webrtc.js b/client/static/js/webrtc.js index 4b14837..6680c90 100644 --- a/client/static/js/webrtc.js +++ b/client/static/js/webrtc.js @@ -21,11 +21,11 @@ webRTCBtn.onclick = (_) => { conn.onconnectionstatechange = () => { console.info(`WebRTC Connection State: ${conn.connectionState}`); }; - + conn.oniceconnectionstatechange = () => { console.info(`ICE Connection State: ${conn.iceConnectionState}`); }; - + conn.onicegatheringstatechange = () => { console.info(`ICE Gathering State: ${conn.iceGatheringState}`); }; @@ -61,14 +61,28 @@ webRTCBtn.onclick = (_) => { console.info(`WebRTC DataChannel established in ${new Date() - t0} ms.`); }; - dataChannel.onmessage = (e) => { + dataChannel.onmessage = async (e) => { if (messageCount === 0) { webRTCBtn.disabled = true; t0 = new Date(); chart.data.datasets[2].data.push({x: 0, y: 0}); } messageCount += 1; - visualizePacket(decoder.decode(e.data)); + let decoded; + if (typeof e.data === 'string') { + decoded = e.data; + } else if (e.data instanceof Blob) { + const buffer = await e.data.arrayBuffer(); + decoded = decoder.decode(buffer); + } else if (e.data instanceof ArrayBuffer) { + decoded = decoder.decode(e.data); + } else if (e.data instanceof Uint8Array || e.data instanceof DataView) { + decoded = decoder.decode(e.data); + } else { + // fallback: try to convert to string + decoded = String(e.data); + } + visualizePacket(decoded); if (new Date() - t0 - chart.data.datasets[2].data.at(-1).x > 200) { chart.data.datasets[2].data.push({x: new Date() - t0, y: messageCount}); chart.update(); From bddbf1d5c2a187c9d7312cbabbd941de0648f297 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 20:30:03 +0200 Subject: [PATCH 30/39] Fix write on windows --- webrtc/str0m-server/src/main.rs | 73 ++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 58200c3..6c7a098 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -413,7 +413,12 @@ fn run_rtc_thread( let answer_sdp = answer.to_sdp_string(); eprintln!("SDP answer generated"); - let (mut next_deadline, _) = drain_outputs(&mut rtc, udp_socket.as_ref())?; + // Track data channel state for sending messages + let mut channel_id: Option = None; + let mut messages_to_send: Vec = Vec::new(); + let mut message_index = 0; + + let (mut next_deadline, _) = drain_outputs(&mut rtc, udp_socket.as_ref(), &mut channel_id, &mut messages_to_send, &mut message_index)?; let _ = next_out.send(next_deadline); let _ = answer_out.send(answer_sdp); @@ -455,7 +460,7 @@ fn run_rtc_thread( } } - let (nd, _) = drain_outputs(&mut rtc, udp_socket.as_ref())?; + let (nd, connected_state) = drain_outputs(&mut rtc, udp_socket.as_ref(), &mut channel_id, &mut messages_to_send, &mut message_index)?; if nd != next_deadline { next_deadline = nd; let _ = next_out.send(next_deadline); @@ -465,11 +470,45 @@ fn run_rtc_thread( } } -fn drain_outputs(rtc: &mut str0m::Rtc, udp_socket: &std::net::UdpSocket) -> anyhow::Result<(Instant, bool)> { +fn drain_outputs( + rtc: &mut str0m::Rtc, + udp_socket: &std::net::UdpSocket, + channel_id: &mut Option, + messages_to_send: &mut Vec, + message_index: &mut usize, +) -> anyhow::Result<(Instant, bool)> { let mut connected = false; loop { match rtc.poll_output()? { - Output::Timeout(t) => break Ok((t, connected)), + Output::Timeout(t) => { + // Try to send next message if channel is open and we have messages + if let Some(id) = *channel_id { + if *message_index < messages_to_send.len() { + // Get channel, write message, drop channel before continuing + if let Some(mut ch) = rtc.channel(id) { + let message = &messages_to_send[*message_index]; + match ch.write(false, message.as_bytes()) { + Ok(_bytes) => { + *message_index += 1; + if *message_index % 100 == 0 { + eprintln!("Sent {} messages", message_index); + } + } + Err(e) => { + eprintln!("DataChannel write error (message {}): {e}", message_index); + } + } + } else { + // Channel not available - skip this message and try again next time + eprintln!("Channel not available, skipping message {}", message_index); + } + } else if *message_index == messages_to_send.len() && !messages_to_send.is_empty() { + eprintln!("Finished sending all {} messages", messages_to_send.len()); + *message_index += 1; // Prevent repeated logging + } + } + break Ok((t, connected)); + }, Output::Transmit(tx) => { if let Err(e) = udp_socket.send_to(&tx.contents, tx.destination) { if is_send_einval_to_non_loopback(&e, tx.destination) { @@ -486,21 +525,33 @@ fn drain_outputs(rtc: &mut str0m::Rtc, udp_socket: &std::net::UdpSocket) -> anyh } Event::ChannelOpen(id, label) => { eprintln!("DataChannel opened: id={id:?}, label={label}"); - if let Some(mut ch) = rtc.channel(*id) { + if !connected { + eprintln!("Warning: Channel opened but connection not yet established"); + } + *channel_id = Some(*id); + // Generate all messages to send (they will be sent one at a time in the Timeout handler) + if messages_to_send.is_empty() { + eprintln!("Starting to send messages through data channel"); // Send message so we can test the server performance + // Write messages and continue polling to ensure they're sent + // This is critical on Windows where writes are buffered + // Queue messages instead of writing them all at once + if messages_to_send.is_empty() { + eprintln!("Generating messages to send"); for i in (10..=510).step_by(10) { for j in (10..=510).step_by(10) { - let message = format!("{j},{i}"); - if let Err(e) = ch.write(false, message.as_bytes()) { - eprintln!("DataChannel send error: {e}"); - } - std::thread::sleep(std::time::Duration::from_millis(1)); + messages_to_send.push(format!("{j},{i}")); } } + eprintln!("Generated {} messages to send", messages_to_send.len()); + } } } Event::ChannelData(data) => eprintln!("DataChannel received: {data:?}"), - Event::ChannelClose(id) => eprintln!("DataChannel closed: id={id:?}"), + Event::ChannelClose(id) => { + eprintln!("DataChannel closed: id={id:?}"); + *channel_id = None; + } _ => {} }, } From 7bd46a896d8d1ab3cba0c712be7559c44f5a0a8a Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 22:44:03 +0200 Subject: [PATCH 31/39] Better I/O and basic back pressure --- webrtc/str0m-server/Cargo.lock | 1 + webrtc/str0m-server/Cargo.toml | 1 + webrtc/str0m-server/src/main.rs | 208 +++++++++++++++++++++++++++----- 3 files changed, 179 insertions(+), 31 deletions(-) diff --git a/webrtc/str0m-server/Cargo.lock b/webrtc/str0m-server/Cargo.lock index 009a13e..950b275 100644 --- a/webrtc/str0m-server/Cargo.lock +++ b/webrtc/str0m-server/Cargo.lock @@ -873,6 +873,7 @@ dependencies = [ "rustls-pki-types", "serde", "serde_json", + "socket2", "str0m", "tokio", "tokio-rustls", diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index f5fdc9f..d2bffc6 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -15,6 +15,7 @@ rustls-pemfile = "2.2.0" rustls-pki-types = "1.13.0" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" +socket2 = "0.6.1" tokio = { version = "1.48.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] } tokio-rustls = "0.26.4" tokio-tungstenite = { version = "0.28.0", features = ["tokio-rustls"] } diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index 6c7a098..a1dc008 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -6,7 +6,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::Path; use std::sync::Arc; use std::time::{Duration, Instant}; - +use socket2::SockRef; use crossbeam_channel as cb; use futures_util::{SinkExt, StreamExt}; use if_addrs::get_if_addrs; @@ -124,6 +124,63 @@ async fn handle_client( eprintln!("UDP bound to: {local_udp}"); let std_udp = udp.into_std()?; + + SockRef::from(&std_udp).set_send_buffer_size(20 * 1024 * 1024)?; + + #[cfg(windows)] + { + use std::os::windows::io::AsRawSocket; + use winapi::shared::minwindef::{DWORD, LPVOID}; + use winapi::um::winsock2::{setsockopt, SO_SNDBUF, SOL_SOCKET, SOCKET_ERROR, WSAGetLastError, WSAIoctl}; + + // SIO_UDP_CONNRESET = _WSAIOW(IOC_VENDOR, 12) = 0x9800000C + const SIO_UDP_CONNRESET: DWORD = 0x9800000C; + + let socket = std_udp.as_raw_socket(); + let buffer_size: i32 = 40 * 1024 * 1024; + let result = unsafe { + setsockopt( + socket as _, + SOL_SOCKET, + SO_SNDBUF, + &buffer_size as *const _ as *const _, + std::mem::size_of::() as _, + ) + }; + if result == SOCKET_ERROR { + let err = unsafe { WSAGetLastError() }; + eprintln!("Warning: Failed to set UDP send buffer size, error: {}", err); + } else { + eprintln!("Set UDP send buffer to {} bytes", buffer_size); + } + + // disable spurious UDP connection reset errors on Windows + let mut bytes_returned: DWORD = 0; + let mut enable: u32 = 0; + + let rc = unsafe { + WSAIoctl( + socket as _, + SIO_UDP_CONNRESET, + &mut enable as *mut _ as LPVOID, + std::mem::size_of::() as DWORD, + std::ptr::null_mut(), + 0, + &mut bytes_returned as *mut _, + std::ptr::null_mut(), + None, + ) + }; + if rc == SOCKET_ERROR { + eprintln!("Warning: failed to set SIO_UDP_CONNRESET"); + } else { + eprintln!("Disabled UDP connection reset errors (SIO_UDP_CONNRESET)"); + } + } + + let got = SockRef::from(&std_udp).send_buffer_size()?; + eprintln!("SO_SNDBUF set; effective size = {} bytes", got); + let std_udp_arc = Arc::new(std_udp); let (ws_to_rtc_tx, ws_to_rtc_rx) = cb::unbounded::(); @@ -413,12 +470,13 @@ fn run_rtc_thread( let answer_sdp = answer.to_sdp_string(); eprintln!("SDP answer generated"); - // Track data channel state for sending messages let mut channel_id: Option = None; let mut messages_to_send: Vec = Vec::new(); let mut message_index = 0; + let mut backpressure_state = BackpressureState::new(); + - let (mut next_deadline, _) = drain_outputs(&mut rtc, udp_socket.as_ref(), &mut channel_id, &mut messages_to_send, &mut message_index)?; + let (mut next_deadline, _) = drain_outputs(&mut rtc, udp_socket.as_ref(), &mut channel_id, &mut messages_to_send, &mut message_index, &mut backpressure_state)?; let _ = next_out.send(next_deadline); let _ = answer_out.send(answer_sdp); @@ -460,54 +518,129 @@ fn run_rtc_thread( } } - let (nd, connected_state) = drain_outputs(&mut rtc, udp_socket.as_ref(), &mut channel_id, &mut messages_to_send, &mut message_index)?; + try_write_messages(&mut rtc, &channel_id, &messages_to_send, &mut message_index, &mut backpressure_state); + + let (nd, _connected_state) = drain_outputs(&mut rtc, udp_socket.as_ref(), &mut channel_id, &mut messages_to_send, &mut message_index, &mut backpressure_state)?; if nd != next_deadline { next_deadline = nd; let _ = next_out.send(next_deadline); } + // Keep normal sleep time even when sending to avoid overwhelming Windows std::thread::sleep(Duration::from_millis(1)); } } +struct BackpressureState { + consecutive_errors: usize, + successful_batches: usize, + current_batch_size: usize, +} + +impl BackpressureState { + fn new() -> Self { + let initial = 5; + Self { + consecutive_errors: 0, + successful_batches: 0, + current_batch_size: initial, + } + } + + fn adjust_for_backpressure(&mut self, written: usize, had_error: bool, batch_size: usize) { + if had_error { + self.consecutive_errors += 1; + self.successful_batches = 0; + self.current_batch_size = (self.current_batch_size / 2).max(1); + } else { + self.consecutive_errors = 0; + if written == batch_size && written > 0 { + self.successful_batches += 1; + if self.successful_batches >= 10 && self.current_batch_size < 20 { + self.current_batch_size += 1; + self.successful_batches = 0; + } + } else if written == 0 && batch_size > 1 { + self.current_batch_size = (self.current_batch_size * 2 / 3).max(1); + } + } + } + + fn batch_size(&self) -> usize { + self.current_batch_size + } +} + +fn try_write_messages( + rtc: &mut str0m::Rtc, + channel_id: &Option, + messages_to_send: &[String], + message_index: &mut usize, + backpressure_state: &mut BackpressureState, +) { + if let Some(id) = *channel_id { + if *message_index < messages_to_send.len() { + if let Some(mut ch) = rtc.channel(id) { + let batch_size = backpressure_state.batch_size(); + let mut written = 0; + let mut had_error = false; + + while *message_index < messages_to_send.len() && written < batch_size { + let message = &messages_to_send[*message_index]; + match ch.write(false, message.as_bytes()) { + Ok(bytes_written) => { + if bytes_written == 0 { + had_error = true; + break; + } + *message_index += 1; + written += 1; + } + Err(e) => { + if *message_index > 50 { + eprintln!("Write error at message {}: {:?}", message_index, e); + } + had_error = true; + break; + } + } + } + + backpressure_state.adjust_for_backpressure(written, had_error, batch_size); + + if *message_index % 100 == 0 && written > 0 { + eprintln!("Sent {} messages (batch: {}, bp: {})", message_index, written, backpressure_state.batch_size()); + } + } + } else if *message_index == messages_to_send.len() && !messages_to_send.is_empty() { + eprintln!("Finished sending all {} messages", messages_to_send.len()); + *message_index += 1; + } + } +} + fn drain_outputs( rtc: &mut str0m::Rtc, udp_socket: &std::net::UdpSocket, channel_id: &mut Option, messages_to_send: &mut Vec, message_index: &mut usize, + backpressure_state: &mut BackpressureState, ) -> anyhow::Result<(Instant, bool)> { let mut connected = false; + let mut next_timeout = None; + let mut output_count = 0; + const MAX_OUTPUTS_PER_CALL: usize = 10; // Process fewer outputs to avoid overwhelming + loop { match rtc.poll_output()? { Output::Timeout(t) => { - // Try to send next message if channel is open and we have messages - if let Some(id) = *channel_id { - if *message_index < messages_to_send.len() { - // Get channel, write message, drop channel before continuing - if let Some(mut ch) = rtc.channel(id) { - let message = &messages_to_send[*message_index]; - match ch.write(false, message.as_bytes()) { - Ok(_bytes) => { - *message_index += 1; - if *message_index % 100 == 0 { - eprintln!("Sent {} messages", message_index); - } - } - Err(e) => { - eprintln!("DataChannel write error (message {}): {e}", message_index); - } - } - } else { - // Channel not available - skip this message and try again next time - eprintln!("Channel not available, skipping message {}", message_index); - } - } else if *message_index == messages_to_send.len() && !messages_to_send.is_empty() { - eprintln!("Finished sending all {} messages", messages_to_send.len()); - *message_index += 1; // Prevent repeated logging - } + next_timeout = Some(t); + try_write_messages(rtc, channel_id, messages_to_send, message_index, backpressure_state); + output_count += 1; + if output_count >= MAX_OUTPUTS_PER_CALL { + break; } - break Ok((t, connected)); }, Output::Transmit(tx) => { if let Err(e) = udp_socket.send_to(&tx.contents, tx.destination) { @@ -516,8 +649,13 @@ fn drain_outputs( } eprintln!("UDP send_to error to {}: {}", tx.destination, e); } + output_count += 1; + if output_count >= MAX_OUTPUTS_PER_CALL { + break; + } } - Output::Event(ev) => match &ev { + Output::Event(ev) => { + match &ev { Event::IceConnectionStateChange(state) => eprintln!("ICE state: {state:?}"), Event::Connected => { eprintln!("WebRTC CONNECTED! ICE and DTLS established."); @@ -553,9 +691,17 @@ fn drain_outputs( *channel_id = None; } _ => {} + } + output_count += 1; + if output_count >= MAX_OUTPUTS_PER_CALL { + break; + } }, } } + + let timeout = next_timeout.unwrap_or_else(|| Instant::now() + Duration::from_millis(10)); + Ok((timeout, connected)) } fn try_add_remote_candidate(rtc: &mut str0m::Rtc, candidate: &str, local_ip: IpAddr) { From 2e1f3caaa4bac958d0de53a4b6714f7a8bdac6a2 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 6 Nov 2025 22:46:55 +0200 Subject: [PATCH 32/39] Missing cargo features --- webrtc/str0m-server/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc/str0m-server/Cargo.toml b/webrtc/str0m-server/Cargo.toml index d2bffc6..1bd3b25 100644 --- a/webrtc/str0m-server/Cargo.toml +++ b/webrtc/str0m-server/Cargo.toml @@ -26,7 +26,7 @@ mimalloc = [] [target.'cfg(windows)'.dependencies] windows = { version = "0.62.2", features = ["Win32_Media_Multimedia"] } -winapi = { version = "0.3", features = ["winbase"] } +winapi = { version = "0.3", features = ["winbase", "winsock2", "ws2tcpip"] } tracing = { version = "0.1.41", features = ["async-await"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } From 0dcc7c6a4f268a1625a5d5d746a255b712279c66 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Thu, 6 Nov 2025 12:55:03 -0800 Subject: [PATCH 33/39] Remove unnecessary println --- webrtc/str0m-server/src/main.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/webrtc/str0m-server/src/main.rs b/webrtc/str0m-server/src/main.rs index a1dc008..f80bbcf 100644 --- a/webrtc/str0m-server/src/main.rs +++ b/webrtc/str0m-server/src/main.rs @@ -607,10 +607,6 @@ fn try_write_messages( } backpressure_state.adjust_for_backpressure(written, had_error, batch_size); - - if *message_index % 100 == 0 && written > 0 { - eprintln!("Sent {} messages (batch: {}, bp: {})", message_index, written, backpressure_state.batch_size()); - } } } else if *message_index == messages_to_send.len() && !messages_to_send.is_empty() { eprintln!("Finished sending all {} messages", messages_to_send.len()); From 34c1b394adc0ae2e52ea00d74b86643d1d70063a Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Thu, 6 Nov 2025 13:00:54 -0800 Subject: [PATCH 34/39] Add str0m to readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 43d52d5..c815cea 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ for i := 10; i < 510; i += 10 { } ``` +## TODO +- [Add more options for each network connection type](https://github.com/pion/realtime-web-comparison/issues/6) +- Add more libraries to test. + ## Dependencies - Server Sent Events - [tmaxmax/go-sse](https://github.com/tmaxmax/go-sse) @@ -34,6 +38,7 @@ for i := 10; i < 510; i += 10 { - [snapview/tungstenite-rs](https://github.com/snapview/tungstenite-rs) - WebRTC - [pion/webrtc](https://github.com/pion/webrtc) + - [algesten/str0m](https://github.com/algesten/str0m) - WebTransport - [adriancable/webtransport-go](https://github.com/adriancable/webtransport-go) - Client is written in pure HTML/CSS/JS. From 3387e9351e2ad691a49a1c54b63d3131bc37e3ee Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 17:24:23 -0800 Subject: [PATCH 35/39] I have no idea why this doesn't work. --- .vscode/settings.json | 6 + webrtc/webrtc-rs-server/Cargo.lock | 2631 +++++++++++++++++++++++++++ webrtc/webrtc-rs-server/Cargo.toml | 15 + webrtc/webrtc-rs-server/src/main.rs | 191 ++ 4 files changed, 2843 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 webrtc/webrtc-rs-server/Cargo.lock create mode 100644 webrtc/webrtc-rs-server/Cargo.toml create mode 100644 webrtc/webrtc-rs-server/src/main.rs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b091363 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.linkedProjects": [ + "webrtc\\webrtc-rs-server\\Cargo.toml", + "websocket\\tungstenite-server\\Cargo.toml" + ] +} \ No newline at end of file diff --git a/webrtc/webrtc-rs-server/Cargo.lock b/webrtc/webrtc-rs-server/Cargo.lock new file mode 100644 index 0000000..1be1ee1 --- /dev/null +++ b/webrtc/webrtc-rs-server/Cargo.lock @@ -0,0 +1,2631 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "ccm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847" +dependencies = [ + "aead", + "cipher", + "ctr", + "subtle", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dtls" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f531dd7c181beaf3cebab3716afa4d0d41ab888be85232583f56bbaf07ca208a" +dependencies = [ + "aes", + "aes-gcm", + "async-trait", + "bincode", + "byteorder", + "cbc", + "ccm", + "chacha20poly1305", + "der-parser", + "hmac", + "log", + "p256", + "p384", + "portable-atomic", + "rand", + "rand_core 0.6.4", + "rcgen", + "ring", + "rustls", + "sec1", + "serde", + "sha1", + "sha2", + "thiserror 1.0.69", + "tokio", + "webrtc-util", + "x25519-dalek", + "x509-parser", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "interceptor" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea51375727680dc15f06e8ad90fa31df75d79dd030100e8ad60eef1c27fe2c98" +dependencies = [ + "async-trait", + "bytes", + "futures", + "log", + "portable-atomic", + "rand", + "rtcp", + "rtp", + "thiserror 1.0.69", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rtcp" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d30d1c4091644431c22acf9f8be6191b56805e0e977f15ca7104b4a6d6eaec" +dependencies = [ + "bytes", + "thiserror 1.0.69", + "webrtc-util", +] + +[[package]] +name = "rtp" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f126f38ea84c02480e32e547c1459a939052f74fb92117ac3eef23fdac6b023" +dependencies = [ + "bytes", + "memchr", + "portable-atomic", + "rand", + "serde", + "thiserror 1.0.69", + "webrtc-util", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c374dceda16965d541c8800ce9cc4e1c14acfd661ddf7952feeedc3411e5c6" +dependencies = [ + "rand", + "substring", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stun" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a512c5d501e3e3b5a4bb3e8e31462d56d54a66b95a28b8596e14422bf21c32b" +dependencies = [ + "base64", + "crc", + "lazy_static", + "md-5", + "rand", + "ring", + "subtle", + "thiserror 1.0.69", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.1", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + +[[package]] +name = "turn" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ed995882f66ab94238de77c62e5e778389698ab700afa4696f4754da8f457cb" +dependencies = [ + "async-trait", + "base64", + "futures", + "log", + "md-5", + "portable-atomic", + "rand", + "ring", + "stun", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webrtc" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08fd686c0920ac08f3a57eacc48e31f0e4ca1ffefba4478784606f78c14e83ad" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "dtls", + "hex", + "interceptor", + "lazy_static", + "log", + "portable-atomic", + "rand", + "rcgen", + "regex", + "ring", + "rtcp", + "rtp", + "sdp", + "serde", + "serde_json", + "sha2", + "smol_str", + "stun", + "thiserror 1.0.69", + "tokio", + "turn", + "unicase", + "url", + "waitgroup", + "webrtc-data", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062a5438d63bb0756a221693d76cc0dd6119affee1dfdfe57abe3a2a8c8b3eea" +dependencies = [ + "bytes", + "log", + "portable-atomic", + "thiserror 1.0.69", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-ice" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cb13fd1a373e68addc4bba0c8ca058627518e54342583d024bdcbb8ae5d97d" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "portable-atomic", + "rand", + "serde", + "serde_json", + "stun", + "thiserror 1.0.69", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17279a067e75df72ce923fdeb7f04cd808f6f5aa4910dc6bcb4fbe66b396ace" +dependencies = [ + "log", + "socket2 0.5.10", + "thiserror 1.0.69", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a84c910fec0848fd5a0d8a5651e0ddbdedaf25a7d3ae3f0b15f71ac73a1773" +dependencies = [ + "byteorder", + "bytes", + "rand", + "rtp", + "thiserror 1.0.69", +] + +[[package]] +name = "webrtc-rs-server" +version = "0.1.0" +dependencies = [ + "anyhow", + "interceptor", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "tokio", + "tungstenite", + "webrtc", +] + +[[package]] +name = "webrtc-sctp" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f985465467d8910c1f8ac4382cd64f83b1f6a1a75021a82b221546f6fb3b856f" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "portable-atomic", + "rand", + "thiserror 1.0.69", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d8cdc33413f1d0192670a80ce93d17cb78d57fe3a2414be30d6f6dff121123" +dependencies = [ + "aead", + "aes", + "aes-gcm", + "byteorder", + "bytes", + "ctr", + "hmac", + "log", + "rtcp", + "rtp", + "sha1", + "subtle", + "thiserror 1.0.69", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c0c7e0c8f280f2bbfae442701465777ac07adaf46ce0c5863cd58e13fe472a" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes", + "ipnet", + "lazy_static", + "log", + "nix", + "portable-atomic", + "rand", + "thiserror 1.0.69", + "tokio", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/webrtc/webrtc-rs-server/Cargo.toml b/webrtc/webrtc-rs-server/Cargo.toml new file mode 100644 index 0000000..2bee66f --- /dev/null +++ b/webrtc/webrtc-rs-server/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "webrtc-rs-server" +version = "0.1.0" +edition = "2024" + +[dependencies] +webrtc = "0.14.0" +interceptor = "0.15.0" +tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "full"] } +tungstenite = "0.28" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +rustls = {version = "0.23", features = ["ring"]} +rustls-pki-types = "1.13.0" +anyhow = "1.0.100" diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs new file mode 100644 index 0000000..c875707 --- /dev/null +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -0,0 +1,191 @@ +use std::net::TcpListener; +use std::path::Path; +use std::sync::{Arc, LazyLock, OnceLock}; +use std::thread::spawn; + +use rustls::crypto::CryptoProvider; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use rustls::{ServerConfig, ServerConnection, StreamOwned}; +use tungstenite::accept; + +use tokio::time::Duration; +use webrtc::api::APIBuilder; +use webrtc::api::interceptor_registry::register_default_interceptors; +use webrtc::api::media_engine::MediaEngine; +use webrtc::data_channel::RTCDataChannel; +use webrtc::data_channel::data_channel_message::DataChannelMessage; +use webrtc::ice_transport::ice_candidate::RTCIceCandidate; +use webrtc::ice_transport::ice_server::RTCIceServer; +use webrtc::interceptor::registry::Registry; +use webrtc::peer_connection::configuration::RTCConfiguration; +use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState; +use webrtc::peer_connection::sdp::session_description::RTCSessionDescription; +use webrtc::peer_connection::{RTCPeerConnection, math_rand_alpha}; + +static PEER_CONNECTION: OnceLock> = OnceLock::new(); + +/// A WebSocket echo server over TLS (wss://) +#[tokio::main] +async fn main() -> anyhow::Result<()> { + CryptoProvider::install_default(rustls::crypto::ring::default_provider()).unwrap(); + // Use fixed relative paths from this crate to the repo certs + let cert_path = Path::new("../../certs/localhost.pem"); + let key_path = Path::new("../../certs/localhost-key.pem"); + + eprintln!("Using certificate: {}", cert_path.display()); + eprintln!("Using private key: {}", key_path.display()); + + let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); + let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + + // Build rustls server config (no client auth) + let config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], key) + .expect("invalid cert/key"); + let config = Arc::new(config); + + let listener = TcpListener::bind("127.0.0.1:8002").expect("bind 127.0.0.1:8002"); + eprintln!("tungstenite server listening on wss://127.0.0.1:8002"); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + eprintln!( + "incoming TCP connection from {}", + stream.peer_addr().unwrap() + ); + let cfg = Arc::clone(&config); + tokio::spawn(async move { + // Wrap TCP in a rustls TLS stream. + let conn = match ServerConnection::new(cfg) { + Ok(c) => c, + Err(e) => { + eprintln!("TLS ServerConnection error: {e}"); + return; + } + }; + let tls_stream = StreamOwned::new(conn, stream); + + match accept(tls_stream) { + Ok(mut websocket) => { + // Create a MediaEngine object to configure the supported codec + let mut m = MediaEngine::default(); + + // Register default codecs + m.register_default_codecs().unwrap(); + + // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. + // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` + // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry + // for each PeerConnection. + let mut registry = Registry::new(); + + // Use the default set of Interceptors + registry = register_default_interceptors(registry, &mut m).unwrap(); + + // Create the API object with the MediaEngine + let api = APIBuilder::new() + .with_media_engine(m) + .with_interceptor_registry(registry) + .build(); + + // Prepare the configuration + let config = RTCConfiguration { + ice_servers: vec![RTCIceServer { + urls: vec!["stun:stun.l.google.com:19302".to_owned()], + ..Default::default() + }], + ..Default::default() + }; + + // Create a new RTCPeerConnection + let peer_connection = + Arc::new(api.new_peer_connection(config).await.unwrap()); + + // Set the handler for Peer connection state + // This will notify you when the peer has connected/disconnected + peer_connection.on_peer_connection_state_change(Box::new( + move |s: RTCPeerConnectionState| { + println!("Peer Connection State has changed: {s}"); + + if s == RTCPeerConnectionState::Failed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. + // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. + println!("Peer Connection has gone to failed exiting"); + } + + Box::pin(async {}) + }, + )); + + peer_connection.on_ice_candidate(Box::new( + move |candidate: Option| { + Box::pin(async move { + if let Some(c) = candidate { + println!( + "New ICE candidate: {}", + c.to_json().unwrap().candidate + ); + } else { + println!("ICE gathering complete"); + } + }) + }, + )); + + peer_connection.on_data_channel(Box::new( + move |data_channel: Arc| { + println!( + "New DataChannel {}-{}", + data_channel.label(), + data_channel.id() + ); + Box::pin(async move {}) + }, + )); + + let msg = websocket.read_message().expect("read message"); + let sdp_bytes = msg.into_text().expect("msg to text"); + let offer: RTCSessionDescription = + serde_json::from_str(&sdp_bytes).expect("unmarshal SDP"); + println!("Received offer: {offer:?}"); + + // Apply the offer as the remote description + peer_connection.set_remote_description(offer).await.unwrap(); + + // Create an answer to send to the browser + let answer = peer_connection.create_answer(None).await.unwrap(); + + // Sets the LocalDescription, and starts our UDP listeners + peer_connection.set_local_description(answer).await.unwrap(); + + // Create channel that is blocked until ICE Gathering is complete + let mut gather_complete = + peer_connection.gathering_complete_promise().await; + + // Block until ICE Gathering is complete, disabling trickle ICE + // we do this because we only can exchange one signaling message + // in a production application you should exchange ICE Candidates via OnICECandidate + let _ = gather_complete.recv().await; + + println!("Connection established, waiting for messages..."); + + PEER_CONNECTION.set(peer_connection).unwrap(); + } + Err(e) => { + eprintln!("websocket handshake failed: {e}"); + eprintln!( + "Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/." + ); + } + } + }); + } + Err(e) => return Err(anyhow::anyhow!("TCP accept error: {e}")), + } + } + Ok(()) +} From 2bf3d385e18d1aea40b986cf4183ff3972d481bc Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 18:20:56 -0800 Subject: [PATCH 36/39] Move to axum webserver --- webrtc/webrtc-rs-server/Cargo.lock | 496 +++++++++++++++++++++++++++- webrtc/webrtc-rs-server/Cargo.toml | 9 +- webrtc/webrtc-rs-server/src/main.rs | 411 +++++++++++++---------- 3 files changed, 739 insertions(+), 177 deletions(-) diff --git a/webrtc/webrtc-rs-server/Cargo.lock b/webrtc/webrtc-rs-server/Cargo.lock index 1be1ee1..ffd8a72 100644 --- a/webrtc/webrtc-rs-server/Cargo.lock +++ b/webrtc/webrtc-rs-server/Cargo.lock @@ -143,6 +143,105 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "axum" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +dependencies = [ + "axum-core", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5136e6c5e7e7978fe23e9876fb924af2c0f84c72127ac6ac17e7c46f457d362c" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-core", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-server" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" +dependencies = [ + "arc-swap", + "bytes", + "fs-err", + "http", + "http-body", + "hyper", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -575,6 +674,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "ff" version = "0.13.1" @@ -612,6 +717,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad492b2cf1d89d568a43508ab24f98501fe03f2f31c01e1d0fe7366a71745d2" +dependencies = [ + "autocfg", + "tokio", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -768,6 +883,55 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + [[package]] name = "hex" version = "0.4.3" @@ -803,12 +967,85 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -911,6 +1148,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inout" version = "0.1.4" @@ -1026,6 +1273,21 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -1051,6 +1313,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1091,6 +1369,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1516,6 +1803,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.13.0" @@ -1630,6 +1926,29 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1652,6 +1971,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1779,6 +2107,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.2" @@ -1830,6 +2164,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "time" version = "0.3.44" @@ -1899,6 +2242,28 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -1912,6 +2277,122 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "tungstenite" version = "0.28.0" @@ -2019,6 +2500,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -2207,13 +2694,20 @@ name = "webrtc-rs-server" version = "0.1.0" dependencies = [ "anyhow", + "axum", + "axum-extra", + "axum-server", + "futures-util", "interceptor", "rustls", "rustls-pki-types", "serde", "serde_json", "tokio", - "tungstenite", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", "webrtc", ] diff --git a/webrtc/webrtc-rs-server/Cargo.toml b/webrtc/webrtc-rs-server/Cargo.toml index 2bee66f..5a58e42 100644 --- a/webrtc/webrtc-rs-server/Cargo.toml +++ b/webrtc/webrtc-rs-server/Cargo.toml @@ -7,9 +7,16 @@ edition = "2024" webrtc = "0.14.0" interceptor = "0.15.0" tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "full"] } -tungstenite = "0.28" serde = { version = "1", features = ["derive"] } serde_json = "1" rustls = {version = "0.23", features = ["ring"]} rustls-pki-types = "1.13.0" anyhow = "1.0.100" +axum = { version = "0.8.6", features = ["ws"] } +tower = "0.5.2" +tower-http = { version = "0.6.6", features = ["fs", "trace"] } +axum-extra = { version = "0.12.1", features = ["typed-header"] } +tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } +futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } +tracing = "0.1.41" +axum-server = { version = "0.7.2", features = ["tls-rustls"] } diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs index c875707..249c2a4 100644 --- a/webrtc/webrtc-rs-server/src/main.rs +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -1,191 +1,252 @@ -use std::net::TcpListener; -use std::path::Path; -use std::sync::{Arc, LazyLock, OnceLock}; -use std::thread::spawn; - +//! Example websocket server. +//! +//! Run the server with +//! ```not_rust +//! cargo run -p example-websockets --bin example-websockets +//! ``` +//! +//! Run a browser client with +//! ```not_rust +//! firefox http://localhost:3000 +//! ``` +//! +//! Alternatively you can run the rust client (showing two +//! concurrent websocket connections being established) with +//! ```not_rust +//! cargo run -p example-websockets --bin example-client +//! ``` + +use axum::{ + Router, + body::Bytes, + extract::ws::{Message, Utf8Bytes, WebSocket, WebSocketUpgrade}, + response::IntoResponse, + routing::any, +}; +use axum_extra::TypedHeader; use rustls::crypto::CryptoProvider; -use rustls::pki_types::pem::PemObject; -use rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use rustls::{ServerConfig, ServerConnection, StreamOwned}; -use tungstenite::accept; - -use tokio::time::Duration; -use webrtc::api::APIBuilder; -use webrtc::api::interceptor_registry::register_default_interceptors; -use webrtc::api::media_engine::MediaEngine; -use webrtc::data_channel::RTCDataChannel; -use webrtc::data_channel::data_channel_message::DataChannelMessage; -use webrtc::ice_transport::ice_candidate::RTCIceCandidate; -use webrtc::ice_transport::ice_server::RTCIceServer; -use webrtc::interceptor::registry::Registry; -use webrtc::peer_connection::configuration::RTCConfiguration; -use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState; -use webrtc::peer_connection::sdp::session_description::RTCSessionDescription; -use webrtc::peer_connection::{RTCPeerConnection, math_rand_alpha}; - -static PEER_CONNECTION: OnceLock> = OnceLock::new(); - -/// A WebSocket echo server over TLS (wss://) + +use std::{net::SocketAddr, path::PathBuf}; +use std::{ops::ControlFlow, path::Path}; +use tower_http::{ + services::ServeDir, + trace::{DefaultMakeSpan, TraceLayer}, +}; + +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +//allows to extract the IP of connecting user +use axum::extract::connect_info::ConnectInfo; +use axum::extract::ws::CloseFrame; +use axum_extra::headers; + +//allows to split the websocket stream into separate TX and RX branches +use futures_util::{sink::SinkExt, stream::StreamExt}; + #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { + format!("{}=debug,tower_http=debug", env!("CARGO_CRATE_NAME")).into() + }), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + CryptoProvider::install_default(rustls::crypto::ring::default_provider()).unwrap(); + + let assets_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("assets"); + // Use fixed relative paths from this crate to the repo certs let cert_path = Path::new("../../certs/localhost.pem"); let key_path = Path::new("../../certs/localhost-key.pem"); - eprintln!("Using certificate: {}", cert_path.display()); - eprintln!("Using private key: {}", key_path.display()); + // build our application with some routes + let app = Router::new() + .fallback_service(ServeDir::new(assets_dir).append_index_html_on_directories(true)) + .route("/", any(ws_handler)) + // logging so we can see what's going on + .layer( + TraceLayer::new_for_http() + .make_span_with(DefaultMakeSpan::default().include_headers(true)), + ); + // run it with hyper + let bind_addr = SocketAddr::from(([127, 0, 0, 1], 8002)); + tracing::debug!("listening on {}", bind_addr); + let tls_config = axum_server::tls_rustls::RustlsConfig::from_pem_file(cert_path, key_path) + .await + .unwrap(); + let server = axum_server::bind_rustls(bind_addr, tls_config); + server.serve(app.into_make_service()).await.unwrap(); +} + +/// The handler for the HTTP request (this gets called when the HTTP request lands at the start +/// of websocket negotiation). After this completes, the actual switching from HTTP to +/// websocket protocol will occur. +/// This is the last point where we can extract TCP/IP metadata such as IP address of the client +/// as well as things from HTTP headers such as user-agent of the browser etc. +async fn ws_handler( + ws: WebSocketUpgrade, + user_agent: Option>, + ConnectInfo(addr): ConnectInfo, +) -> impl IntoResponse { + let user_agent = if let Some(TypedHeader(user_agent)) = user_agent { + user_agent.to_string() + } else { + String::from("Unknown browser") + }; + println!("`{user_agent}` at {addr} connected."); + // finalize the upgrade process by returning upgrade callback. + // we can customize the callback by sending additional info such as address. + ws.on_upgrade(move |socket| handle_socket(socket, addr)) +} + +/// Actual websocket statemachine (one will be spawned per connection) +async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { + // send a ping (unsupported by some browsers) just to kick things off and get a response + if socket + .send(Message::Ping(Bytes::from_static(&[1, 2, 3]))) + .await + .is_ok() + { + println!("Pinged {who}..."); + } else { + println!("Could not send ping {who}!"); + // no Error here since the only thing we can do is to close the connection. + // If we can not send messages, there is no way to salvage the statemachine anyway. + return; + } + + // receive single message from a client (we can either receive or send with socket). + // this will likely be the Pong for our Ping or a hello message from client. + // waiting for message from a client will block this task, but will not block other client's + // connections. + if let Some(msg) = socket.recv().await { + if let Ok(msg) = msg { + if process_message(msg, who).is_break() { + return; + } + } else { + println!("client {who} abruptly disconnected"); + return; + } + } + + // Since each client gets individual statemachine, we can pause handling + // when necessary to wait for some external event (in this case illustrated by sleeping). + // Waiting for this client to finish getting its greetings does not prevent other clients from + // connecting to server and receiving their greetings. + for i in 1..5 { + if socket + .send(Message::Text(format!("Hi {i} times!").into())) + .await + .is_err() + { + println!("client {who} abruptly disconnected"); + return; + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } - let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); - let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + // By splitting socket we can send and receive at the same time. In this example we will send + // unsolicited messages to client based on some sort of server's internal event (i.e .timer). + let (mut sender, mut receiver) = socket.split(); + + // Spawn a task that will push several messages to the client (does not matter what client does) + let mut send_task = tokio::spawn(async move { + let n_msg = 20; + for i in 0..n_msg { + // In case of any websocket error, we exit. + if sender + .send(Message::Text(format!("Server message {i} ...").into())) + .await + .is_err() + { + return i; + } + + tokio::time::sleep(std::time::Duration::from_millis(300)).await; + } - // Build rustls server config (no client auth) - let config = ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(vec![cert], key) - .expect("invalid cert/key"); - let config = Arc::new(config); + println!("Sending close to {who}..."); + if let Err(e) = sender + .send(Message::Close(Some(CloseFrame { + code: axum::extract::ws::close_code::NORMAL, + reason: Utf8Bytes::from_static("Goodbye"), + }))) + .await + { + println!("Could not send Close due to {e}, probably it is ok?"); + } + n_msg + }); + + // This second task will receive messages from client and print them on server console + let mut recv_task = tokio::spawn(async move { + let mut cnt = 0; + while let Some(Ok(msg)) = receiver.next().await { + cnt += 1; + // print message and break if instructed to do so + if process_message(msg, who).is_break() { + break; + } + } + cnt + }); + + // If any one of the tasks exit, abort the other. + tokio::select! { + rv_a = (&mut send_task) => { + match rv_a { + Ok(a) => println!("{a} messages sent to {who}"), + Err(a) => println!("Error sending messages {a:?}") + } + recv_task.abort(); + }, + rv_b = (&mut recv_task) => { + match rv_b { + Ok(b) => println!("Received {b} messages"), + Err(b) => println!("Error receiving messages {b:?}") + } + send_task.abort(); + } + } - let listener = TcpListener::bind("127.0.0.1:8002").expect("bind 127.0.0.1:8002"); - eprintln!("tungstenite server listening on wss://127.0.0.1:8002"); + // returning from the handler closes the websocket connection + println!("Websocket context {who} destroyed"); +} - for stream in listener.incoming() { - match stream { - Ok(stream) => { - eprintln!( - "incoming TCP connection from {}", - stream.peer_addr().unwrap() +/// helper to print contents of messages to stdout. Has special treatment for Close. +fn process_message(msg: Message, who: SocketAddr) -> ControlFlow<(), ()> { + match msg { + Message::Text(t) => { + println!(">>> {who} sent str: {t:?}"); + } + Message::Binary(d) => { + println!(">>> {who} sent {} bytes: {d:?}", d.len()); + } + Message::Close(c) => { + if let Some(cf) = c { + println!( + ">>> {who} sent close with code {} and reason `{}`", + cf.code, cf.reason ); - let cfg = Arc::clone(&config); - tokio::spawn(async move { - // Wrap TCP in a rustls TLS stream. - let conn = match ServerConnection::new(cfg) { - Ok(c) => c, - Err(e) => { - eprintln!("TLS ServerConnection error: {e}"); - return; - } - }; - let tls_stream = StreamOwned::new(conn, stream); - - match accept(tls_stream) { - Ok(mut websocket) => { - // Create a MediaEngine object to configure the supported codec - let mut m = MediaEngine::default(); - - // Register default codecs - m.register_default_codecs().unwrap(); - - // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. - // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` - // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry - // for each PeerConnection. - let mut registry = Registry::new(); - - // Use the default set of Interceptors - registry = register_default_interceptors(registry, &mut m).unwrap(); - - // Create the API object with the MediaEngine - let api = APIBuilder::new() - .with_media_engine(m) - .with_interceptor_registry(registry) - .build(); - - // Prepare the configuration - let config = RTCConfiguration { - ice_servers: vec![RTCIceServer { - urls: vec!["stun:stun.l.google.com:19302".to_owned()], - ..Default::default() - }], - ..Default::default() - }; - - // Create a new RTCPeerConnection - let peer_connection = - Arc::new(api.new_peer_connection(config).await.unwrap()); - - // Set the handler for Peer connection state - // This will notify you when the peer has connected/disconnected - peer_connection.on_peer_connection_state_change(Box::new( - move |s: RTCPeerConnectionState| { - println!("Peer Connection State has changed: {s}"); - - if s == RTCPeerConnectionState::Failed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. - // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. - // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. - println!("Peer Connection has gone to failed exiting"); - } - - Box::pin(async {}) - }, - )); - - peer_connection.on_ice_candidate(Box::new( - move |candidate: Option| { - Box::pin(async move { - if let Some(c) = candidate { - println!( - "New ICE candidate: {}", - c.to_json().unwrap().candidate - ); - } else { - println!("ICE gathering complete"); - } - }) - }, - )); - - peer_connection.on_data_channel(Box::new( - move |data_channel: Arc| { - println!( - "New DataChannel {}-{}", - data_channel.label(), - data_channel.id() - ); - Box::pin(async move {}) - }, - )); - - let msg = websocket.read_message().expect("read message"); - let sdp_bytes = msg.into_text().expect("msg to text"); - let offer: RTCSessionDescription = - serde_json::from_str(&sdp_bytes).expect("unmarshal SDP"); - println!("Received offer: {offer:?}"); - - // Apply the offer as the remote description - peer_connection.set_remote_description(offer).await.unwrap(); - - // Create an answer to send to the browser - let answer = peer_connection.create_answer(None).await.unwrap(); - - // Sets the LocalDescription, and starts our UDP listeners - peer_connection.set_local_description(answer).await.unwrap(); - - // Create channel that is blocked until ICE Gathering is complete - let mut gather_complete = - peer_connection.gathering_complete_promise().await; - - // Block until ICE Gathering is complete, disabling trickle ICE - // we do this because we only can exchange one signaling message - // in a production application you should exchange ICE Candidates via OnICECandidate - let _ = gather_complete.recv().await; - - println!("Connection established, waiting for messages..."); - - PEER_CONNECTION.set(peer_connection).unwrap(); - } - Err(e) => { - eprintln!("websocket handshake failed: {e}"); - eprintln!( - "Hint: Ensure your client connects with wss://localhost:8002 and trusts the local certificate in certs/." - ); - } - } - }); + } else { + println!(">>> {who} somehow sent close message without CloseFrame"); } - Err(e) => return Err(anyhow::anyhow!("TCP accept error: {e}")), + return ControlFlow::Break(()); + } + + Message::Pong(v) => { + println!(">>> {who} sent pong with {v:?}"); + } + // You should never need to manually handle Message::Ping, as axum's websocket library + // will do so for you automagically by replying with Pong and copying the v according to + // spec. But if you need the contents of the pings you can see them here. + Message::Ping(v) => { + println!(">>> {who} sent ping with {v:?}"); } } - Ok(()) + ControlFlow::Continue(()) } From b79a55a5fb4c28a02bca2c2bc23f1af8cdd06433 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 19:53:00 -0800 Subject: [PATCH 37/39] Well, we have a connection at least. --- webrtc/webrtc-rs-server/src/main.rs | 258 +++++++++++++++------------- 1 file changed, 134 insertions(+), 124 deletions(-) diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs index 249c2a4..d189664 100644 --- a/webrtc/webrtc-rs-server/src/main.rs +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -24,9 +24,28 @@ use axum::{ routing::any, }; use axum_extra::TypedHeader; +use rustls::ServerConfig as RustlsServerConfig; use rustls::crypto::CryptoProvider; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use webrtc::{ + api::{ + APIBuilder, interceptor_registry::register_default_interceptors, media_engine::MediaEngine, + }, + data_channel::RTCDataChannel, + ice_transport::{ice_candidate::RTCIceCandidate, ice_server::RTCIceServer}, + peer_connection::{ + RTCPeerConnection, configuration::RTCConfiguration, + peer_connection_state::RTCPeerConnectionState, + sdp::session_description::RTCSessionDescription, + }, +}; -use std::{net::SocketAddr, path::PathBuf}; +use std::{ + net::SocketAddr, + path::PathBuf, + sync::{Arc, OnceLock}, +}; use std::{ops::ControlFlow, path::Path}; use tower_http::{ services::ServeDir, @@ -43,6 +62,8 @@ use axum_extra::headers; //allows to split the websocket stream into separate TX and RX branches use futures_util::{sink::SinkExt, stream::StreamExt}; +static PEER_CONNECTION: OnceLock> = OnceLock::new(); + #[tokio::main] async fn main() { tracing_subscriber::registry() @@ -53,7 +74,7 @@ async fn main() { ) .with(tracing_subscriber::fmt::layer()) .init(); - + CryptoProvider::install_default(rustls::crypto::ring::default_provider()).unwrap(); let assets_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("assets"); @@ -74,11 +95,22 @@ async fn main() { // run it with hyper let bind_addr = SocketAddr::from(([127, 0, 0, 1], 8002)); tracing::debug!("listening on {}", bind_addr); - let tls_config = axum_server::tls_rustls::RustlsConfig::from_pem_file(cert_path, key_path) + // Build a Rustls ServerConfig and force ALPN to http/1.1 so WebSocket upgrade works over TLS + let cert = CertificateDer::from_pem_file(cert_path).expect("load certs"); + let key = PrivateKeyDer::from_pem_file(key_path).expect("load private key"); + let mut tls_config = RustlsServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], key) + .expect("invalid cert/key"); + tls_config.alpn_protocols = vec![b"http/1.1".to_vec()]; + let tls_config = + axum_server::tls_rustls::RustlsConfig::from_config(std::sync::Arc::new(tls_config)); + let server = axum_server::bind_rustls(bind_addr, tls_config); + // Provide peer SocketAddr to handlers so ConnectInfo extractor works (prevents 500s on WS upgrade) + server + .serve(app.into_make_service_with_connect_info::()) .await .unwrap(); - let server = axum_server::bind_rustls(bind_addr, tls_config); - server.serve(app.into_make_service()).await.unwrap(); } /// The handler for the HTTP request (this gets called when the HTTP request lands at the start @@ -118,135 +150,113 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { return; } - // receive single message from a client (we can either receive or send with socket). - // this will likely be the Pong for our Ping or a hello message from client. - // waiting for message from a client will block this task, but will not block other client's - // connections. - if let Some(msg) = socket.recv().await { - if let Ok(msg) = msg { - if process_message(msg, who).is_break() { - return; - } - } else { - println!("client {who} abruptly disconnected"); - return; - } - } + // Create a MediaEngine object to configure the supported codec + let mut m = MediaEngine::default(); - // Since each client gets individual statemachine, we can pause handling - // when necessary to wait for some external event (in this case illustrated by sleeping). - // Waiting for this client to finish getting its greetings does not prevent other clients from - // connecting to server and receiving their greetings. - for i in 1..5 { - if socket - .send(Message::Text(format!("Hi {i} times!").into())) - .await - .is_err() - { - println!("client {who} abruptly disconnected"); - return; - } - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - } + // Register default codecs + m.register_default_codecs().unwrap(); - // By splitting socket we can send and receive at the same time. In this example we will send - // unsolicited messages to client based on some sort of server's internal event (i.e .timer). - let (mut sender, mut receiver) = socket.split(); - - // Spawn a task that will push several messages to the client (does not matter what client does) - let mut send_task = tokio::spawn(async move { - let n_msg = 20; - for i in 0..n_msg { - // In case of any websocket error, we exit. - if sender - .send(Message::Text(format!("Server message {i} ...").into())) - .await - .is_err() - { - return i; - } + // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. + // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` + // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry + // for each PeerConnection. + let mut registry = interceptor::registry::Registry::new(); - tokio::time::sleep(std::time::Duration::from_millis(300)).await; - } + // Use the default set of Interceptors + registry = register_default_interceptors(registry, &mut m).unwrap(); - println!("Sending close to {who}..."); - if let Err(e) = sender - .send(Message::Close(Some(CloseFrame { - code: axum::extract::ws::close_code::NORMAL, - reason: Utf8Bytes::from_static("Goodbye"), - }))) - .await - { - println!("Could not send Close due to {e}, probably it is ok?"); - } - n_msg - }); - - // This second task will receive messages from client and print them on server console - let mut recv_task = tokio::spawn(async move { - let mut cnt = 0; - while let Some(Ok(msg)) = receiver.next().await { - cnt += 1; - // print message and break if instructed to do so - if process_message(msg, who).is_break() { - break; - } - } - cnt - }); - - // If any one of the tasks exit, abort the other. - tokio::select! { - rv_a = (&mut send_task) => { - match rv_a { - Ok(a) => println!("{a} messages sent to {who}"), - Err(a) => println!("Error sending messages {a:?}") - } - recv_task.abort(); - }, - rv_b = (&mut recv_task) => { - match rv_b { - Ok(b) => println!("Received {b} messages"), - Err(b) => println!("Error receiving messages {b:?}") - } - send_task.abort(); - } - } + // Create the API object with the MediaEngine + let api = APIBuilder::new() + .with_media_engine(m) + .with_interceptor_registry(registry) + .build(); - // returning from the handler closes the websocket connection - println!("Websocket context {who} destroyed"); -} + // Prepare the configuration + let config = RTCConfiguration { + ice_servers: vec![RTCIceServer { + urls: vec!["stun:stun.l.google.com:19302".to_owned()], + ..Default::default() + }], + ..Default::default() + }; -/// helper to print contents of messages to stdout. Has special treatment for Close. -fn process_message(msg: Message, who: SocketAddr) -> ControlFlow<(), ()> { - match msg { - Message::Text(t) => { - println!(">>> {who} sent str: {t:?}"); - } - Message::Binary(d) => { - println!(">>> {who} sent {} bytes: {d:?}", d.len()); + // Create a new RTCPeerConnection + let peer_connection = Arc::new(api.new_peer_connection(config).await.unwrap()); + + // Set the handler for Peer connection state + // This will notify you when the peer has connected/disconnected + peer_connection.on_peer_connection_state_change(Box::new(move |s: RTCPeerConnectionState| { + println!("Peer Connection State has changed: {s}"); + + if s == RTCPeerConnectionState::Failed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. + // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. + println!("Peer Connection has gone to failed exiting"); } - Message::Close(c) => { - if let Some(cf) = c { - println!( - ">>> {who} sent close with code {} and reason `{}`", - cf.code, cf.reason - ); + + Box::pin(async {}) + })); + + peer_connection.on_ice_candidate(Box::new(move |candidate: Option| { + Box::pin(async move { + if let Some(c) = candidate { + println!("New ICE candidate: {}", c.to_json().unwrap().candidate); } else { - println!(">>> {who} somehow sent close message without CloseFrame"); + println!("ICE gathering complete"); } - return ControlFlow::Break(()); - } + }) + })); - Message::Pong(v) => { - println!(">>> {who} sent pong with {v:?}"); - } - // You should never need to manually handle Message::Ping, as axum's websocket library - // will do so for you automagically by replying with Pong and copying the v according to - // spec. But if you need the contents of the pings you can see them here. - Message::Ping(v) => { - println!(">>> {who} sent ping with {v:?}"); + peer_connection.on_data_channel(Box::new(move |data_channel: Arc| { + println!( + "New DataChannel {}-{}", + data_channel.label(), + data_channel.id() + ); + Box::pin(async move {}) + })); + + while let Some(msg) = socket.recv().await { + if let Ok(msg) = msg { + let sdp_bytes = msg.into_text().expect("msg to text"); + println!("Received SDP offer from client {sdp_bytes}"); + if let Some(offer) = serde_json::from_str::(&sdp_bytes).ok() { + println!("Received offer: {offer:?}"); + + let pc = peer_connection.clone(); + + // Apply the offer as the remote description + pc.set_remote_description(offer).await.unwrap(); + + // Create an answer to send to the browser + let answer = pc.create_answer(None).await.unwrap(); + + // Sets the LocalDescription, and starts our UDP listeners + pc.set_local_description(answer).await.unwrap(); + + // Create channel that is blocked until ICE Gathering is complete + let mut gather_complete = pc.gathering_complete_promise().await; + + // Block until ICE Gathering is complete, disabling trickle ICE + // we do this because we only can exchange one signaling message + // in a production application you should exchange ICE Candidates via OnICECandidate + let _ = gather_complete.recv().await; + + println!("Connection established, waiting for messages..."); + + PEER_CONNECTION.set(pc).unwrap(); + return; + } else { + println!("Could not parse SDP from client {who}"); + continue; + } + } else { + println!("client {who} abruptly disconnected"); + return; } } - ControlFlow::Continue(()) + + // returning from the handler closes the websocket connection + println!("Websocket context {who} destroyed"); } From c5c2f6c236c6b796520064b1f16cb7179e385d20 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Tue, 4 Nov 2025 21:29:55 -0800 Subject: [PATCH 38/39] Stopping here, will wait for review. --- webrtc/webrtc-rs-server/Cargo.lock | 4 ++ webrtc/webrtc-rs-server/Cargo.toml | 1 + webrtc/webrtc-rs-server/src/main.rs | 84 ++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/webrtc/webrtc-rs-server/Cargo.lock b/webrtc/webrtc-rs-server/Cargo.lock index ffd8a72..07c08dd 100644 --- a/webrtc/webrtc-rs-server/Cargo.lock +++ b/webrtc/webrtc-rs-server/Cargo.lock @@ -336,6 +336,9 @@ name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] [[package]] name = "cbc" @@ -2697,6 +2700,7 @@ dependencies = [ "axum", "axum-extra", "axum-server", + "bytes", "futures-util", "interceptor", "rustls", diff --git a/webrtc/webrtc-rs-server/Cargo.toml b/webrtc/webrtc-rs-server/Cargo.toml index 5a58e42..603feb1 100644 --- a/webrtc/webrtc-rs-server/Cargo.toml +++ b/webrtc/webrtc-rs-server/Cargo.toml @@ -20,3 +20,4 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } tracing = "0.1.41" axum-server = { version = "0.7.2", features = ["tls-rustls"] } +bytes = { version = "1.10.1", features = ["serde"] } diff --git a/webrtc/webrtc-rs-server/src/main.rs b/webrtc/webrtc-rs-server/src/main.rs index d189664..4c49ae1 100644 --- a/webrtc/webrtc-rs-server/src/main.rs +++ b/webrtc/webrtc-rs-server/src/main.rs @@ -15,6 +15,23 @@ //! ```not_rust //! cargo run -p example-websockets --bin example-client //! ``` +//! Example websocket server. +//! +//! Run the server with +//! ```not_rust +//! cargo run -p example-websockets --bin example-websockets +//! ``` +//! +//! Run a browser client with +//! ```not_rust +//! firefox http://localhost:3000 +//! ``` +//! +//! Alternatively you can run the rust client (showing two +//! concurrent websocket connections being established) with +//! ```not_rust +//! cargo run -p example-websockets --bin example-client +//! ``` use axum::{ Router, @@ -31,6 +48,7 @@ use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use webrtc::{ api::{ APIBuilder, interceptor_registry::register_default_interceptors, media_engine::MediaEngine, + setting_engine::SettingEngine, }, data_channel::RTCDataChannel, ice_transport::{ice_candidate::RTCIceCandidate, ice_server::RTCIceServer}, @@ -156,11 +174,18 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { // Register default codecs m.register_default_codecs().unwrap(); + // Create a SettingEngine to configure network settings + let mut s = SettingEngine::default(); + + // Include loopback candidates so the browser can connect via 127.0.0.1 when running locally + // This mirrors typical local testing behavior and avoids hairpin NAT issues on the same host + s.set_include_loopback_candidate(true); + // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - let mut registry = interceptor::registry::Registry::new(); + let mut registry = webrtc::interceptor::registry::Registry::new(); // Use the default set of Interceptors registry = register_default_interceptors(registry, &mut m).unwrap(); @@ -169,9 +194,12 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { let api = APIBuilder::new() .with_media_engine(m) .with_interceptor_registry(registry) + .with_setting_engine(s) .build(); // Prepare the configuration + // When using ICE-Lite, we must not specify ICE servers (STUN/TURN) + // The client will perform all connectivity checks to our host candidates let config = RTCConfiguration { ice_servers: vec![RTCIceServer { urls: vec!["stun:stun.l.google.com:19302".to_owned()], @@ -208,12 +236,38 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { }) })); + // Log ICE connection state transitions for deeper diagnostics + peer_connection.on_ice_connection_state_change(Box::new(move |s| { + println!("ICE Connection State changed: {:?}", s); + Box::pin(async {}) + })); + + // Add signaling state change handler + peer_connection.on_signaling_state_change(Box::new(move |s| { + println!("Signaling State changed: {:?}", s); + Box::pin(async {}) + })); + peer_connection.on_data_channel(Box::new(move |data_channel: Arc| { - println!( - "New DataChannel {}-{}", - data_channel.label(), - data_channel.id() - ); + let d1 = Arc::clone(&data_channel); + let d2 = Arc::clone(&data_channel); + println!("Data channel '{}'-'{}' received from peer", data_channel.label(), data_channel.id()); + d1.on_open(Box::new(move || { + println!("Data channel '{}'-'{}' open. Random messages will now be sent to any connected DataChannels every 5 seconds", data_channel.label(), data_channel.id()); + Box::pin(async move { + let mut interval = tokio::time::interval(std::time::Duration::from_secs(5)); + loop { + interval.tick().await; + let msg = String::from("Hello from webrtc-rs server!"); + if d2.send_text(msg).await.is_ok() { + println!("Sent message to DataChannel '{}'-'{}'", d2.label(), d2.id()); + } else { + println!("Failed to send message to DataChannel '{}'-'{}'", d2.label(), d2.id()); + break; + } + } + }) + })); Box::pin(async move {}) })); @@ -243,9 +297,25 @@ async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { // in a production application you should exchange ICE Candidates via OnICECandidate let _ = gather_complete.recv().await; + // Send the SDP answer back to the client before closing the websocket + if let Some(local) = pc.local_description().await { + let answer_json = serde_json::to_string(&local).expect("marshal SDP answer"); + if socket.send(Message::Text(answer_json.into())).await.is_ok() { + println!("Sent SDP answer to client"); + } else { + println!("Failed to send SDP answer to client"); + } + } else { + println!("No local description available to send as answer"); + } + println!("Connection established, waiting for messages..."); - PEER_CONNECTION.set(pc).unwrap(); + PEER_CONNECTION.set(pc.clone()).unwrap(); + + // Close the WebSocket after sending the answer, like the Go version + // The peer connection will stay alive via the global PEER_CONNECTION + drop(socket); return; } else { println!("Could not parse SDP from client {who}"); From 3a42220a63b3761bcfd2741239b457681ae27b99 Mon Sep 17 00:00:00 2001 From: Srayan Jana Date: Thu, 6 Nov 2025 14:09:21 -0800 Subject: [PATCH 39/39] Add str0m to rust-analyzer settings --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index b091363..c0311bc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "rust-analyzer.linkedProjects": [ "webrtc\\webrtc-rs-server\\Cargo.toml", + "webrtc\\str0m-server\\Cargo.toml", "websocket\\tungstenite-server\\Cargo.toml" ] } \ No newline at end of file