From fe1163c966cbfc54b30d535155554c9d225d6c71 Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Tue, 12 Dec 2023 16:49:28 -0600 Subject: [PATCH] Add WebSocket types (#23) --- .codespellrc | 2 +- .github/workflows/ci.yml | 7 +- Cargo.lock | 1660 +++++++++++++++++++++++++++++++- modeling-cmds/Cargo.toml | 6 + modeling-cmds/src/lib.rs | 3 + modeling-cmds/src/websocket.rs | 818 ++++++++++++++++ 6 files changed, 2463 insertions(+), 33 deletions(-) create mode 100644 modeling-cmds/src/websocket.rs diff --git a/.codespellrc b/.codespellrc index 99b5b594..800f19d4 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] ignore-words-list: crate,co-ordinate -skip: **/target,node_modules,build +skip: **/target,node_modules,build,Cargo.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e2eba8f..d2031f50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,12 @@ jobs: components: clippy - uses: Swatinem/rust-cache@v2.7.0 - - name: Run clippy + - name: Run clippy (no default features) + shell: bash + run: cargo clippy --tests --benches --workspace --examples --no-default-features -- -D warnings + env: + RUST_BACKTRACE: 1 + - name: Run clippy (only default features) shell: bash run: cargo clippy --tests --benches --workspace --examples -- -D warnings env: diff --git a/Cargo.lock b/Cargo.lock index 17b96549..51e24b6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,56 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +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.2" @@ -26,18 +76,119 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits 0.2.17", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[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.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bigdecimal" version = "0.4.2" @@ -51,6 +202,15 @@ dependencies = [ "num-traits 0.2.17", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -63,6 +223,24 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[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.14.0" @@ -81,6 +259,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[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.0.83" @@ -90,6 +277,18 @@ dependencies = [ "libc", ] +[[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 = "cfg-if" version = "1.0.0" @@ -105,7 +304,7 @@ dependencies = [ "approx", "mint", "num-traits 0.1.43", - "rand", + "rand 0.4.6", ] [[package]] @@ -122,12 +321,111 @@ dependencies = [ "windows-targets", ] +[[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 = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "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 2.0.39", +] + [[package]] name = "cxx" version = "1.0.110" @@ -163,6 +461,40 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits 0.2.17", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + [[package]] name = "diesel" version = "2.1.4" @@ -212,12 +544,70 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "dyn-clone" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +[[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 = "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 = "enum-iterator" version = "1.4.1" @@ -248,6 +638,22 @@ dependencies = [ "cgmath", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + [[package]] name = "fnv" version = "1.0.7" @@ -270,60 +676,263 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] -name = "http" -version = "0.2.11" +name = "futures" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ - "bytes", - "fnv", - "itoa", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "iana-time-zone" -version = "0.1.58" +name = "futures-channel" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", + "futures-core", + "futures-sink", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "futures-core" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ - "cc", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "idna" -version = "0.5.0" +name = "futures-io" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "proc-macro2", + "quote", + "syn 2.0.39", ] [[package]] -name = "inflections" -version = "1.1.1" +name = "futures-sink" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] -name = "itoa" -version = "1.0.9" +name = "futures-task" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[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 = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +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 = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "interceptor" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5927883184e6a819b22d5e4f5f7bc7ca134fde9b2026fbddd8d95249746ba21e" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -338,6 +947,7 @@ dependencies = [ name = "kittycad-modeling-cmds" version = "0.1.5" dependencies = [ + "anyhow", "chrono", "cxx", "data-encoding", @@ -354,7 +964,10 @@ dependencies = [ "schemars", "serde", "serde_bytes", + "serde_json", + "slog", "uuid", + "webrtc", ] [[package]] @@ -380,6 +993,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.150" @@ -417,6 +1036,16 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[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 = "measurements" version = "0.11.0" @@ -432,12 +1061,47 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[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 = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mint" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + [[package]] name = "mysqlclient-sys" version = "0.2.5" @@ -448,6 +1112,29 @@ dependencies = [ "vcpkg", ] +[[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.4" @@ -487,12 +1174,70 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -542,18 +1287,89 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64", + "serde", +] + +[[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[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 = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -564,6 +1380,15 @@ dependencies = [ "yansi", ] +[[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.70" @@ -606,6 +1431,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -621,6 +1467,28 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rcgen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "x509-parser", + "yasna", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -674,6 +1542,115 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[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.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys", +] + +[[package]] +name = "rtcp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3677908cadfbecb4cc1da9a56a32524fae4ebdfa7c2ea93886e1b1e846488cb9" +dependencies = [ + "bytes", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rtp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e60482acbe8afb31edf6b1413103b7bca7a65004c423b3c3993749a083994fbe" +dependencies = [ + "bytes", + "rand 0.8.5", + "serde", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +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.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.7", + "untrusted 0.9.0", +] + [[package]] name = "ryu" version = "1.0.15" @@ -723,6 +1700,48 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.7", + "untrusted 0.9.0", +] + +[[package]] +name = "sdp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4653054c30ebce63658762eb0d64e27673868a95564474811ae6c220cf767640" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror", + "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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + [[package]] name = "serde" version = "1.0.193" @@ -774,12 +1793,109 @@ dependencies = [ "serde", ] +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "structmeta" version = "0.2.0" @@ -803,6 +1919,40 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "stun" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7beb1624a3ea34778d58d30e2b8606b4d29fe65e87c4d50b87ed30afd5c3830c" +dependencies = [ + "base64", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring 0.16.20", + "subtle", + "thiserror", + "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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -825,6 +1975,67 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -840,6 +2051,61 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "turn" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f4fcb97da0426e8146fe0e9b78cc13120161087256198701d12d9df77f7701" +dependencies = [ + "async-trait", + "base64", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring 0.16.20", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -861,6 +2127,34 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[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.0" @@ -878,6 +2172,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ + "getrandom", "serde", ] @@ -887,6 +2182,27 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.89" @@ -941,6 +2257,220 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webrtc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91e7cf018f7185552bf6a5dd839f4ed9827aea33b746763c9a215f84a0d0b34" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "cfg-if", + "hex", + "interceptor", + "lazy_static", + "log", + "rand 0.8.5", + "rcgen", + "regex", + "ring 0.16.20", + "rtcp", + "rtp", + "rustls", + "sdp", + "serde", + "serde_json", + "sha2", + "smol_str", + "stun", + "thiserror", + "time", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45d2461d0e0bf93f181e30eb0b40df32b8bf3efb89c53cebb1990e603e2067d" +dependencies = [ + "bytes", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b140b953f986e97828aa33ec6318186b05d862bee689efbc57af04a243e832" +dependencies = [ + "aes", + "aes-gcm", + "async-trait", + "bincode", + "byteorder", + "cbc", + "ccm", + "der-parser", + "hkdf", + "hmac", + "log", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen", + "ring 0.16.20", + "rustls", + "sec1", + "serde", + "sha1", + "sha2", + "subtle", + "thiserror", + "tokio", + "webrtc-util", + "x25519-dalek", + "x509-parser", +] + +[[package]] +name = "webrtc-ice" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66eb4b85646f1c52225779db3e1e7e873dede6db68cc9be080b648f1713083a3" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "rand 0.8.5", + "serde", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bebbd40e7f8b630a0f1a74783dbfff1edfc0ccaae891c4689891156a8c4d8c" +dependencies = [ + "log", + "socket2", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfde3c7b9450b67d466bb2f02c6d9ff9514d33535eb9994942afd1f828839d1" +dependencies = [ + "byteorder", + "bytes", + "rand 0.8.5", + "rtp", + "thiserror", +] + +[[package]] +name = "webrtc-sctp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1af6116b7f9703560c3ad0b32f67220b171bb1b59633b03563db8404d0e482ea" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "rand 0.8.5", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db1f36c1c81e4b1e531c0b9678ba0c93809e196ce62122d87259bb71c03b9f" +dependencies = [ + "aead", + "aes", + "aes-gcm", + "byteorder", + "bytes", + "ctr", + "hmac", + "log", + "rtcp", + "rtp", + "sha1", + "subtle", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adc96bee68417e1f4d19dd7698124a7f859db55ae2fd3eedbbb7e732f614735" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "thiserror", + "tokio", + "winapi", +] + [[package]] name = "winapi" version = "0.3.9" @@ -972,6 +2502,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -1029,8 +2568,67 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "x25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring 0.16.20", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +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 2.0.39", +] diff --git a/modeling-cmds/Cargo.toml b/modeling-cmds/Cargo.toml index 98d97bdb..cedbefe5 100644 --- a/modeling-cmds/Cargo.toml +++ b/modeling-cmds/Cargo.toml @@ -11,6 +11,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.75" chrono = "0.4.31" cxx = { version = "1.0", optional = true } data-encoding = "2.5.0" @@ -27,12 +28,17 @@ parse-display-derive = "0.8.2" schemars = { version = "0.8.16", features = ["bigdecimal04", "chrono", "url", "uuid1"] } serde = { version = "1.0.193", features = ["derive"] } serde_bytes = "0.11.12" +serde_json = { version = "1.0.108", optional = true } +slog = { version = "2.7.0", optional = true } uuid = { version = "1.6.1", features = ["serde"] } +webrtc = { version = "0.9.0", optional = true } [lints] workspace = true [features] default = [] +slog = ["dep:slog"] cxx = ["dep:cxx"] diesel = ["dep:diesel"] +websocket = ["dep:webrtc", "dep:serde_json"] diff --git a/modeling-cmds/src/lib.rs b/modeling-cmds/src/lib.rs index 2850cce0..cc3b6d59 100644 --- a/modeling-cmds/src/lib.rs +++ b/modeling-cmds/src/lib.rs @@ -25,6 +25,9 @@ pub mod shared; mod traits; /// Units of measurement. pub mod units; +/// Types for the WebSocket API. +#[cfg(feature = "websocket")] +pub mod websocket; pub use def_enum::*; pub use traits::*; diff --git a/modeling-cmds/src/websocket.rs b/modeling-cmds/src/websocket.rs new file mode 100644 index 00000000..2caa7fba --- /dev/null +++ b/modeling-cmds/src/websocket.rs @@ -0,0 +1,818 @@ +//! Types for the websocket server. + +use std::borrow::Cow; + +use parse_display_derive::{Display, FromStr}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "slog")] +use slog::{Record, Serializer, KV}; +use uuid::Uuid; + +use crate::{ + id::ModelingCmdId, + ok_response::OkModelingCmdResponse, + shared::{EngineErrorCode, ExportFile}, + ModelingCmd, +}; + +/// The type of error sent by the KittyCAD API. +#[derive(Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd)] +#[serde(rename_all = "snake_case")] +pub enum ErrorCode { + /// Graphics engine failed to complete request, consider retrying + InternalEngine, + /// API failed to complete request, consider retrying + InternalApi, + /// User requested something geometrically or graphically impossible. + /// Don't retry this request, as it's inherently impossible. Instead, read the error message + /// and change your request. + BadRequest, + /// Client sent invalid JSON. + InvalidJson, + /// Client sent invalid BSON. + InvalidBson, + /// Client sent a message which is not accepted over this protocol. + WrongProtocol, + /// Problem sending data between client and KittyCAD API. + ConnectionProblem, + /// Client sent a Websocket message type which the KittyCAD API does not handle. + MessageTypeNotAccepted, + /// Client sent a Websocket message intended for WebRTC but it was configured as a WebRTC + /// connection. + MessageTypeNotAcceptedForWebRTC, +} + +/// Because [`EngineErrorCode`] is a subset of [`ErrorCode`], you can trivially map +/// each variant of the former to a variant of the latter. +impl From for ErrorCode { + fn from(value: EngineErrorCode) -> Self { + match value { + EngineErrorCode::InternalEngine => Self::InternalEngine, + EngineErrorCode::BadRequest => Self::BadRequest, + } + } +} + +/// A graphics command submitted to the KittyCAD engine via the Modeling API. +#[derive(Debug, Clone, JsonSchema, Deserialize, Serialize)] +pub struct ModelingCmdReq { + /// Which command to submit to the Kittycad engine. + pub cmd: ModelingCmd, + /// ID of command being submitted. + pub cmd_id: ModelingCmdId, +} + +/// The websocket messages the server receives. +#[derive(Serialize, Deserialize, JsonSchema, Debug)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum WebSocketRequest { + /// The trickle ICE candidate request. + // We box these to avoid a huge size difference between variants. + TrickleIce { + /// Information about the ICE candidate. + candidate: Box, + }, + /// The SDP offer request. + SdpOffer { + /// The session description. + offer: Box, + }, + /// The modeling command request. + ModelingCmdReq(ModelingCmdReq), + /// A sequence of modeling requests. If any request fails, following requests will not be tried. + ModelingCmdBatchReq(ModelingBatch), + /// The client-to-server Ping to ensure the WebSocket stays alive. + Ping {}, + + /// The response to a metrics collection request from the server. + MetricsResponse { + /// Collected metrics from the Client's end of the engine connection. + metrics: Box, + }, +} + +/// A sequence of modeling requests. If any request fails, following requests will not be tried. +#[derive(Serialize, Deserialize, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct ModelingBatch { + /// A sequence of modeling requests. If any request fails, following requests will not be tried. + pub requests: Vec, +} + +/// Representation of an ICE server used for STUN/TURN +/// Used to initiate WebRTC connections +/// based on +#[derive(serde::Serialize, serde::Deserialize, Debug, JsonSchema)] +pub struct IceServer { + /// URLs for a given STUN/TURN server. + /// IceServer urls can either be a string or an array of strings + /// But, we choose to always convert to an array of strings for consistency + pub urls: Vec, + /// Credentials for a given TURN server. + pub credential: Option, + /// Username for a given TURN server. + pub username: Option, +} + +/// The websocket messages this server sends. +#[derive(Serialize, Deserialize, JsonSchema, Debug)] +#[serde(tag = "type", content = "data", rename_all = "snake_case")] +pub enum OkWebSocketResponseData { + /// Information about the ICE servers. + IceServerInfo { + /// Information about the ICE servers. + ice_servers: Vec, + }, + /// The trickle ICE candidate response. + // We box these to avoid a huge size difference between variants. + TrickleIce { + /// Information about the ICE candidate. + candidate: Box, + }, + /// The SDP answer response. + SdpAnswer { + /// The session description. + answer: Box, + }, + /// The modeling command response. + Modeling { + /// The result of the command. + modeling_response: OkModelingCmdResponse, + }, + /// The exported files. + Export { + /// The exported files + files: Vec, + }, + + /// Request a collection of metrics, to include WebRTC. + MetricsRequest {}, +} + +/// Successful Websocket response. +#[derive(JsonSchema, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct SuccessWebSocketResponse { + /// Always true + pub success: bool, + /// Which request this is a response to. + /// If the request was a modeling command, this is the modeling command ID. + /// If no request ID was sent, this will be null. + pub request_id: Option, + /// The data sent with a successful response. + /// This will be flattened into a 'type' and 'data' field. + pub resp: OkWebSocketResponseData, +} + +/// Unsuccessful Websocket response. +#[derive(JsonSchema, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct FailureWebSocketResponse { + /// Always false + pub success: bool, + /// Which request this is a response to. + /// If the request was a modeling command, this is the modeling command ID. + /// If no request ID was sent, this will be null. + pub request_id: Option, + /// The errors that occurred. + pub errors: Vec, +} + +/// Websocket responses can either be successful or unsuccessful. +/// Slightly different schemas in either case. +#[derive(JsonSchema, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case", untagged)] +pub enum WebSocketResponse { + /// Response sent when a request succeeded. + Success(SuccessWebSocketResponse), + /// Response sent when a request did not succeed. + Failure(FailureWebSocketResponse), +} + +impl WebSocketResponse { + /// Make a new success response. + pub fn success(request_id: Option, resp: OkWebSocketResponseData) -> Self { + Self::Success(SuccessWebSocketResponse { + success: true, + request_id, + resp, + }) + } + + /// Make a new failure response. + pub fn failure(request_id: Option, errors: Vec) -> Self { + Self::Failure(FailureWebSocketResponse { + success: false, + request_id, + errors, + }) + } + + /// Did the request succeed? + pub fn is_success(&self) -> bool { + matches!(self, Self::Success(_)) + } + + /// Did the request fail? + pub fn is_failure(&self) -> bool { + matches!(self, Self::Failure(_)) + } +} + +/// A raw file with unencoded contents to be passed over binary websockets. +/// When raw files come back for exports it is sent as binary/bson, not text/json. +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct RawFile { + /// The name of the file. + pub name: String, + /// The contents of the file. + #[serde( + serialize_with = "serde_bytes::serialize", + deserialize_with = "serde_bytes::deserialize" + )] + pub contents: Vec, +} + +impl From for RawFile { + fn from(f: ExportFile) -> Self { + Self { + name: f.name, + contents: f.contents.0, + } + } +} + +/// An error with an internal message for logging. +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct LoggableApiError { + /// The error shown to users + pub error: ApiError, + /// The string logged internally + pub msg_internal: Option>, +} + +#[cfg(feature = "slog")] +impl KV for LoggableApiError { + fn serialize(&self, _rec: &Record, serializer: &mut dyn Serializer) -> slog::Result { + if let Some(ref msg_internal) = self.msg_internal { + serializer.emit_str("msg_internal", msg_internal)?; + } + serializer.emit_str("msg_external", &self.error.message)?; + serializer.emit_str("error_code", &self.error.error_code.to_string()) + } +} + +/// An error. +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct ApiError { + /// The error code. + pub error_code: ErrorCode, + /// The error message. + pub message: String, +} + +impl ApiError { + /// Convert to a `LoggableApiError` with no internal message. + pub fn no_internal_message(self) -> LoggableApiError { + LoggableApiError { + error: self, + msg_internal: None, + } + } + /// Add an internal log message to this error. + pub fn with_message(self, msg_internal: Cow<'static, str>) -> LoggableApiError { + LoggableApiError { + error: self, + msg_internal: Some(msg_internal), + } + } + + /// Should the internal error message be logged? + pub fn should_log_internal_message(&self) -> bool { + use ErrorCode as Code; + match self.error_code { + // Internal errors should always be logged, as they're problems with KittyCAD programming + Code::InternalEngine | Code::InternalApi => true, + // The user did something wrong, no need to log it, as there's nothing KittyCAD programmers can fix + Code::MessageTypeNotAcceptedForWebRTC + | Code::MessageTypeNotAccepted + | Code::BadRequest + | Code::WrongProtocol + | Code::InvalidBson + | Code::InvalidJson => false, + // In debug builds, log connection problems, otherwise don't. + Code::ConnectionProblem => cfg!(debug_assertions), + } + } +} + +/// Serde serializes Result into JSON as "Ok" and "Err", but we want "ok" and "err". +/// So, create a new enum that serializes as lowercase. +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case", rename = "SnakeCaseResult")] +pub enum SnakeCaseResult { + /// The result is Ok. + Ok(T), + /// The result is Err. + Err(E), +} + +impl From> for Result { + fn from(value: SnakeCaseResult) -> Self { + match value { + SnakeCaseResult::Ok(x) => Self::Ok(x), + SnakeCaseResult::Err(x) => Self::Err(x), + } + } +} + +impl From> for SnakeCaseResult { + fn from(value: Result) -> Self { + match value { + Ok(x) => Self::Ok(x), + Err(x) => Self::Err(x), + } + } +} + +/// ClientMetrics contains information regarding the state of the peer. +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct ClientMetrics { + /// Counter of the number of WebRTC frames the client has dropped during + /// this session. + pub rtc_frames_dropped: u32, + + /// Counter of the number of WebRTC frames that the client has decoded + /// during this session. + pub rtc_frames_decoded: u64, + + /// Counter of the number of WebRTC frames that the client has received + /// during this session. + pub rtc_frames_received: u64, + + /// Current number of frames being rendered per second. A good target + /// is 60 frames per second, but it can fluctuate depending on network + /// conditions. + pub rtc_frames_per_second: u8, // no way we're more than 255 fps :) + + /// Number of times the WebRTC playback has frozen. This is usually due to + /// network conditions. + pub rtc_freeze_count: u32, + + /// Amount of "jitter" in the WebRTC session. Network latency is the time + /// it takes a packet to traverse the network. The amount that the latency + /// varies is the jitter. Video latency is the time it takes to render + /// a frame sent by the server (including network latency). A low jitter + /// means the video latency can be reduced without impacting smooth + /// playback. High jitter means clients will increase video latency to + /// ensure smooth playback. + pub rtc_jitter_sec: f32, + + /// Number of "key frames" decoded in the underlying h.264 stream. A + /// key frame is an expensive (bandwidth-wise) "full image" of the video + /// frame. Data after the keyframe become -- effectively -- "diff" + /// operations on that key frame. The Engine will only send a keyframe if + /// required, which is an indication that some of the "diffs" have been + /// lost, usually an indication of poor network conditions. We like this + /// metric to understand times when the connection has had to recover. + pub rtc_keyframes_decoded: u32, + + /// Number of seconds of frozen video the user has been subjected to. + pub rtc_total_freezes_duration_sec: f32, +} + +/// ICECandidate represents a ice candidate +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +pub struct RtcIceCandidate { + /// The stats ID. + pub stats_id: String, + /// The foundation for the address. + pub foundation: String, + /// The priority of the candidate. + pub priority: u32, + /// The address of the candidate. + pub address: String, + /// The protocol used for the candidate. + pub protocol: RtcIceProtocol, + /// The port used for the candidate. + pub port: u16, + /// The type of the candidate. + pub typ: RtcIceCandidateType, + /// The component of the candidate. + pub component: u16, + /// The related address of the candidate. + pub related_address: String, + /// The related port of the candidate. + pub related_port: u16, + /// The TCP type of the candidate. + pub tcp_type: String, +} + +impl From for RtcIceCandidate { + fn from(candidate: webrtc::ice_transport::ice_candidate::RTCIceCandidate) -> Self { + Self { + stats_id: candidate.stats_id, + foundation: candidate.foundation, + priority: candidate.priority, + address: candidate.address, + protocol: candidate.protocol.into(), + port: candidate.port, + typ: candidate.typ.into(), + component: candidate.component, + related_address: candidate.related_address, + related_port: candidate.related_port, + tcp_type: candidate.tcp_type, + } + } +} + +impl From for webrtc::ice_transport::ice_candidate::RTCIceCandidate { + fn from(candidate: RtcIceCandidate) -> Self { + Self { + stats_id: candidate.stats_id, + foundation: candidate.foundation, + priority: candidate.priority, + address: candidate.address, + protocol: candidate.protocol.into(), + port: candidate.port, + typ: candidate.typ.into(), + component: candidate.component, + related_address: candidate.related_address, + related_port: candidate.related_port, + tcp_type: candidate.tcp_type, + } + } +} + +/// ICECandidateType represents the type of the ICE candidate used. +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RtcIceCandidateType { + /// Unspecified indicates that the candidate type is unspecified. + #[default] + Unspecified, + + /// ICECandidateTypeHost indicates that the candidate is of Host type as + /// described in . A + /// candidate obtained by binding to a specific port from an IP address on + /// the host. This includes IP addresses on physical interfaces and logical + /// ones, such as ones obtained through VPNs. + Host, + + /// ICECandidateTypeSrflx indicates the the candidate is of Server + /// Reflexive type as described + /// . A candidate type + /// whose IP address and port are a binding allocated by a NAT for an ICE + /// agent after it sends a packet through the NAT to a server, such as a + /// STUN server. + Srflx, + + /// ICECandidateTypePrflx indicates that the candidate is of Peer + /// Reflexive type. A candidate type whose IP address and port are a binding + /// allocated by a NAT for an ICE agent after it sends a packet through the + /// NAT to its peer. + Prflx, + + /// ICECandidateTypeRelay indicates the the candidate is of Relay type as + /// described in . A + /// candidate type obtained from a relay server, such as a TURN server. + Relay, +} + +impl From for RtcIceCandidateType { + fn from(candidate_type: webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType) -> Self { + match candidate_type { + webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Host => RtcIceCandidateType::Host, + webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Srflx => RtcIceCandidateType::Srflx, + webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Prflx => RtcIceCandidateType::Prflx, + webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Relay => RtcIceCandidateType::Relay, + webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Unspecified => { + RtcIceCandidateType::Unspecified + } + } + } +} + +impl From for webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType { + fn from(candidate_type: RtcIceCandidateType) -> Self { + match candidate_type { + RtcIceCandidateType::Host => webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Host, + RtcIceCandidateType::Srflx => webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Srflx, + RtcIceCandidateType::Prflx => webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Prflx, + RtcIceCandidateType::Relay => webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Relay, + RtcIceCandidateType::Unspecified => { + webrtc::ice_transport::ice_candidate_type::RTCIceCandidateType::Unspecified + } + } + } +} + +/// ICEProtocol indicates the transport protocol type that is used in the +/// ice.URL structure. +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RtcIceProtocol { + /// Unspecified indicates that the protocol is unspecified. + #[default] + Unspecified, + + /// UDP indicates the URL uses a UDP transport. + Udp, + + /// TCP indicates the URL uses a TCP transport. + Tcp, +} + +impl From for RtcIceProtocol { + fn from(protocol: webrtc::ice_transport::ice_protocol::RTCIceProtocol) -> Self { + match protocol { + webrtc::ice_transport::ice_protocol::RTCIceProtocol::Udp => RtcIceProtocol::Udp, + webrtc::ice_transport::ice_protocol::RTCIceProtocol::Tcp => RtcIceProtocol::Tcp, + webrtc::ice_transport::ice_protocol::RTCIceProtocol::Unspecified => RtcIceProtocol::Unspecified, + } + } +} + +impl From for webrtc::ice_transport::ice_protocol::RTCIceProtocol { + fn from(protocol: RtcIceProtocol) -> Self { + match protocol { + RtcIceProtocol::Udp => webrtc::ice_transport::ice_protocol::RTCIceProtocol::Udp, + RtcIceProtocol::Tcp => webrtc::ice_transport::ice_protocol::RTCIceProtocol::Tcp, + RtcIceProtocol::Unspecified => webrtc::ice_transport::ice_protocol::RTCIceProtocol::Unspecified, + } + } +} + +/// ICECandidateInit is used to serialize ice candidates +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +// These HAVE to be camel case as per the RFC. +pub struct RtcIceCandidateInit { + /// The candidate string associated with the object. + pub candidate: String, + /// The identifier of the "media stream identification" as defined in + /// [RFC 8841](https://tools.ietf.org/html/rfc8841). + pub sdp_mid: Option, + /// The index (starting at zero) of the m-line in the SDP this candidate is + /// associated with. + #[serde(rename = "sdpMLineIndex")] + pub sdp_mline_index: Option, + /// The username fragment (as defined in + /// [RFC 8445](https://tools.ietf.org/html/rfc8445#section-5.2.1)) associated with the object. + pub username_fragment: Option, +} + +impl From for RtcIceCandidateInit { + fn from(candidate: webrtc::ice_transport::ice_candidate::RTCIceCandidateInit) -> Self { + Self { + candidate: candidate.candidate, + sdp_mid: candidate.sdp_mid, + sdp_mline_index: candidate.sdp_mline_index, + username_fragment: candidate.username_fragment, + } + } +} + +impl From for webrtc::ice_transport::ice_candidate::RTCIceCandidateInit { + fn from(candidate: RtcIceCandidateInit) -> Self { + Self { + candidate: candidate.candidate, + sdp_mid: candidate.sdp_mid, + sdp_mline_index: candidate.sdp_mline_index, + username_fragment: candidate.username_fragment, + } + } +} + +/// SessionDescription is used to expose local and remote session descriptions. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct RtcSessionDescription { + /// SDP type. + #[serde(rename = "type")] + pub sdp_type: RtcSdpType, + + /// SDP string. + pub sdp: String, +} + +impl From for RtcSessionDescription { + fn from(desc: webrtc::peer_connection::sdp::session_description::RTCSessionDescription) -> Self { + Self { + sdp_type: desc.sdp_type.into(), + sdp: desc.sdp, + } + } +} + +impl TryFrom for webrtc::peer_connection::sdp::session_description::RTCSessionDescription { + type Error = anyhow::Error; + + fn try_from(desc: RtcSessionDescription) -> Result { + let result = match desc.sdp_type { + RtcSdpType::Offer => { + webrtc::peer_connection::sdp::session_description::RTCSessionDescription::offer(desc.sdp)? + } + RtcSdpType::Pranswer => { + webrtc::peer_connection::sdp::session_description::RTCSessionDescription::pranswer(desc.sdp)? + } + RtcSdpType::Answer => { + webrtc::peer_connection::sdp::session_description::RTCSessionDescription::answer(desc.sdp)? + } + RtcSdpType::Rollback => anyhow::bail!("Rollback is not supported"), + RtcSdpType::Unspecified => anyhow::bail!("Unspecified is not supported"), + }; + + Ok(result) + } +} + +/// SDPType describes the type of an SessionDescription. +#[derive(Default, Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RtcSdpType { + /// Unspecified indicates that the type is unspecified. + #[default] + Unspecified = 0, + + /// indicates that a description MUST be treated as an SDP offer. + Offer, + + /// indicates that a description MUST be treated as an + /// SDP answer, but not a final answer. A description used as an SDP + /// pranswer may be applied as a response to an SDP offer, or an update to + /// a previously sent SDP pranswer. + Pranswer, + + /// indicates that a description MUST be treated as an SDP + /// final answer, and the offer-answer exchange MUST be considered complete. + /// A description used as an SDP answer may be applied as a response to an + /// SDP offer or as an update to a previously sent SDP pranswer. + Answer, + + /// indicates that a description MUST be treated as + /// canceling the current SDP negotiation and moving the SDP offer and + /// answer back to what it was in the previous stable state. Note the + /// local or remote SDP descriptions in the previous stable state could be + /// null if there has not yet been a successful offer-answer negotiation. + Rollback, +} + +impl From for RtcSdpType { + fn from(sdp_type: webrtc::peer_connection::sdp::sdp_type::RTCSdpType) -> Self { + match sdp_type { + webrtc::peer_connection::sdp::sdp_type::RTCSdpType::Offer => Self::Offer, + webrtc::peer_connection::sdp::sdp_type::RTCSdpType::Pranswer => Self::Pranswer, + webrtc::peer_connection::sdp::sdp_type::RTCSdpType::Answer => Self::Answer, + webrtc::peer_connection::sdp::sdp_type::RTCSdpType::Rollback => Self::Rollback, + webrtc::peer_connection::sdp::sdp_type::RTCSdpType::Unspecified => Self::Unspecified, + } + } +} + +impl From for webrtc::peer_connection::sdp::sdp_type::RTCSdpType { + fn from(sdp_type: RtcSdpType) -> Self { + match sdp_type { + RtcSdpType::Offer => Self::Offer, + RtcSdpType::Pranswer => Self::Pranswer, + RtcSdpType::Answer => Self::Answer, + RtcSdpType::Rollback => Self::Rollback, + RtcSdpType::Unspecified => Self::Unspecified, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::output; + + const REQ_ID: Uuid = uuid::uuid!("cc30d5e2-482b-4498-b5d2-6131c30a50a4"); + + #[test] + fn serialize_websocket_modeling_ok() { + let actual = + WebSocketResponse::Success(SuccessWebSocketResponse { + success: true, + request_id: Some(REQ_ID), + resp: OkWebSocketResponseData::Modeling { + modeling_response: OkModelingCmdResponse::CurveGetControlPoints( + output::CurveGetControlPoints { control_points: vec![] } + ), + }, + }); + let expected = serde_json::json!({ + "success": true, + "request_id": "cc30d5e2-482b-4498-b5d2-6131c30a50a4", + "resp": { + "type": "modeling", + "data": { + "modeling_response": { + "type": "curve_get_control_points", + "data": { "control_points": [] } + } + } + } + }); + assert_json_eq(actual, expected); + } + + #[test] + fn serialize_websocket_webrtc_ok() { + let actual = WebSocketResponse::Success(SuccessWebSocketResponse { + success: true, + request_id: Some(REQ_ID), + resp: OkWebSocketResponseData::IceServerInfo { ice_servers: vec![] }, + }); + let expected = + serde_json::json!({ + "success": true, + "request_id": "cc30d5e2-482b-4498-b5d2-6131c30a50a4", + "resp": { + "type": "ice_server_info", + "data": { + "ice_servers": [] + } + } + }); + assert_json_eq(actual, expected); + } + + #[test] + fn serialize_websocket_export_ok() { + let actual = WebSocketResponse::Success(SuccessWebSocketResponse { + success: true, + request_id: Some(REQ_ID), + resp: OkWebSocketResponseData::Export { files: vec![] }, + }); + let expected = + serde_json::json!({ + "success": true, + "request_id": "cc30d5e2-482b-4498-b5d2-6131c30a50a4", + "resp": { + "type": "export", + "data": {"files": [] } + } + }); + assert_json_eq(actual, expected); + } + + #[test] + fn serialize_websocket_err() { + let actual = WebSocketResponse::Failure(FailureWebSocketResponse { + success: false, + request_id: Some(REQ_ID), + errors: vec![ApiError { + error_code: ErrorCode::InternalApi, + message: "you fucked up!".to_owned(), + }], + }); + let expected = serde_json::json!({ + "success": false, + "request_id": "cc30d5e2-482b-4498-b5d2-6131c30a50a4", + "errors": [ + { + "error_code": "internal_api", + "message": "you fucked up!" + } + ], + }); + assert_json_eq(actual, expected); + } + + #[test] + fn serialize_websocket_metrics() { + let actual = WebSocketRequest::MetricsResponse { + metrics: Box::new(ClientMetrics { + rtc_frames_dropped: 1, + rtc_frames_decoded: 2, + rtc_frames_per_second: 3, + rtc_frames_received: 4, + rtc_freeze_count: 5, + rtc_jitter_sec: 6.7, + rtc_keyframes_decoded: 8, + rtc_total_freezes_duration_sec: 9.1, + }), + }; + let expected = serde_json::json!({ + "type": "metrics_response", + "metrics": { + "rtc_frames_dropped": 1, + "rtc_frames_decoded": 2, + "rtc_frames_per_second": 3, + "rtc_frames_received": 4, + "rtc_freeze_count": 5, + "rtc_jitter_sec": 6.7, + "rtc_keyframes_decoded": 8, + "rtc_total_freezes_duration_sec": 9.1 + }, + }); + assert_json_eq(actual, expected); + } + + fn assert_json_eq(actual: T, expected: serde_json::Value) { + let json_str = serde_json::to_string(&actual).unwrap(); + let actual: serde_json::Value = serde_json::from_str(&json_str).unwrap(); + assert_eq!(actual, expected, "got\n{actual:#}\n, expected\n{expected:#}\n"); + } +}