tokio-tungstenite-0.20.1/.cargo_vcs_info.json0000644000000001360000000000100146100ustar { "git": { "sha1": "27b98302f8d4ddd4748c0a1a435cd295a59f51a9" }, "path_in_vcs": "" }tokio-tungstenite-0.20.1/CHANGELOG.md000064400000000000000000000061241046102023000152140ustar 00000000000000# 0.20.1 - Fix RUSTSEC-2023-0053. - Fix transitive CVE-2023-43669 from `tungstenite`. # 0.20.0 - Change the buffering behavior for `Sink::send()` and `Sink::feed()`, [see `tungstenite`'s changelog for more details](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md#0200). # 0.19.0 - Allow users to enable/disable Nagle algorithm when using `connect()` helpers. - Improve the behavior of the `Sink` for the `WebSocketStream`, so it does not return an error when it’s not necessary (when `poll_flush()` is called on a connection that has just been closed). - Workaround an issue where `rustls` TLS backend expected domain in a certain format and reject IPv6 addresses if they contained square brackets in them. - Update dependencies and remove unused errors. # 0.18.0 - Update dependencies (underlying `tungstenite` core). # 0.17.2 - Make `Origin` header case-sensitive (to keep compatibility with poorely-written servers that don't accept lowercase `Origin` header). - Make semantics of the reading form the `WebSocketStream` more reasonable (return `None` instead of an error when the stream is normally closed). - Imrpove the way `poll_close()` works by properly driving the close of the stream till completion. # 0.17.1 - Update the `tungstenite` dependency (fixes a panic in `tungstenite` and MSRV), see [`tungstenite`'s changelog for more details](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md#0172). # 0.17.0 - Update the dependencies, please refer to the [`tungstenite` changelog](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md#0170) for the actual changes. # 0.16.1 - Fix feature selection problem when using TLS. # 0.16.0 - Add a function to allow to specify the TLS connector when using `connect()` like logic. - Add support for choosing the right root certificates for the TLS. - Change the behavior of the `connect()` so that it fails when using TLS without TLS feature. - Do not project with Unpin. - Update the dependencies with important [implications / improvements](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md#0160). # 0.15.0 - Update the `tungstenite-rs` version to `0.14.0`, [check `tungstenite-rs` release for more details](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md#0140). # 0.14.0 - Support for `rustls` as TLS backend. - The `tls` feature was renamed to `native-tls` and uses a OS-native TLS implementation. - A new `native-tls-vendored` feature that uses `native-tls` but forces to build a vendored version (mostly for `openssl`) instead of linking against the system installation. - New `rustls-tls` feature flag to enable TLS with `rustls` as backend. - `stream::Stream` was renamed to `MaybeTlsStream` and wraps a `rustls` TLS stream as well now. - If both `native-tls` and `rustls-tls` are enabled `native-tls` is used by default. - A new `Connector` was introduced that is similar to the previous `TlsConnector` but now allows to control the used TLS backend explicitly (or disable it) in `client_async_tls_with_config`. # 0.13.0 - Upgrade from Tokio 0.3 to Tokio 1.0.0. tokio-tungstenite-0.20.1/Cargo.lock0000644000001044460000000000100125740ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cc" version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[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.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "env_logger" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", "windows-sys 0.48.0", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "fastrand" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "fnv" 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 = "form_urlencoded" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-sink", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "http" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", "pin-project-lite", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", "socket2", "tokio", "tower-service", "tracing", "want", ] [[package]] name = "idna" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "io-lifetimes" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", "windows-sys 0.48.0", ] [[package]] name = "is-terminal" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", "windows-sys 0.48.0", ] [[package]] name = "itoa" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[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.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "linux-raw-sys" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi", "windows-sys 0.42.0", ] [[package]] name = "native-tls" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "num_cpus" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ "hermit-abi 0.1.19", "libc", ] [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "openssl" version = "0.10.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", "syn 1.0.104", ] [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" version = "111.24.0+1.1.1s" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd" dependencies = [ "cc", ] [[package]] name = "openssl-sys" version = "0.9.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132" dependencies = [ "autocfg", "cc", "libc", "openssl-src", "pkg-config", "vcpkg", ] [[package]] name = "percent-encoding" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[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.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] [[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", ] [[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", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[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", "untrusted", "web-sys", "winapi", ] [[package]] name = "rustix" version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "rustls" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", "rustls-webpki", "sct", ] [[package]] name = "rustls-native-certs" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", ] [[package]] name = "schannel" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", "windows-sys 0.36.1", ] [[package]] name = "sct" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", ] [[package]] name = "security-framework" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "sha1" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "slab" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ "autocfg", ] [[package]] name = "socket2" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", "fastrand", "libc", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] name = "termcolor" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", "syn 1.0.104", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", "syn 2.0.15", ] [[package]] name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", ] [[package]] name = "tokio-tungstenite" version = "0.20.1" dependencies = [ "env_logger", "futures-channel", "futures-util", "hyper", "log", "native-tls", "rustls", "rustls-native-certs", "tokio", "tokio-native-tls", "tokio-rustls", "tungstenite", "url", "webpki-roots", ] [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "tungstenite" version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", "data-encoding", "http", "httparse", "log", "native-tls", "rand", "rustls", "sha1", "thiserror", "url", "utf-8", ] [[package]] name = "typenum" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-bidi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ "log", "try-lock", ] [[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.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 1.0.104", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", "syn 1.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "webpki-roots" version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[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-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[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-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc 0.36.1", "windows_i686_gnu 0.36.1", "windows_i686_msvc 0.36.1", "windows_x86_64_gnu 0.36.1", "windows_x86_64_msvc 0.36.1", ] [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm 0.42.0", "windows_aarch64_msvc 0.42.0", "windows_i686_gnu 0.42.0", "windows_i686_msvc 0.42.0", "windows_x86_64_gnu 0.42.0", "windows_x86_64_gnullvm 0.42.0", "windows_x86_64_msvc 0.42.0", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", "windows_i686_gnu 0.48.0", "windows_i686_msvc 0.48.0", "windows_x86_64_gnu 0.48.0", "windows_x86_64_gnullvm 0.48.0", "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" tokio-tungstenite-0.20.1/Cargo.toml0000644000000072120000000000100126100ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.63" name = "tokio-tungstenite" version = "0.20.1" authors = [ "Daniel Abramov ", "Alexey Galakhov ", ] include = [ "examples/**/*", "src/**/*", "LICENSE", "README.md", "CHANGELOG.md", ] description = "Tokio binding for Tungstenite, the Lightweight stream-based WebSocket implementation" homepage = "https://github.com/snapview/tokio-tungstenite" documentation = "https://docs.rs/tokio-tungstenite/0.20.1" readme = "README.md" keywords = [ "websocket", "io", "web", ] categories = [ "web-programming::websocket", "network-programming", "asynchronous", "concurrency", ] license = "MIT" repository = "https://github.com/snapview/tokio-tungstenite" [package.metadata.docs.rs] features = [ "native-tls", "__rustls-tls", ] [[example]] name = "autobahn-client" required-features = ["connect"] [[example]] name = "autobahn-server" required-features = ["handshake"] [[example]] name = "client" required-features = ["connect"] [[example]] name = "echo-server" required-features = ["handshake"] [[example]] name = "server-custom-accept" required-features = ["handshake"] [[example]] name = "server" required-features = ["handshake"] [[example]] name = "server-headers" required-features = ["handshake"] [[example]] name = "interval-server" required-features = ["handshake"] [dependencies.futures-util] version = "0.3.28" features = [ "sink", "std", ] default-features = false [dependencies.log] version = "0.4.17" [dependencies.native-tls-crate] version = "0.2.11" optional = true package = "native-tls" [dependencies.rustls] version = "0.21.6" optional = true [dependencies.rustls-native-certs] version = "0.6.2" optional = true [dependencies.tokio] version = "1.0.0" features = ["io-util"] default-features = false [dependencies.tokio-native-tls] version = "0.3.1" optional = true [dependencies.tokio-rustls] version = "0.24.1" optional = true [dependencies.tungstenite] version = "0.20.1" default-features = false [dependencies.webpki-roots] version = "0.25.2" optional = true [dev-dependencies.env_logger] version = "0.10.0" [dev-dependencies.futures-channel] version = "0.3.28" [dev-dependencies.hyper] version = "0.14.25" features = [ "http1", "server", "tcp", ] default-features = false [dev-dependencies.tokio] version = "1.27.0" features = [ "io-std", "macros", "net", "rt-multi-thread", "time", ] default-features = false [dev-dependencies.url] version = "2.3.1" [features] __rustls-tls = [ "rustls", "tokio-rustls", "stream", "tungstenite/__rustls-tls", "handshake", ] connect = [ "stream", "tokio/net", "handshake", ] default = [ "connect", "handshake", ] handshake = ["tungstenite/handshake"] native-tls = [ "native-tls-crate", "tokio-native-tls", "stream", "tungstenite/native-tls", "handshake", ] native-tls-vendored = [ "native-tls", "native-tls-crate/vendored", "tungstenite/native-tls-vendored", ] rustls-tls-native-roots = [ "__rustls-tls", "rustls-native-certs", ] rustls-tls-webpki-roots = [ "__rustls-tls", "webpki-roots", ] stream = [] tokio-tungstenite-0.20.1/Cargo.toml.orig000064400000000000000000000055051046102023000162740ustar 00000000000000[package] name = "tokio-tungstenite" description = "Tokio binding for Tungstenite, the Lightweight stream-based WebSocket implementation" categories = ["web-programming::websocket", "network-programming", "asynchronous", "concurrency"] keywords = ["websocket", "io", "web"] authors = ["Daniel Abramov ", "Alexey Galakhov "] license = "MIT" homepage = "https://github.com/snapview/tokio-tungstenite" documentation = "https://docs.rs/tokio-tungstenite/0.20.1" repository = "https://github.com/snapview/tokio-tungstenite" version = "0.20.1" edition = "2018" rust-version = "1.63" include = ["examples/**/*", "src/**/*", "LICENSE", "README.md", "CHANGELOG.md"] [package.metadata.docs.rs] features = ["native-tls", "__rustls-tls"] [features] default = ["connect", "handshake"] connect = ["stream", "tokio/net", "handshake"] handshake = ["tungstenite/handshake"] native-tls = ["native-tls-crate", "tokio-native-tls", "stream", "tungstenite/native-tls", "handshake"] native-tls-vendored = ["native-tls", "native-tls-crate/vendored", "tungstenite/native-tls-vendored"] rustls-tls-native-roots = ["__rustls-tls", "rustls-native-certs"] rustls-tls-webpki-roots = ["__rustls-tls", "webpki-roots"] __rustls-tls = ["rustls", "tokio-rustls", "stream", "tungstenite/__rustls-tls", "handshake"] stream = [] [dependencies] log = "0.4.17" futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] } tokio = { version = "1.0.0", default-features = false, features = ["io-util"] } [dependencies.tungstenite] version = "0.20.1" default-features = false [dependencies.native-tls-crate] optional = true package = "native-tls" version = "0.2.11" [dependencies.rustls] optional = true version = "0.21.6" [dependencies.rustls-native-certs] optional = true version = "0.6.2" [dependencies.tokio-native-tls] optional = true version = "0.3.1" [dependencies.tokio-rustls] optional = true version = "0.24.1" [dependencies.webpki-roots] optional = true version = "0.25.2" [dev-dependencies] futures-channel = "0.3.28" hyper = { version = "0.14.25", default-features = false, features = ["http1", "server", "tcp"] } tokio = { version = "1.27.0", default-features = false, features = ["io-std", "macros", "net", "rt-multi-thread", "time"] } url = "2.3.1" env_logger = "0.10.0" [[example]] name = "autobahn-client" required-features = ["connect"] [[example]] name = "autobahn-server" required-features = ["handshake"] [[example]] name = "client" required-features = ["connect"] [[example]] name = "echo-server" required-features = ["handshake"] [[example]] name = "server-custom-accept" required-features = ["handshake"] [[example]] name = "server" required-features = ["handshake"] [[example]] name = "server-headers" required-features = ["handshake"] [[example]] name = "interval-server" required-features = ["handshake"] tokio-tungstenite-0.20.1/LICENSE000064400000000000000000000021051046102023000144030ustar 00000000000000Copyright (c) 2017 Daniel Abramov Copyright (c) 2017 Alexey Galakhov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tokio-tungstenite-0.20.1/README.md000064400000000000000000000054641046102023000146700ustar 00000000000000# tokio-tungstenite Asynchronous WebSockets for Tokio stack. [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![Crates.io](https://img.shields.io/crates/v/tokio-tungstenite.svg?maxAge=2592000)](https://crates.io/crates/tokio-tungstenite) [![Build Status](https://travis-ci.org/snapview/tokio-tungstenite.svg?branch=master)](https://travis-ci.org/snapview/tokio-tungstenite) [Documentation](https://docs.rs/tokio-tungstenite) ## Usage Add this in your `Cargo.toml`: ```toml [dependencies] tokio-tungstenite = "*" ``` Take a look at the `examples/` directory for client and server examples. You may also want to get familiar with [Tokio](https://github.com/tokio-rs/tokio) if you don't have any experience with it. ## What is tokio-tungstenite? This crate is based on [`tungstenite-rs`](https://github.com/snapview/tungstenite-rs) Rust WebSocket library and provides `Tokio` bindings and wrappers for it, so you can use it with non-blocking/asynchronous `TcpStream`s from and couple it together with other crates from `Tokio` stack. ## Features As with [`tungstenite-rs`](https://github.com/snapview/tungstenite-rs) TLS is supported on all platforms using [`native-tls`](https://github.com/sfackler/rust-native-tls) or [`rustls`](https://github.com/ctz/rustls) through feature flags: `native-tls`, `rustls-tls-native-roots` or `rustls-tls-webpki-roots` feature flags. Neither is enabled by default. See the `Cargo.toml` for more information. If you require support for secure WebSockets (`wss://`) enable one of them. ## Is it performant? In essence, `tokio-tungstenite` is a wrapper for `tungstenite`, so the performance is capped by the performance of `tungstenite`. `tungstenite` has a decent performance (it has been used in production for real-time communication software, video conferencing, etc), but it's definitely not the fastest WebSocket library in the world at the moment of writing this note. If performance is of a paramount importance for you (especially if you send **large messages**), then you might want to check other libraries that have been designed to be performant or you could file a PR against `tungstenite` to improve the performance! We are aware of changes that both `tungstenite` and `tokio-tungstenite` need in order to fill the gap of ~30% performance difference between `tungstenite` and more performant libraries like `fastwebsockets`, but we have not worked on that yet as it was not required for the use case that original authors designed the library for. In the course of past years we have merged several performance improvements submitted by the awesome community of Rust users who helped to improve the library! For a quick summary of the pending performance problems/improvements, see [the comment](https://github.com/snapview/tungstenite-rs/issues/352#issuecomment-1537488614). tokio-tungstenite-0.20.1/examples/README.md000064400000000000000000000013361046102023000165000ustar 00000000000000Examples - [autobahn-client.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/autobahn-client.rs) - [autobahn-server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/autobahn-server.rs) - [client.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/client.rs) - [echo-server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/echo-server.rs) - [server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/server.rs) - [server-headers.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/server-headers.rs) - [interval-server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/interval-server.rs) tokio-tungstenite-0.20.1/examples/autobahn-client.rs000064400000000000000000000033771046102023000206530ustar 00000000000000use futures_util::{SinkExt, StreamExt}; use log::*; use tokio_tungstenite::{ connect_async, tungstenite::{Error, Result}, }; use url::Url; const AGENT: &str = "Tungstenite"; async fn get_case_count() -> Result { let (mut socket, _) = connect_async( Url::parse("ws://localhost:9001/getCaseCount").expect("Can't connect to case count URL"), ) .await?; let msg = socket.next().await.expect("Can't fetch case count")?; socket.close(None).await?; Ok(msg.into_text()?.parse::().expect("Can't parse case count")) } async fn update_reports() -> Result<()> { let (mut socket, _) = connect_async( Url::parse(&format!("ws://localhost:9001/updateReports?agent={}", AGENT)) .expect("Can't update reports"), ) .await?; socket.close(None).await?; Ok(()) } async fn run_test(case: u32) -> Result<()> { info!("Running test case {}", case); let case_url = Url::parse(&format!("ws://localhost:9001/runCase?case={}&agent={}", case, AGENT)) .expect("Bad testcase URL"); let (mut ws_stream, _) = connect_async(case_url).await?; while let Some(msg) = ws_stream.next().await { let msg = msg?; if msg.is_text() || msg.is_binary() { ws_stream.send(msg).await?; } } Ok(()) } #[tokio::main] async fn main() { env_logger::init(); let total = get_case_count().await.expect("Error getting case count"); for case in 1..=total { if let Err(e) = run_test(case).await { match e { Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (), err => error!("Testcase failed: {}", err), } } } update_reports().await.expect("Error updating reports"); } tokio-tungstenite-0.20.1/examples/autobahn-server.rs000064400000000000000000000025331046102023000206740ustar 00000000000000use futures_util::{SinkExt, StreamExt}; use log::*; use std::net::SocketAddr; use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::{ accept_async, tungstenite::{Error, Result}, }; async fn accept_connection(peer: SocketAddr, stream: TcpStream) { if let Err(e) = handle_connection(peer, stream).await { match e { Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (), err => error!("Error processing connection: {}", err), } } } async fn handle_connection(peer: SocketAddr, stream: TcpStream) -> Result<()> { let mut ws_stream = accept_async(stream).await.expect("Failed to accept"); info!("New WebSocket connection: {}", peer); while let Some(msg) = ws_stream.next().await { let msg = msg?; if msg.is_text() || msg.is_binary() { ws_stream.send(msg).await?; } } Ok(()) } #[tokio::main] async fn main() { env_logger::init(); let addr = "127.0.0.1:9002"; let listener = TcpListener::bind(&addr).await.expect("Can't listen"); info!("Listening on: {}", addr); while let Ok((stream, _)) = listener.accept().await { let peer = stream.peer_addr().expect("connected streams should have a peer address"); info!("Peer address: {}", peer); tokio::spawn(accept_connection(peer, stream)); } } tokio-tungstenite-0.20.1/examples/client.rs000064400000000000000000000037501046102023000170470ustar 00000000000000//! A simple example of hooking up stdin/stdout to a WebSocket stream. //! //! This example will connect to a server specified in the argument list and //! then forward all data read on stdin to the server, printing out all data //! received on stdout. //! //! Note that this is not currently optimized for performance, especially around //! buffer management. Rather it's intended to show an example of working with a //! client. //! //! You can use this example together with the `server` example. use std::env; use futures_util::{future, pin_mut, StreamExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; #[tokio::main] async fn main() { let connect_addr = env::args().nth(1).unwrap_or_else(|| panic!("this program requires at least one argument")); let url = url::Url::parse(&connect_addr).unwrap(); let (stdin_tx, stdin_rx) = futures_channel::mpsc::unbounded(); tokio::spawn(read_stdin(stdin_tx)); let (ws_stream, _) = connect_async(url).await.expect("Failed to connect"); println!("WebSocket handshake has been successfully completed"); let (write, read) = ws_stream.split(); let stdin_to_ws = stdin_rx.map(Ok).forward(write); let ws_to_stdout = { read.for_each(|message| async { let data = message.unwrap().into_data(); tokio::io::stdout().write_all(&data).await.unwrap(); }) }; pin_mut!(stdin_to_ws, ws_to_stdout); future::select(stdin_to_ws, ws_to_stdout).await; } // Our helper method which will read data from stdin and send it along the // sender provided. async fn read_stdin(tx: futures_channel::mpsc::UnboundedSender) { let mut stdin = tokio::io::stdin(); loop { let mut buf = vec![0; 1024]; let n = match stdin.read(&mut buf).await { Err(_) | Ok(0) => break, Ok(n) => n, }; buf.truncate(n); tx.unbounded_send(Message::binary(buf)).unwrap(); } } tokio-tungstenite-0.20.1/examples/echo-server.rs000064400000000000000000000031351046102023000200100ustar 00000000000000//! A simple echo server. //! //! You can test this out by running: //! //! cargo run --example echo-server 127.0.0.1:12345 //! //! And then in another window run: //! //! cargo run --example client ws://127.0.0.1:12345/ //! //! Type a message into the client window, press enter to send it and //! see it echoed back. use std::{env, io::Error}; use futures_util::{future, StreamExt, TryStreamExt}; use log::info; use tokio::net::{TcpListener, TcpStream}; #[tokio::main] async fn main() -> Result<(), Error> { let _ = env_logger::try_init(); let addr = env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string()); // Create the event loop and TCP listener we'll accept connections on. let try_socket = TcpListener::bind(&addr).await; let listener = try_socket.expect("Failed to bind"); info!("Listening on: {}", addr); while let Ok((stream, _)) = listener.accept().await { tokio::spawn(accept_connection(stream)); } Ok(()) } async fn accept_connection(stream: TcpStream) { let addr = stream.peer_addr().expect("connected streams should have a peer address"); info!("Peer address: {}", addr); let ws_stream = tokio_tungstenite::accept_async(stream) .await .expect("Error during the websocket handshake occurred"); info!("New WebSocket connection: {}", addr); let (write, read) = ws_stream.split(); // We should not forward messages other than text or binary. read.try_filter(|msg| future::ready(msg.is_text() || msg.is_binary())) .forward(write) .await .expect("Failed to forward messages") } tokio-tungstenite-0.20.1/examples/interval-server.rs000064400000000000000000000040371046102023000207200ustar 00000000000000use futures_util::{SinkExt, StreamExt}; use log::*; use std::{net::SocketAddr, time::Duration}; use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::{ accept_async, tungstenite::{Error, Message, Result}, }; async fn accept_connection(peer: SocketAddr, stream: TcpStream) { if let Err(e) = handle_connection(peer, stream).await { match e { Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (), err => error!("Error processing connection: {}", err), } } } async fn handle_connection(peer: SocketAddr, stream: TcpStream) -> Result<()> { let ws_stream = accept_async(stream).await.expect("Failed to accept"); info!("New WebSocket connection: {}", peer); let (mut ws_sender, mut ws_receiver) = ws_stream.split(); let mut interval = tokio::time::interval(Duration::from_millis(1000)); // Echo incoming WebSocket messages and send a message periodically every second. loop { tokio::select! { msg = ws_receiver.next() => { match msg { Some(msg) => { let msg = msg?; if msg.is_text() ||msg.is_binary() { ws_sender.send(msg).await?; } else if msg.is_close() { break; } } None => break, } } _ = interval.tick() => { ws_sender.send(Message::Text("tick".to_owned())).await?; } } } Ok(()) } #[tokio::main] async fn main() { env_logger::init(); let addr = "127.0.0.1:9002"; let listener = TcpListener::bind(&addr).await.expect("Can't listen"); info!("Listening on: {}", addr); while let Ok((stream, _)) = listener.accept().await { let peer = stream.peer_addr().expect("connected streams should have a peer address"); info!("Peer address: {}", peer); tokio::spawn(accept_connection(peer, stream)); } } tokio-tungstenite-0.20.1/examples/server-custom-accept.rs000064400000000000000000000131561046102023000216450ustar 00000000000000//! A chat server that broadcasts a message to all connections. //! //! This is a simple line-based server which accepts WebSocket connections, //! reads lines from those connections, and broadcasts the lines to all other //! connected clients. //! //! You can test this out by running: //! //! cargo run --example server 127.0.0.1:12345 //! //! And then in another window run: //! //! cargo run --example client ws://127.0.0.1:12345/socket //! //! You can run the second command in multiple windows and then chat between the //! two, seeing the messages from the other client as they're received. For all //! connected clients they'll all join the same room and see everyone else's //! messages. use std::{ collections::HashMap, convert::Infallible, env, net::SocketAddr, sync::{Arc, Mutex}, }; use hyper::{ header::{ HeaderValue, CONNECTION, SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_VERSION, UPGRADE, }, server::conn::AddrStream, service::{make_service_fn, service_fn}, upgrade::Upgraded, Body, Method, Request, Response, Server, StatusCode, Version, }; use futures_channel::mpsc::{unbounded, UnboundedSender}; use futures_util::{future, pin_mut, stream::TryStreamExt, StreamExt}; use tokio_tungstenite::{ tungstenite::{ handshake::derive_accept_key, protocol::{Message, Role}, }, WebSocketStream, }; type Tx = UnboundedSender; type PeerMap = Arc>>; async fn handle_connection( peer_map: PeerMap, ws_stream: WebSocketStream, addr: SocketAddr, ) { println!("WebSocket connection established: {}", addr); // Insert the write part of this peer to the peer map. let (tx, rx) = unbounded(); peer_map.lock().unwrap().insert(addr, tx); let (outgoing, incoming) = ws_stream.split(); let broadcast_incoming = incoming.try_for_each(|msg| { println!("Received a message from {}: {}", addr, msg.to_text().unwrap()); let peers = peer_map.lock().unwrap(); // We want to broadcast the message to everyone except ourselves. let broadcast_recipients = peers.iter().filter(|(peer_addr, _)| peer_addr != &&addr).map(|(_, ws_sink)| ws_sink); for recp in broadcast_recipients { recp.unbounded_send(msg.clone()).unwrap(); } future::ok(()) }); let receive_from_others = rx.map(Ok).forward(outgoing); pin_mut!(broadcast_incoming, receive_from_others); future::select(broadcast_incoming, receive_from_others).await; println!("{} disconnected", &addr); peer_map.lock().unwrap().remove(&addr); } async fn handle_request( peer_map: PeerMap, mut req: Request, addr: SocketAddr, ) -> Result, Infallible> { println!("Received a new, potentially ws handshake"); println!("The request's path is: {}", req.uri().path()); println!("The request's headers are:"); for (ref header, _value) in req.headers() { println!("* {}", header); } let upgrade = HeaderValue::from_static("Upgrade"); let websocket = HeaderValue::from_static("websocket"); let headers = req.headers(); let key = headers.get(SEC_WEBSOCKET_KEY); let derived = key.map(|k| derive_accept_key(k.as_bytes())); if req.method() != Method::GET || req.version() < Version::HTTP_11 || !headers .get(CONNECTION) .and_then(|h| h.to_str().ok()) .map(|h| { h.split(|c| c == ' ' || c == ',') .any(|p| p.eq_ignore_ascii_case(upgrade.to_str().unwrap())) }) .unwrap_or(false) || !headers .get(UPGRADE) .and_then(|h| h.to_str().ok()) .map(|h| h.eq_ignore_ascii_case("websocket")) .unwrap_or(false) || !headers.get(SEC_WEBSOCKET_VERSION).map(|h| h == "13").unwrap_or(false) || key.is_none() || req.uri() != "/socket" { return Ok(Response::new(Body::from("Hello World!"))); } let ver = req.version(); tokio::task::spawn(async move { match hyper::upgrade::on(&mut req).await { Ok(upgraded) => { handle_connection( peer_map, WebSocketStream::from_raw_socket(upgraded, Role::Server, None).await, addr, ) .await; } Err(e) => println!("upgrade error: {}", e), } }); let mut res = Response::new(Body::empty()); *res.status_mut() = StatusCode::SWITCHING_PROTOCOLS; *res.version_mut() = ver; res.headers_mut().append(CONNECTION, upgrade); res.headers_mut().append(UPGRADE, websocket); res.headers_mut().append(SEC_WEBSOCKET_ACCEPT, derived.unwrap().parse().unwrap()); // Let's add an additional header to our response to the client. res.headers_mut().append("MyCustomHeader", ":)".parse().unwrap()); res.headers_mut().append("SOME_TUNGSTENITE_HEADER", "header_value".parse().unwrap()); Ok(res) } #[tokio::main] async fn main() -> Result<(), hyper::Error> { let state = PeerMap::new(Mutex::new(HashMap::new())); let addr = env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string()).parse().unwrap(); let make_svc = make_service_fn(move |conn: &AddrStream| { let remote_addr = conn.remote_addr(); let state = state.clone(); let service = service_fn(move |req| handle_request(state.clone(), req, remote_addr)); async { Ok::<_, Infallible>(service) } }); let server = Server::bind(&addr).serve(make_svc); server.await?; Ok::<_, hyper::Error>(()) } tokio-tungstenite-0.20.1/examples/server-headers.rs000064400000000000000000000045401046102023000205060ustar 00000000000000//! Read/Write headers on server example //! //! Run with logs: //! Linux: //! ```sh //! RUST_LOG=debug cargo run --example server-headers //! ``` //! Windows //! ```sh //! cmd /c "set RUST_LOG=debug && cargo run --example server-headers" //! ``` use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::{ accept_hdr_async, tungstenite::{ connect, handshake::server::{Request, Response}, Message, }, }; use url::Url; #[macro_use] extern crate log; use futures_util::{SinkExt, StreamExt}; #[tokio::main] async fn main() { env_logger::builder().format_timestamp(None).init(); tokio::spawn(async move { server().await; }); client(); } async fn server() { let server = TcpListener::bind("127.0.0.1:8080").await.unwrap(); while let Ok((stream, _)) = server.accept().await { tokio::spawn(accept_connection(stream)); } } async fn accept_connection(stream: TcpStream) { let callback = |req: &Request, mut response: Response| { debug!("Received a new ws handshake"); debug!("The request's path is: {}", req.uri().path()); debug!("The request's headers are:"); for (ref header, _value) in req.headers() { debug!("* {}: {:?}", header, _value); } let headers = response.headers_mut(); headers.append("MyCustomHeader", ":)".parse().unwrap()); Ok(response) }; let mut ws_stream = accept_hdr_async(stream, callback) .await .expect("Error during the websocket handshake occurred"); while let Some(msg) = ws_stream.next().await { let msg = msg.unwrap(); if msg.is_text() || msg.is_binary() { debug!("Server on message: {:?}", &msg); ws_stream.send(msg).await.unwrap(); } } } fn client() { let (mut socket, response) = connect(Url::parse("ws://localhost:8080/socket").unwrap()).expect("Can't connect"); debug!("Connected to the server"); debug!("Response HTTP code: {}", response.status()); debug!("Response contains the following headers:"); for (ref header, _value) in response.headers() { debug!("* {}: {:?}", header, _value); } socket.send(Message::Text("Hello WebSocket".into())).unwrap(); loop { let msg = socket.read().expect("Error reading message"); debug!("Received: {}", msg); } } tokio-tungstenite-0.20.1/examples/server.rs000064400000000000000000000060751046102023000171020ustar 00000000000000//! A chat server that broadcasts a message to all connections. //! //! This is a simple line-based server which accepts WebSocket connections, //! reads lines from those connections, and broadcasts the lines to all other //! connected clients. //! //! You can test this out by running: //! //! cargo run --example server 127.0.0.1:12345 //! //! And then in another window run: //! //! cargo run --example client ws://127.0.0.1:12345/ //! //! You can run the second command in multiple windows and then chat between the //! two, seeing the messages from the other client as they're received. For all //! connected clients they'll all join the same room and see everyone else's //! messages. use std::{ collections::HashMap, env, io::Error as IoError, net::SocketAddr, sync::{Arc, Mutex}, }; use futures_channel::mpsc::{unbounded, UnboundedSender}; use futures_util::{future, pin_mut, stream::TryStreamExt, StreamExt}; use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::tungstenite::protocol::Message; type Tx = UnboundedSender; type PeerMap = Arc>>; async fn handle_connection(peer_map: PeerMap, raw_stream: TcpStream, addr: SocketAddr) { println!("Incoming TCP connection from: {}", addr); let ws_stream = tokio_tungstenite::accept_async(raw_stream) .await .expect("Error during the websocket handshake occurred"); println!("WebSocket connection established: {}", addr); // Insert the write part of this peer to the peer map. let (tx, rx) = unbounded(); peer_map.lock().unwrap().insert(addr, tx); let (outgoing, incoming) = ws_stream.split(); let broadcast_incoming = incoming.try_for_each(|msg| { println!("Received a message from {}: {}", addr, msg.to_text().unwrap()); let peers = peer_map.lock().unwrap(); // We want to broadcast the message to everyone except ourselves. let broadcast_recipients = peers.iter().filter(|(peer_addr, _)| peer_addr != &&addr).map(|(_, ws_sink)| ws_sink); for recp in broadcast_recipients { recp.unbounded_send(msg.clone()).unwrap(); } future::ok(()) }); let receive_from_others = rx.map(Ok).forward(outgoing); pin_mut!(broadcast_incoming, receive_from_others); future::select(broadcast_incoming, receive_from_others).await; println!("{} disconnected", &addr); peer_map.lock().unwrap().remove(&addr); } #[tokio::main] async fn main() -> Result<(), IoError> { let addr = env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string()); let state = PeerMap::new(Mutex::new(HashMap::new())); // Create the event loop and TCP listener we'll accept connections on. let try_socket = TcpListener::bind(&addr).await; let listener = try_socket.expect("Failed to bind"); println!("Listening on: {}", addr); // Let's spawn the handling of each connection in a separate task. while let Ok((stream, addr)) = listener.accept().await { tokio::spawn(handle_connection(state.clone(), stream, addr)); } Ok(()) } tokio-tungstenite-0.20.1/src/compat.rs000064400000000000000000000153671046102023000160340ustar 00000000000000use log::*; use std::{ io::{Read, Write}, pin::Pin, task::{Context, Poll}, }; use futures_util::task; use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tungstenite::Error as WsError; pub(crate) enum ContextWaker { Read, Write, } #[derive(Debug)] pub(crate) struct AllowStd { inner: S, // We have the problem that external read operations (i.e. the Stream impl) // can trigger both read (AsyncRead) and write (AsyncWrite) operations on // the underyling stream. At the same time write operations (i.e. the Sink // impl) can trigger write operations (AsyncWrite) too. // Both the Stream and the Sink can be used on two different tasks, but it // is required that AsyncRead and AsyncWrite are only ever used by a single // task (or better: with a single waker) at a time. // // Doing otherwise would cause only the latest waker to be remembered, so // in our case either the Stream or the Sink impl would potentially wait // forever to be woken up because only the other one would've been woken // up. // // To solve this we implement a waker proxy that has two slots (one for // read, one for write) to store wakers. One waker proxy is always passed // to the AsyncRead, the other to AsyncWrite so that they will only ever // have to store a single waker, but internally we dispatch any wakeups to // up to two actual wakers (one from the Sink impl and one from the Stream // impl). // // write_waker_proxy is always used for AsyncWrite, read_waker_proxy for // AsyncRead. The read_waker slots of both are used for the Stream impl // (and handshaking), the write_waker slots for the Sink impl. write_waker_proxy: Arc, read_waker_proxy: Arc, } // Internal trait used only in the Handshake module for registering // the waker for the context used during handshaking. We're using the // read waker slot for this, but any would do. // // Don't ever use this from multiple tasks at the same time! pub(crate) trait SetWaker { fn set_waker(&self, waker: &task::Waker); } impl SetWaker for AllowStd { fn set_waker(&self, waker: &task::Waker) { self.set_waker(ContextWaker::Read, waker); } } impl AllowStd { pub(crate) fn new(inner: S, waker: &task::Waker) -> Self { let res = Self { inner, write_waker_proxy: Default::default(), read_waker_proxy: Default::default(), }; // Register the handshake waker as read waker for both proxies, // see also the SetWaker trait. res.write_waker_proxy.read_waker.register(waker); res.read_waker_proxy.read_waker.register(waker); res } // Set the read or write waker for our proxies. // // Read: this is only supposed to be called by read (or handshake) operations, i.e. the Stream // impl on the WebSocketStream. // Reading can also cause writes to happen, e.g. in case of Message::Ping handling. // // Write: this is only supposde to be called by write operations, i.e. the Sink impl on the // WebSocketStream. pub(crate) fn set_waker(&self, kind: ContextWaker, waker: &task::Waker) { match kind { ContextWaker::Read => { self.write_waker_proxy.read_waker.register(waker); self.read_waker_proxy.read_waker.register(waker); } ContextWaker::Write => { self.write_waker_proxy.write_waker.register(waker); self.read_waker_proxy.write_waker.register(waker); } } } } // Proxy Waker that we pass to the internal AsyncRead/Write of the // stream underlying the websocket. We have two slots here for the // actual wakers to allow external read operations to trigger both // reads and writes, and the same for writes. #[derive(Debug, Default)] struct WakerProxy { read_waker: task::AtomicWaker, write_waker: task::AtomicWaker, } impl task::ArcWake for WakerProxy { fn wake_by_ref(arc_self: &Arc) { arc_self.read_waker.wake(); arc_self.write_waker.wake(); } } impl AllowStd where S: Unpin, { fn with_context(&mut self, kind: ContextWaker, f: F) -> Poll> where F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> Poll>, { trace!("{}:{} AllowStd.with_context", file!(), line!()); let waker = match kind { ContextWaker::Read => task::waker_ref(&self.read_waker_proxy), ContextWaker::Write => task::waker_ref(&self.write_waker_proxy), }; let mut context = task::Context::from_waker(&waker); f(&mut context, Pin::new(&mut self.inner)) } pub(crate) fn get_mut(&mut self) -> &mut S { &mut self.inner } pub(crate) fn get_ref(&self) -> &S { &self.inner } } impl Read for AllowStd where S: AsyncRead + Unpin, { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { trace!("{}:{} Read.read", file!(), line!()); let mut buf = ReadBuf::new(buf); match self.with_context(ContextWaker::Read, |ctx, stream| { trace!("{}:{} Read.with_context read -> poll_read", file!(), line!()); stream.poll_read(ctx, &mut buf) }) { Poll::Ready(Ok(_)) => Ok(buf.filled().len()), Poll::Ready(Err(err)) => Err(err), Poll::Pending => Err(std::io::Error::from(std::io::ErrorKind::WouldBlock)), } } } impl Write for AllowStd where S: AsyncWrite + Unpin, { fn write(&mut self, buf: &[u8]) -> std::io::Result { trace!("{}:{} Write.write", file!(), line!()); match self.with_context(ContextWaker::Write, |ctx, stream| { trace!("{}:{} Write.with_context write -> poll_write", file!(), line!()); stream.poll_write(ctx, buf) }) { Poll::Ready(r) => r, Poll::Pending => Err(std::io::Error::from(std::io::ErrorKind::WouldBlock)), } } fn flush(&mut self) -> std::io::Result<()> { trace!("{}:{} Write.flush", file!(), line!()); match self.with_context(ContextWaker::Write, |ctx, stream| { trace!("{}:{} Write.with_context flush -> poll_flush", file!(), line!()); stream.poll_flush(ctx) }) { Poll::Ready(r) => r, Poll::Pending => Err(std::io::Error::from(std::io::ErrorKind::WouldBlock)), } } } pub(crate) fn cvt(r: Result) -> Poll> { match r { Ok(v) => Poll::Ready(Ok(v)), Err(WsError::Io(ref e)) if e.kind() == std::io::ErrorKind::WouldBlock => { trace!("WouldBlock"); Poll::Pending } Err(e) => Poll::Ready(Err(e)), } } tokio-tungstenite-0.20.1/src/connect.rs000064400000000000000000000052731046102023000161750ustar 00000000000000//! Connection helper. use tokio::net::TcpStream; use tungstenite::{ error::{Error, UrlError}, handshake::client::{Request, Response}, protocol::WebSocketConfig, }; use crate::{domain, stream::MaybeTlsStream, Connector, IntoClientRequest, WebSocketStream}; /// Connect to a given URL. pub async fn connect_async( request: R, ) -> Result<(WebSocketStream>, Response), Error> where R: IntoClientRequest + Unpin, { connect_async_with_config(request, None, false).await } /// The same as `connect_async()` but the one can specify a websocket configuration. /// Please refer to `connect_async()` for more details. `disable_nagle` specifies if /// the Nagle's algorithm must be disabled, i.e. `set_nodelay(true)`. If you don't know /// what the Nagle's algorithm is, better leave it set to `false`. pub async fn connect_async_with_config( request: R, config: Option, disable_nagle: bool, ) -> Result<(WebSocketStream>, Response), Error> where R: IntoClientRequest + Unpin, { connect(request.into_client_request()?, config, disable_nagle, None).await } /// The same as `connect_async()` but the one can specify a websocket configuration, /// and a TLS connector to use. Please refer to `connect_async()` for more details. /// `disable_nagle` specifies if the Nagle's algorithm must be disabled, i.e. /// `set_nodelay(true)`. If you don't know what the Nagle's algorithm is, better /// leave it to `false`. #[cfg(any(feature = "native-tls", feature = "__rustls-tls"))] pub async fn connect_async_tls_with_config( request: R, config: Option, disable_nagle: bool, connector: Option, ) -> Result<(WebSocketStream>, Response), Error> where R: IntoClientRequest + Unpin, { connect(request.into_client_request()?, config, disable_nagle, connector).await } async fn connect( request: Request, config: Option, disable_nagle: bool, connector: Option, ) -> Result<(WebSocketStream>, Response), Error> { let domain = domain(&request)?; let port = request .uri() .port_u16() .or_else(|| match request.uri().scheme_str() { Some("wss") => Some(443), Some("ws") => Some(80), _ => None, }) .ok_or(Error::Url(UrlError::UnsupportedUrlScheme))?; let addr = format!("{domain}:{port}"); let socket = TcpStream::connect(addr).await.map_err(Error::Io)?; if disable_nagle { socket.set_nodelay(true)?; } crate::tls::client_async_tls_with_config(request, socket, config, connector).await } tokio-tungstenite-0.20.1/src/handshake.rs000064400000000000000000000125561046102023000164740ustar 00000000000000#[cfg(feature = "handshake")] use crate::compat::SetWaker; use crate::{compat::AllowStd, WebSocketStream}; use log::*; use std::{ future::Future, io::{Read, Write}, pin::Pin, task::{Context, Poll}, }; use tokio::io::{AsyncRead, AsyncWrite}; use tungstenite::WebSocket; #[cfg(feature = "handshake")] use tungstenite::{ handshake::{ client::Response, server::Callback, HandshakeError as Error, HandshakeRole, MidHandshake as WsHandshake, }, ClientHandshake, ServerHandshake, }; pub(crate) async fn without_handshake(stream: S, f: F) -> WebSocketStream where F: FnOnce(AllowStd) -> WebSocket> + Unpin, S: AsyncRead + AsyncWrite + Unpin, { let start = SkippedHandshakeFuture(Some(SkippedHandshakeFutureInner { f, stream })); let ws = start.await; WebSocketStream::new(ws) } struct SkippedHandshakeFuture(Option>); struct SkippedHandshakeFutureInner { f: F, stream: S, } impl Future for SkippedHandshakeFuture where F: FnOnce(AllowStd) -> WebSocket> + Unpin, S: Unpin, AllowStd: Read + Write, { type Output = WebSocket>; fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { let inner = self.get_mut().0.take().expect("future polled after completion"); trace!("Setting context when skipping handshake"); let stream = AllowStd::new(inner.stream, ctx.waker()); Poll::Ready((inner.f)(stream)) } } #[cfg(feature = "handshake")] struct MidHandshake(Option>); #[cfg(feature = "handshake")] enum StartedHandshake { Done(Role::FinalResult), Mid(WsHandshake), } #[cfg(feature = "handshake")] struct StartedHandshakeFuture(Option>); #[cfg(feature = "handshake")] struct StartedHandshakeFutureInner { f: F, stream: S, } #[cfg(feature = "handshake")] async fn handshake(stream: S, f: F) -> Result> where Role: HandshakeRole + Unpin, Role::InternalStream: SetWaker + Unpin, F: FnOnce(AllowStd) -> Result> + Unpin, S: AsyncRead + AsyncWrite + Unpin, { let start = StartedHandshakeFuture(Some(StartedHandshakeFutureInner { f, stream })); match start.await? { StartedHandshake::Done(r) => Ok(r), StartedHandshake::Mid(s) => { let res: Result> = MidHandshake::(Some(s)).await; res } } } #[cfg(feature = "handshake")] pub(crate) async fn client_handshake( stream: S, f: F, ) -> Result<(WebSocketStream, Response), Error>>> where F: FnOnce( AllowStd, ) -> Result< > as HandshakeRole>::FinalResult, Error>>, > + Unpin, S: AsyncRead + AsyncWrite + Unpin, { let result = handshake(stream, f).await?; let (s, r) = result; Ok((WebSocketStream::new(s), r)) } #[cfg(feature = "handshake")] pub(crate) async fn server_handshake( stream: S, f: F, ) -> Result, Error, C>>> where C: Callback + Unpin, F: FnOnce( AllowStd, ) -> Result< , C> as HandshakeRole>::FinalResult, Error, C>>, > + Unpin, S: AsyncRead + AsyncWrite + Unpin, { let s: WebSocket> = handshake(stream, f).await?; Ok(WebSocketStream::new(s)) } #[cfg(feature = "handshake")] impl Future for StartedHandshakeFuture where Role: HandshakeRole, Role::InternalStream: SetWaker + Unpin, F: FnOnce(AllowStd) -> Result> + Unpin, S: Unpin, AllowStd: Read + Write, { type Output = Result, Error>; fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { let inner = self.0.take().expect("future polled after completion"); trace!("Setting ctx when starting handshake"); let stream = AllowStd::new(inner.stream, ctx.waker()); match (inner.f)(stream) { Ok(r) => Poll::Ready(Ok(StartedHandshake::Done(r))), Err(Error::Interrupted(mid)) => Poll::Ready(Ok(StartedHandshake::Mid(mid))), Err(Error::Failure(e)) => Poll::Ready(Err(Error::Failure(e))), } } } #[cfg(feature = "handshake")] impl Future for MidHandshake where Role: HandshakeRole + Unpin, Role::InternalStream: SetWaker + Unpin, { type Output = Result>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut s = self.as_mut().0.take().expect("future polled after completion"); let machine = s.get_mut(); trace!("Setting context in handshake"); machine.get_mut().set_waker(cx.waker()); match s.handshake() { Ok(stream) => Poll::Ready(Ok(stream)), Err(Error::Failure(e)) => Poll::Ready(Err(Error::Failure(e))), Err(Error::Interrupted(mid)) => { self.0 = Some(mid); Poll::Pending } } } } tokio-tungstenite-0.20.1/src/lib.rs000064400000000000000000000351541046102023000153130ustar 00000000000000//! Async WebSocket usage. //! //! This library is an implementation of WebSocket handshakes and streams. It //! is based on the crate which implements all required WebSocket protocol //! logic. So this crate basically just brings tokio support / tokio integration //! to it. //! //! Each WebSocket stream implements the required `Stream` and `Sink` traits, //! so the socket is just a stream of messages coming in and going out. #![deny(missing_docs, unused_must_use, unused_mut, unused_imports, unused_import_braces)] pub use tungstenite; mod compat; #[cfg(feature = "connect")] mod connect; mod handshake; #[cfg(feature = "stream")] mod stream; #[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect"))] mod tls; use std::io::{Read, Write}; use compat::{cvt, AllowStd, ContextWaker}; use futures_util::{ sink::{Sink, SinkExt}, stream::{FusedStream, Stream}, }; use log::*; use std::{ pin::Pin, task::{Context, Poll}, }; use tokio::io::{AsyncRead, AsyncWrite}; #[cfg(feature = "handshake")] use tungstenite::{ client::IntoClientRequest, handshake::{ client::{ClientHandshake, Response}, server::{Callback, NoCallback}, HandshakeError, }, }; use tungstenite::{ error::Error as WsError, protocol::{Message, Role, WebSocket, WebSocketConfig}, }; #[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect"))] pub use tls::Connector; #[cfg(any(feature = "native-tls", feature = "__rustls-tls"))] pub use tls::{client_async_tls, client_async_tls_with_config}; #[cfg(feature = "connect")] pub use connect::{connect_async, connect_async_with_config}; #[cfg(all(any(feature = "native-tls", feature = "__rustls-tls"), feature = "connect"))] pub use connect::connect_async_tls_with_config; #[cfg(feature = "stream")] pub use stream::MaybeTlsStream; use tungstenite::protocol::CloseFrame; /// Creates a WebSocket handshake from a request and a stream. /// For convenience, the user may call this with a url string, a URL, /// or a `Request`. Calling with `Request` allows the user to add /// a WebSocket protocol or other custom headers. /// /// Internally, this custom creates a handshake representation and returns /// a future representing the resolution of the WebSocket handshake. The /// returned future will resolve to either `WebSocketStream` or `Error` /// depending on whether the handshake is successful. /// /// This is typically used for clients who have already established, for /// example, a TCP connection to the remote server. #[cfg(feature = "handshake")] pub async fn client_async<'a, R, S>( request: R, stream: S, ) -> Result<(WebSocketStream, Response), WsError> where R: IntoClientRequest + Unpin, S: AsyncRead + AsyncWrite + Unpin, { client_async_with_config(request, stream, None).await } /// The same as `client_async()` but the one can specify a websocket configuration. /// Please refer to `client_async()` for more details. #[cfg(feature = "handshake")] pub async fn client_async_with_config<'a, R, S>( request: R, stream: S, config: Option, ) -> Result<(WebSocketStream, Response), WsError> where R: IntoClientRequest + Unpin, S: AsyncRead + AsyncWrite + Unpin, { let f = handshake::client_handshake(stream, move |allow_std| { let request = request.into_client_request()?; let cli_handshake = ClientHandshake::start(allow_std, request, config)?; cli_handshake.handshake() }); f.await.map_err(|e| match e { HandshakeError::Failure(e) => e, e => WsError::Io(std::io::Error::new(std::io::ErrorKind::Other, e.to_string())), }) } /// Accepts a new WebSocket connection with the provided stream. /// /// This function will internally call `server::accept` to create a /// handshake representation and returns a future representing the /// resolution of the WebSocket handshake. The returned future will resolve /// to either `WebSocketStream` or `Error` depending if it's successful /// or not. /// /// This is typically used after a socket has been accepted from a /// `TcpListener`. That socket is then passed to this function to perform /// the server half of the accepting a client's websocket connection. #[cfg(feature = "handshake")] pub async fn accept_async(stream: S) -> Result, WsError> where S: AsyncRead + AsyncWrite + Unpin, { accept_hdr_async(stream, NoCallback).await } /// The same as `accept_async()` but the one can specify a websocket configuration. /// Please refer to `accept_async()` for more details. #[cfg(feature = "handshake")] pub async fn accept_async_with_config( stream: S, config: Option, ) -> Result, WsError> where S: AsyncRead + AsyncWrite + Unpin, { accept_hdr_async_with_config(stream, NoCallback, config).await } /// Accepts a new WebSocket connection with the provided stream. /// /// This function does the same as `accept_async()` but accepts an extra callback /// for header processing. The callback receives headers of the incoming /// requests and is able to add extra headers to the reply. #[cfg(feature = "handshake")] pub async fn accept_hdr_async(stream: S, callback: C) -> Result, WsError> where S: AsyncRead + AsyncWrite + Unpin, C: Callback + Unpin, { accept_hdr_async_with_config(stream, callback, None).await } /// The same as `accept_hdr_async()` but the one can specify a websocket configuration. /// Please refer to `accept_hdr_async()` for more details. #[cfg(feature = "handshake")] pub async fn accept_hdr_async_with_config( stream: S, callback: C, config: Option, ) -> Result, WsError> where S: AsyncRead + AsyncWrite + Unpin, C: Callback + Unpin, { let f = handshake::server_handshake(stream, move |allow_std| { tungstenite::accept_hdr_with_config(allow_std, callback, config) }); f.await.map_err(|e| match e { HandshakeError::Failure(e) => e, e => WsError::Io(std::io::Error::new(std::io::ErrorKind::Other, e.to_string())), }) } /// A wrapper around an underlying raw stream which implements the WebSocket /// protocol. /// /// A `WebSocketStream` represents a handshake that has been completed /// successfully and both the server and the client are ready for receiving /// and sending data. Message from a `WebSocketStream` are accessible /// through the respective `Stream` and `Sink`. Check more information about /// them in `futures-rs` crate documentation or have a look on the examples /// and unit tests for this crate. #[derive(Debug)] pub struct WebSocketStream { inner: WebSocket>, closing: bool, ended: bool, /// Tungstenite is probably ready to receive more data. /// /// `false` once start_send hits `WouldBlock` errors. /// `true` initially and after `flush`ing. ready: bool, } impl WebSocketStream { /// Convert a raw socket into a WebSocketStream without performing a /// handshake. pub async fn from_raw_socket(stream: S, role: Role, config: Option) -> Self where S: AsyncRead + AsyncWrite + Unpin, { handshake::without_handshake(stream, move |allow_std| { WebSocket::from_raw_socket(allow_std, role, config) }) .await } /// Convert a raw socket into a WebSocketStream without performing a /// handshake. pub async fn from_partially_read( stream: S, part: Vec, role: Role, config: Option, ) -> Self where S: AsyncRead + AsyncWrite + Unpin, { handshake::without_handshake(stream, move |allow_std| { WebSocket::from_partially_read(allow_std, part, role, config) }) .await } pub(crate) fn new(ws: WebSocket>) -> Self { Self { inner: ws, closing: false, ended: false, ready: true } } fn with_context(&mut self, ctx: Option<(ContextWaker, &mut Context<'_>)>, f: F) -> R where S: Unpin, F: FnOnce(&mut WebSocket>) -> R, AllowStd: Read + Write, { trace!("{}:{} WebSocketStream.with_context", file!(), line!()); if let Some((kind, ctx)) = ctx { self.inner.get_mut().set_waker(kind, ctx.waker()); } f(&mut self.inner) } /// Returns a shared reference to the inner stream. pub fn get_ref(&self) -> &S where S: AsyncRead + AsyncWrite + Unpin, { self.inner.get_ref().get_ref() } /// Returns a mutable reference to the inner stream. pub fn get_mut(&mut self) -> &mut S where S: AsyncRead + AsyncWrite + Unpin, { self.inner.get_mut().get_mut() } /// Returns a reference to the configuration of the tungstenite stream. pub fn get_config(&self) -> &WebSocketConfig { self.inner.get_config() } /// Close the underlying web socket pub async fn close(&mut self, msg: Option>) -> Result<(), WsError> where S: AsyncRead + AsyncWrite + Unpin, { let msg = msg.map(|msg| msg.into_owned()); self.send(Message::Close(msg)).await } } impl Stream for WebSocketStream where T: AsyncRead + AsyncWrite + Unpin, { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { trace!("{}:{} Stream.poll_next", file!(), line!()); // The connection has been closed or a critical error has occurred. // We have already returned the error to the user, the `Stream` is unusable, // so we assume that the stream has been "fused". if self.ended { return Poll::Ready(None); } match futures_util::ready!(self.with_context(Some((ContextWaker::Read, cx)), |s| { trace!("{}:{} Stream.with_context poll_next -> read()", file!(), line!()); cvt(s.read()) })) { Ok(v) => Poll::Ready(Some(Ok(v))), Err(e) => { self.ended = true; if matches!(e, WsError::AlreadyClosed | WsError::ConnectionClosed) { Poll::Ready(None) } else { Poll::Ready(Some(Err(e))) } } } } } impl FusedStream for WebSocketStream where T: AsyncRead + AsyncWrite + Unpin, { fn is_terminated(&self) -> bool { self.ended } } impl Sink for WebSocketStream where T: AsyncRead + AsyncWrite + Unpin, { type Error = WsError; fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.ready { Poll::Ready(Ok(())) } else { // Currently blocked so try to flush the blockage away (*self).with_context(Some((ContextWaker::Write, cx)), |s| cvt(s.flush())).map(|r| { self.ready = true; r }) } } fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> { match (*self).with_context(None, |s| s.write(item)) { Ok(()) => { self.ready = true; Ok(()) } Err(WsError::Io(err)) if err.kind() == std::io::ErrorKind::WouldBlock => { // the message was accepted and queued so not an error // but `poll_ready` will now start trying to flush the block self.ready = false; Ok(()) } Err(e) => { self.ready = true; debug!("websocket start_send error: {}", e); Err(e) } } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { (*self).with_context(Some((ContextWaker::Write, cx)), |s| cvt(s.flush())).map(|r| { self.ready = true; match r { // WebSocket connection has just been closed. Flushing completed, not an error. Err(WsError::ConnectionClosed) => Ok(()), other => other, } }) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.ready = true; let res = if self.closing { // After queueing it, we call `flush` to drive the close handshake to completion. (*self).with_context(Some((ContextWaker::Write, cx)), |s| s.flush()) } else { (*self).with_context(Some((ContextWaker::Write, cx)), |s| s.close(None)) }; match res { Ok(()) => Poll::Ready(Ok(())), Err(WsError::ConnectionClosed) => Poll::Ready(Ok(())), Err(WsError::Io(err)) if err.kind() == std::io::ErrorKind::WouldBlock => { trace!("WouldBlock"); self.closing = true; Poll::Pending } Err(err) => { debug!("websocket close error: {}", err); Poll::Ready(Err(err)) } } } } /// Get a domain from an URL. #[cfg(any(feature = "connect", feature = "native-tls", feature = "__rustls-tls"))] #[inline] fn domain(request: &tungstenite::handshake::client::Request) -> Result { match request.uri().host() { // rustls expects IPv6 addresses without the surrounding [] brackets #[cfg(feature = "__rustls-tls")] Some(d) if d.starts_with('[') && d.ends_with(']') => Ok(d[1..d.len() - 1].to_string()), Some(d) => Ok(d.to_string()), None => Err(WsError::Url(tungstenite::error::UrlError::NoHostName)), } } #[cfg(test)] mod tests { #[cfg(feature = "connect")] use crate::stream::MaybeTlsStream; use crate::{compat::AllowStd, WebSocketStream}; use std::io::{Read, Write}; #[cfg(feature = "connect")] use tokio::io::{AsyncReadExt, AsyncWriteExt}; fn is_read() {} fn is_write() {} #[cfg(feature = "connect")] fn is_async_read() {} #[cfg(feature = "connect")] fn is_async_write() {} fn is_unpin() {} #[test] fn web_socket_stream_has_traits() { is_read::>(); is_write::>(); #[cfg(feature = "connect")] is_async_read::>(); #[cfg(feature = "connect")] is_async_write::>(); is_unpin::>(); #[cfg(feature = "connect")] is_unpin::>>(); } } tokio-tungstenite-0.20.1/src/stream.rs000064400000000000000000000055711046102023000160400ustar 00000000000000//! Convenience wrapper for streams to switch between plain TCP and TLS at runtime. //! //! There is no dependency on actual TLS implementations. Everything like //! `native_tls` or `openssl` will work as long as there is a TLS stream supporting standard //! `Read + Write` traits. use std::{ pin::Pin, task::{Context, Poll}, }; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; /// A stream that might be protected with TLS. #[non_exhaustive] #[derive(Debug)] pub enum MaybeTlsStream { /// Unencrypted socket stream. Plain(S), /// Encrypted socket stream using `native-tls`. #[cfg(feature = "native-tls")] NativeTls(tokio_native_tls::TlsStream), /// Encrypted socket stream using `rustls`. #[cfg(feature = "__rustls-tls")] Rustls(tokio_rustls::client::TlsStream), } impl AsyncRead for MaybeTlsStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { match self.get_mut() { MaybeTlsStream::Plain(ref mut s) => Pin::new(s).poll_read(cx, buf), #[cfg(feature = "native-tls")] MaybeTlsStream::NativeTls(s) => Pin::new(s).poll_read(cx, buf), #[cfg(feature = "__rustls-tls")] MaybeTlsStream::Rustls(s) => Pin::new(s).poll_read(cx, buf), } } } impl AsyncWrite for MaybeTlsStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { match self.get_mut() { MaybeTlsStream::Plain(ref mut s) => Pin::new(s).poll_write(cx, buf), #[cfg(feature = "native-tls")] MaybeTlsStream::NativeTls(s) => Pin::new(s).poll_write(cx, buf), #[cfg(feature = "__rustls-tls")] MaybeTlsStream::Rustls(s) => Pin::new(s).poll_write(cx, buf), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.get_mut() { MaybeTlsStream::Plain(ref mut s) => Pin::new(s).poll_flush(cx), #[cfg(feature = "native-tls")] MaybeTlsStream::NativeTls(s) => Pin::new(s).poll_flush(cx), #[cfg(feature = "__rustls-tls")] MaybeTlsStream::Rustls(s) => Pin::new(s).poll_flush(cx), } } fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { match self.get_mut() { MaybeTlsStream::Plain(ref mut s) => Pin::new(s).poll_shutdown(cx), #[cfg(feature = "native-tls")] MaybeTlsStream::NativeTls(s) => Pin::new(s).poll_shutdown(cx), #[cfg(feature = "__rustls-tls")] MaybeTlsStream::Rustls(s) => Pin::new(s).poll_shutdown(cx), } } } tokio-tungstenite-0.20.1/src/tls.rs000064400000000000000000000207741046102023000153510ustar 00000000000000//! Connection helper. use tokio::io::{AsyncRead, AsyncWrite}; use tungstenite::{ client::uri_mode, error::Error, handshake::client::Response, protocol::WebSocketConfig, }; use crate::{client_async_with_config, IntoClientRequest, WebSocketStream}; pub use crate::stream::MaybeTlsStream; /// A connector that can be used when establishing connections, allowing to control whether /// `native-tls` or `rustls` is used to create a TLS connection. Or TLS can be disabled with the /// `Plain` variant. #[non_exhaustive] #[derive(Clone)] pub enum Connector { /// Plain (non-TLS) connector. Plain, /// `native-tls` TLS connector. #[cfg(feature = "native-tls")] NativeTls(native_tls_crate::TlsConnector), /// `rustls` TLS connector. #[cfg(feature = "__rustls-tls")] Rustls(std::sync::Arc), } mod encryption { #[cfg(feature = "native-tls")] pub mod native_tls { use native_tls_crate::TlsConnector; use tokio_native_tls::TlsConnector as TokioTlsConnector; use tokio::io::{AsyncRead, AsyncWrite}; use tungstenite::{error::TlsError, stream::Mode, Error}; use crate::stream::MaybeTlsStream; pub async fn wrap_stream( socket: S, domain: String, mode: Mode, tls_connector: Option, ) -> Result, Error> where S: 'static + AsyncRead + AsyncWrite + Send + Unpin, { match mode { Mode::Plain => Ok(MaybeTlsStream::Plain(socket)), Mode::Tls => { let try_connector = tls_connector.map_or_else(TlsConnector::new, Ok); let connector = try_connector.map_err(TlsError::Native)?; let stream = TokioTlsConnector::from(connector); let connected = stream.connect(&domain, socket).await; match connected { Err(e) => Err(Error::Tls(e.into())), Ok(s) => Ok(MaybeTlsStream::NativeTls(s)), } } } } } #[cfg(feature = "__rustls-tls")] pub mod rustls { pub use rustls::ClientConfig; use rustls::{RootCertStore, ServerName}; use tokio_rustls::TlsConnector as TokioTlsConnector; use std::{convert::TryFrom, sync::Arc}; use tokio::io::{AsyncRead, AsyncWrite}; use tungstenite::{error::TlsError, stream::Mode, Error}; use crate::stream::MaybeTlsStream; pub async fn wrap_stream( socket: S, domain: String, mode: Mode, tls_connector: Option>, ) -> Result, Error> where S: 'static + AsyncRead + AsyncWrite + Send + Unpin, { match mode { Mode::Plain => Ok(MaybeTlsStream::Plain(socket)), Mode::Tls => { let config = match tls_connector { Some(config) => config, None => { #[allow(unused_mut)] let mut root_store = RootCertStore::empty(); #[cfg(feature = "rustls-tls-native-roots")] { let native_certs = rustls_native_certs::load_native_certs()?; let der_certs: Vec> = native_certs.into_iter().map(|cert| cert.0).collect(); let total_number = der_certs.len(); let (number_added, number_ignored) = root_store.add_parsable_certificates(&der_certs); log::debug!("Added {number_added}/{total_number} native root certificates (ignored {number_ignored})"); } #[cfg(feature = "rustls-tls-webpki-roots")] { root_store.add_trust_anchors( webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) }) ); } Arc::new( ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(), ) } }; let domain = ServerName::try_from(domain.as_str()) .map_err(|_| TlsError::InvalidDnsName)?; let stream = TokioTlsConnector::from(config); let connected = stream.connect(domain, socket).await; match connected { Err(e) => Err(Error::Io(e)), Ok(s) => Ok(MaybeTlsStream::Rustls(s)), } } } } } pub mod plain { use tokio::io::{AsyncRead, AsyncWrite}; use tungstenite::{ error::{Error, UrlError}, stream::Mode, }; use crate::stream::MaybeTlsStream; pub async fn wrap_stream(socket: S, mode: Mode) -> Result, Error> where S: 'static + AsyncRead + AsyncWrite + Send + Unpin, { match mode { Mode::Plain => Ok(MaybeTlsStream::Plain(socket)), Mode::Tls => Err(Error::Url(UrlError::TlsFeatureNotEnabled)), } } } } /// Creates a WebSocket handshake from a request and a stream, /// upgrading the stream to TLS if required. #[cfg(any(feature = "native-tls", feature = "__rustls-tls"))] pub async fn client_async_tls( request: R, stream: S, ) -> Result<(WebSocketStream>, Response), Error> where R: IntoClientRequest + Unpin, S: 'static + AsyncRead + AsyncWrite + Send + Unpin, MaybeTlsStream: Unpin, { client_async_tls_with_config(request, stream, None, None).await } /// The same as `client_async_tls()` but the one can specify a websocket configuration, /// and an optional connector. If no connector is specified, a default one will /// be created. /// /// Please refer to `client_async_tls()` for more details. pub async fn client_async_tls_with_config( request: R, stream: S, config: Option, connector: Option, ) -> Result<(WebSocketStream>, Response), Error> where R: IntoClientRequest + Unpin, S: 'static + AsyncRead + AsyncWrite + Send + Unpin, MaybeTlsStream: Unpin, { let request = request.into_client_request()?; #[cfg(any(feature = "native-tls", feature = "__rustls-tls"))] let domain = crate::domain(&request)?; // Make sure we check domain and mode first. URL must be valid. let mode = uri_mode(request.uri())?; let stream = match connector { Some(conn) => match conn { #[cfg(feature = "native-tls")] Connector::NativeTls(conn) => { self::encryption::native_tls::wrap_stream(stream, domain, mode, Some(conn)).await } #[cfg(feature = "__rustls-tls")] Connector::Rustls(conn) => { self::encryption::rustls::wrap_stream(stream, domain, mode, Some(conn)).await } Connector::Plain => self::encryption::plain::wrap_stream(stream, mode).await, }, None => { #[cfg(feature = "native-tls")] { self::encryption::native_tls::wrap_stream(stream, domain, mode, None).await } #[cfg(all(feature = "__rustls-tls", not(feature = "native-tls")))] { self::encryption::rustls::wrap_stream(stream, domain, mode, None).await } #[cfg(not(any(feature = "native-tls", feature = "__rustls-tls")))] { self::encryption::plain::wrap_stream(stream, mode).await } } }?; client_async_with_config(request, stream, config).await }