aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp2
-rw-r--r--CHANGELOG.md27
-rw-r--r--Cargo.lock836
-rw-r--r--Cargo.toml60
-rw-r--r--Cargo.toml.orig55
-rw-r--r--METADATA26
-rw-r--r--README.md8
-rw-r--r--benches/buffer.rs3
-rw-r--r--benches/write.rs75
-rw-r--r--examples/autobahn-client.rs53
-rw-r--r--examples/autobahn-server.rs47
-rw-r--r--examples/callback-error.rs23
-rw-r--r--examples/client.rs23
-rw-r--r--examples/server.rs38
-rw-r--r--examples/srv_accept_unmasked_frames.rs48
-rw-r--r--src/client.rs1
-rw-r--r--src/error.rs13
-rw-r--r--src/handshake/client.rs6
-rw-r--r--src/handshake/machine.rs85
-rw-r--r--src/lib.rs4
-rw-r--r--src/protocol/frame/frame.rs21
-rw-r--r--src/protocol/frame/mask.rs2
-rw-r--r--src/protocol/frame/mod.rs122
-rw-r--r--src/protocol/message.rs2
-rw-r--r--src/protocol/mod.rs464
-rw-r--r--src/tls.rs29
27 files changed, 1247 insertions, 832 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..c0c5983
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "85463b264e3f672ef2004294d82fd3f4ee6a8ca3"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 1b12e5c..2e4489b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6,7 +6,7 @@ rust_library {
host_supported: true,
crate_name: "tungstenite",
cargo_env_compat: true,
- cargo_pkg_version: "0.19.0",
+ cargo_pkg_version: "0.21.0",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0aa0a41..5c0348e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,30 @@
+# Unreleased
+- Fix read-predominant auto pong responses not flushing when hitting WouldBlock errors.
+- Improve `FrameHeader::format` write correctness.
+- Up minimum _rustls_ to `0.21.6`.
+- Update _webpki-roots_ to `0.26`.
+
+# 0.20.1
+- Fixes [CVE-2023-43669](https://github.com/snapview/tungstenite-rs/pull/379).
+
+# 0.20.0
+- Remove many implicit flushing behaviours. In general reading and writing messages will no
+ longer flush until calling `flush`. An exception is automatic responses (e.g. pongs)
+ which will continue to be written and flushed when reading and writing.
+ This allows writing a batch of messages and flushing once, improving performance.
+- Add `WebSocket::read`, `write`, `send`, `flush`. Deprecate `read_message`, `write_message`, `write_pending`.
+- Add `FrameSocket::read`, `write`, `send`, `flush`. Remove `read_frame`, `write_frame`, `write_pending`.
+ Note: Previous use of `write_frame` may be replaced with `send`.
+- Add `WebSocketContext::read`, `write`, `flush`. Remove `read_message`, `write_message`, `write_pending`.
+ Note: Previous use of `write_message` may be replaced with `write` + `flush`.
+- Remove `send_queue`, replaced with using the frame write buffer to achieve similar results.
+ * Add `WebSocketConfig::max_write_buffer_size`. Deprecate `max_send_queue`.
+ * Add `Error::WriteBufferFull`. Remove `Error::SendQueueFull`.
+ Note: `WriteBufferFull` returns the message that could not be written as a `Message::Frame`.
+- Add ability to buffer multiple writes before writing to the underlying stream, controlled by
+ `WebSocketConfig::write_buffer_size` (default 128 KiB). Improves batch message write performance.
+- Panic on receiving invalid `WebSocketConfig`.
+
# 0.19.0
- Update TLS dependencies.
diff --git a/Cargo.lock b/Cargo.lock
index f4f2159..7ec88dc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aho-corasick"
-version = "0.7.18"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
@@ -18,15 +18,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
-name = "atty"
-version = "0.2.14"
+name = "anstyle"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "autocfg"
@@ -36,9 +31,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
-version = "0.13.0"
+version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "bitflags"
@@ -47,31 +42,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
name = "block-buffer"
-version = "0.10.2"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
-version = "3.10.0"
+version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "byteorder"
-version = "1.4.3"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
-version = "1.1.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cast"
@@ -81,15 +82,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.0.73"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
[[package]]
name = "cfg-if"
@@ -99,9 +97,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
+checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
@@ -110,15 +108,15 @@ dependencies = [
[[package]]
name = "ciborium-io"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
+checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
+checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
@@ -126,30 +124,34 @@ dependencies = [
[[package]]
name = "clap"
-version = "3.2.21"
+version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7"
+checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
dependencies = [
- "bitflags",
- "clap_lex",
- "indexmap",
- "textwrap",
+ "clap_builder",
]
[[package]]
-name = "clap_lex"
-version = "0.2.4"
+name = "clap_builder"
+version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
dependencies = [
- "os_str_bytes",
+ "anstyle",
+ "clap_lex",
]
[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
name = "core-foundation"
-version = "0.9.3"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
@@ -157,34 +159,34 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.3"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
-version = "0.2.2"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
dependencies = [
"libc",
]
[[package]]
name = "criterion"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
- "atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
+ "is-terminal",
"itertools",
- "lazy_static",
"num-traits",
+ "once_cell",
"oorandom",
"plotters",
"rayon",
@@ -207,55 +209,43 @@ dependencies = [
]
[[package]]
-name = "crossbeam-channel"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
-dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-utils",
-]
-
-[[package]]
name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.9"
+version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-utils",
"memoffset",
- "once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.10"
+version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
- "cfg-if 1.0.0",
- "once_cell",
+ "cfg-if",
]
[[package]]
name = "crypto-common"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
@@ -263,15 +253,15 @@ dependencies = [
[[package]]
name = "data-encoding"
-version = "2.3.3"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "digest"
-version = "0.10.3"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
@@ -279,15 +269,15 @@ dependencies = [
[[package]]
name = "either"
-version = "1.7.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "env_logger"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
dependencies = [
"humantime",
"is-terminal",
@@ -298,33 +288,19 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "windows-sys 0.45.0",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
- "cc",
"libc",
+ "windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
-version = "1.7.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
-dependencies = [
- "instant",
-]
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fnv"
@@ -349,19 +325,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.0.1"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
- "matches",
"percent-encoding",
]
[[package]]
name = "generic-array"
-version = "0.14.5"
+version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
@@ -369,11 +344,11 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.7"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"wasi",
]
@@ -385,31 +360,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.3"
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"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "http"
-version = "0.2.8"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
@@ -418,9 +378,9 @@ dependencies = [
[[package]]
name = "httparse"
-version = "1.7.1"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "humantime"
@@ -430,26 +390,15 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "idna"
-version = "0.2.3"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
- "matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
-name = "indexmap"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
name = "input_buffer"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -459,57 +408,36 @@ dependencies = [
]
[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[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"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
- "hermit-abi 0.3.1",
- "io-lifetimes",
+ "hermit-abi",
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "itertools"
-version = "0.10.3"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
-version = "1.0.2"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "js-sys"
-version = "0.3.58"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -522,51 +450,42 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.141"
+version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
-version = "0.3.1"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
+checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "log"
-version = "0.4.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "matches"
-version = "0.1.9"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
-version = "0.6.5"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "native-tls"
-version = "0.2.10"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
+checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
@@ -581,40 +500,19 @@ dependencies = [
]
[[package]]
-name = "net2"
-version = "0.2.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
-dependencies = [
- "cfg-if 0.1.10",
- "libc",
- "winapi",
-]
-
-[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
-name = "num_cpus"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
-]
-
-[[package]]
name = "once_cell"
-version = "1.13.0"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
@@ -624,12 +522,12 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "openssl"
-version = "0.10.41"
+version = "0.10.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
+checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45"
dependencies = [
- "bitflags",
- "cfg-if 1.0.0",
+ "bitflags 2.4.1",
+ "cfg-if",
"foreign-types",
"libc",
"once_cell",
@@ -639,9 +537,9 @@ dependencies = [
[[package]]
name = "openssl-macros"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
@@ -656,20 +554,19 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
-version = "111.22.0+1.1.1q"
+version = "300.1.6+3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853"
+checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
-version = "0.9.75"
+version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
+checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b"
dependencies = [
- "autocfg",
"cc",
"libc",
"openssl-src",
@@ -678,28 +575,22 @@ dependencies = [
]
[[package]]
-name = "os_str_bytes"
-version = "6.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
-
-[[package]]
name = "percent-encoding"
-version = "2.1.0"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pkg-config"
-version = "0.3.25"
+version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "plotters"
-version = "0.3.2"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
@@ -710,39 +601,39 @@ dependencies = [
[[package]]
name = "plotters-backend"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
-version = "0.3.2"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
-version = "1.0.40"
+version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
+checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.20"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -770,149 +661,157 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
-version = "1.5.3"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
- "autocfg",
- "crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
-version = "1.9.3"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
- "crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "num_cpus",
]
[[package]]
name = "redox_syscall"
-version = "0.2.13"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
name = "regex"
-version = "1.6.0"
+version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
+ "regex-automata",
"regex-syntax",
]
[[package]]
-name = "regex-syntax"
-version = "0.6.27"
+name = "regex-automata"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-syntax"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ring"
-version = "0.16.20"
+version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
+ "getrandom",
"libc",
- "once_cell",
"spin",
"untrusted",
- "web-sys",
- "winapi",
+ "windows-sys 0.48.0",
]
[[package]]
name = "rustix"
-version = "0.37.9"
+version = "0.38.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3eb76a3b09109e78c52d45979fea3cd8ddaadb223531d0846bedb60e72c3e99"
+checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
dependencies = [
- "bitflags",
+ "bitflags 2.4.1",
"errno",
- "io-lifetimes",
"libc",
"linux-raw-sys",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "rustls"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d"
+checksum = "5bc238b76c51bbc449c55ffbc39d03772a057cc8cf783c49d4af4c2537b74a8b"
dependencies = [
"log",
"ring",
+ "rustls-pki-types",
"rustls-webpki",
- "sct",
+ "subtle",
+ "zeroize",
]
[[package]]
name = "rustls-native-certs"
-version = "0.6.2"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
+checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
dependencies = [
"openssl-probe",
"rustls-pemfile",
+ "rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pemfile"
-version = "1.0.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
+checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4"
dependencies = [
"base64",
+ "rustls-pki-types",
]
[[package]]
+name = "rustls-pki-types"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b"
+
+[[package]]
name = "rustls-webpki"
-version = "0.100.1"
+version = "0.102.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
+checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89"
dependencies = [
"ring",
+ "rustls-pki-types",
"untrusted",
]
[[package]]
name = "ryu"
-version = "1.0.10"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
@@ -925,37 +824,26 @@ dependencies = [
[[package]]
name = "schannel"
-version = "0.1.20"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
dependencies = [
- "lazy_static",
- "windows-sys 0.36.1",
+ "windows-sys 0.48.0",
]
[[package]]
name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "sct"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
-dependencies = [
- "ring",
- "untrusted",
-]
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
-version = "2.6.1"
+version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -964,9 +852,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.6.1"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
dependencies = [
"core-foundation-sys",
"libc",
@@ -974,18 +862,18 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.139"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.139"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@@ -994,9 +882,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.82"
+version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@@ -1005,26 +893,42 @@ dependencies = [
[[package]]
name = "sha1"
-version = "0.10.4"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"cpufeatures",
"digest",
]
[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "spin"
-version = "0.5.2"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
-version = "1.0.98"
+version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
@@ -1033,47 +937,40 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.3.0"
+version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"fastrand",
- "libc",
"redox_syscall",
- "remove_dir_all",
- "winapi",
+ "rustix",
+ "windows-sys 0.48.0",
]
[[package]]
name = "termcolor"
-version = "1.1.3"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
[[package]]
-name = "textwrap"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
-
-[[package]]
name = "thiserror"
-version = "1.0.31"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.31"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
@@ -1101,13 +998,13 @@ dependencies = [
[[package]]
name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tungstenite"
-version = "0.19.0"
+version = "0.21.0"
dependencies = [
"byteorder",
"bytes",
@@ -1119,60 +1016,59 @@ dependencies = [
"input_buffer",
"log",
"native-tls",
- "net2",
"rand",
"rustls",
"rustls-native-certs",
+ "rustls-pki-types",
"sha1",
+ "socket2",
"thiserror",
"url",
"utf-8",
- "webpki",
"webpki-roots",
]
[[package]]
name = "typenum"
-version = "1.15.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
-version = "1.0.1"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
-version = "0.1.21"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "untrusted"
-version = "0.7.1"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
-version = "2.2.2"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
- "matches",
"percent-encoding",
]
@@ -1196,12 +1092,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
-version = "2.3.2"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
- "winapi",
"winapi-util",
]
@@ -1213,23 +1108,23 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.81"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.81"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
- "lazy_static",
"log",
+ "once_cell",
"proc-macro2",
"quote",
"syn",
@@ -1238,9 +1133,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.81"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1248,9 +1143,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.81"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
@@ -1261,37 +1156,27 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.81"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "web-sys"
-version = "0.3.58"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
+checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
-name = "webpki"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
name = "webpki-roots"
-version = "0.23.0"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125"
+checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188"
dependencies = [
- "rustls-webpki",
+ "rustls-pki-types",
]
[[package]]
@@ -1312,9 +1197,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
@@ -1327,175 +1212,138 @@ 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.45.0"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.42.2",
+ "windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
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",
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.36.1"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
-version = "0.36.1"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
-version = "0.36.1"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.2"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.36.1"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.2"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.0"
+name = "zeroize"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/Cargo.toml b/Cargo.toml
index 896a836..3d8cca5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2018"
rust-version = "1.51"
name = "tungstenite"
-version = "0.19.0"
+version = "0.21.0"
authors = [
"Alexey Galakhov",
"Daniel Abramov",
@@ -21,13 +21,14 @@ authors = [
include = [
"benches/**/*",
"src/**/*",
+ "examples/**/*",
"LICENSE-*",
"README.md",
"CHANGELOG.md",
]
description = "Lightweight stream-based WebSocket implementation"
homepage = "https://github.com/snapview/tungstenite-rs"
-documentation = "https://docs.rs/tungstenite/0.19.0"
+documentation = "https://docs.rs/tungstenite/0.21.0"
readme = "README.md"
keywords = [
"websocket",
@@ -44,10 +45,38 @@ repository = "https://github.com/snapview/tungstenite-rs"
[package.metadata.docs.rs]
all-features = true
+[[example]]
+name = "client"
+required-features = ["handshake"]
+
+[[example]]
+name = "server"
+required-features = ["handshake"]
+
+[[example]]
+name = "autobahn-client"
+required-features = ["handshake"]
+
+[[example]]
+name = "autobahn-server"
+required-features = ["handshake"]
+
+[[example]]
+name = "callback-error"
+required-features = ["handshake"]
+
+[[example]]
+name = "srv_accept_unmasked_frames"
+required-features = ["handshake"]
+
[[bench]]
name = "buffer"
harness = false
+[[bench]]
+name = "write"
+harness = false
+
[dependencies.byteorder]
version = "1.3.2"
@@ -59,7 +88,7 @@ version = "2"
optional = true
[dependencies.http]
-version = "0.2"
+version = "1.0"
optional = true
[dependencies.httparse]
@@ -78,11 +107,15 @@ package = "native-tls"
version = "0.8.0"
[dependencies.rustls]
-version = "0.21.0"
+version = "0.22.0"
optional = true
[dependencies.rustls-native-certs]
-version = "0.6.0"
+version = "0.7.0"
+optional = true
+
+[dependencies.rustls-pki-types]
+version = "1.0"
optional = true
[dependencies.sha1]
@@ -99,17 +132,12 @@ optional = true
[dependencies.utf-8]
version = "0.7.5"
-[dependencies.webpki]
-version = "0.22"
-features = ["std"]
-optional = true
-
[dependencies.webpki-roots]
-version = "0.23"
+version = "0.26"
optional = true
[dev-dependencies.criterion]
-version = "0.4.0"
+version = "0.5.0"
[dev-dependencies.env_logger]
version = "0.10.0"
@@ -117,16 +145,16 @@ version = "0.10.0"
[dev-dependencies.input_buffer]
version = "0.5.0"
-[dev-dependencies.net2]
-version = "0.2.37"
-
[dev-dependencies.rand]
version = "0.8.4"
+[dev-dependencies.socket2]
+version = "0.5.5"
+
[features]
__rustls-tls = [
"rustls",
- "webpki",
+ "rustls-pki-types",
]
default = ["handshake"]
handshake = [
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 6640559..2e67f01 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -7,12 +7,12 @@ authors = ["Alexey Galakhov", "Daniel Abramov"]
license = "MIT OR Apache-2.0"
readme = "README.md"
homepage = "https://github.com/snapview/tungstenite-rs"
-documentation = "https://docs.rs/tungstenite/0.19.0"
+documentation = "https://docs.rs/tungstenite/0.21.0"
repository = "https://github.com/snapview/tungstenite-rs"
-version = "0.19.0"
+version = "0.21.0"
edition = "2018"
rust-version = "1.51"
-include = ["benches/**/*", "src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"]
+include = ["benches/**/*", "src/**/*", "examples/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"]
[package.metadata.docs.rs]
all-features = true
@@ -24,13 +24,13 @@ native-tls = ["native-tls-crate"]
native-tls-vendored = ["native-tls", "native-tls-crate/vendored"]
rustls-tls-native-roots = ["__rustls-tls", "rustls-native-certs"]
rustls-tls-webpki-roots = ["__rustls-tls", "webpki-roots"]
-__rustls-tls = ["rustls", "webpki"]
+__rustls-tls = ["rustls", "rustls-pki-types"]
[dependencies]
data-encoding = { version = "2", optional = true }
byteorder = "1.3.2"
bytes = "1.0"
-http = { version = "0.2", optional = true }
+http = { version = "1.0", optional = true }
httparse = { version = "1.3.4", optional = true }
log = "0.4.8"
rand = "0.8.0"
@@ -46,28 +46,55 @@ version = "0.2.3"
[dependencies.rustls]
optional = true
-version = "0.21.0"
+version = "0.22.0"
-[dependencies.rustls-native-certs]
+[dependencies.rustls-pki-types]
optional = true
-version = "0.6.0"
+version = "1.0"
-[dependencies.webpki]
+[dependencies.rustls-native-certs]
optional = true
-version = "0.22"
-features = ["std"]
+version = "0.7.0"
[dependencies.webpki-roots]
optional = true
-version = "0.23"
+version = "0.26"
[dev-dependencies]
-criterion = "0.4.0"
+criterion = "0.5.0"
env_logger = "0.10.0"
input_buffer = "0.5.0"
-net2 = "0.2.37"
rand = "0.8.4"
+socket2 = "0.5.5"
[[bench]]
name = "buffer"
harness = false
+
+[[bench]]
+name = "write"
+harness = false
+
+[[example]]
+name = "client"
+required-features = ["handshake"]
+
+[[example]]
+name = "server"
+required-features = ["handshake"]
+
+[[example]]
+name = "autobahn-client"
+required-features = ["handshake"]
+
+[[example]]
+name = "autobahn-server"
+required-features = ["handshake"]
+
+[[example]]
+name = "callback-error"
+required-features = ["handshake"]
+
+[[example]]
+name = "srv_accept_unmasked_frames"
+required-features = ["handshake"]
diff --git a/METADATA b/METADATA
index 5d7d855..8220772 100644
--- a/METADATA
+++ b/METADATA
@@ -1,20 +1,20 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/rust/crates/tungstenite
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
name: "tungstenite"
description: "Lightweight stream-based WebSocket implementation"
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/tungstenite"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/tungstenite/tungstenite-0.19.0.crate"
- }
- version: "0.19.0"
- # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
license_type: NOTICE
last_upgrade_date {
- year: 2023
- month: 6
- day: 2
+ year: 2024
+ month: 2
+ day: 8
+ }
+ homepage: "https://crates.io/crates/tungstenite"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/tungstenite/tungstenite-0.21.0.crate"
+ version: "0.21.0"
}
}
diff --git a/README.md b/README.md
index 7e662e4..8c1b2a2 100644
--- a/README.md
+++ b/README.md
@@ -14,11 +14,11 @@ fn main () {
spawn (move || {
let mut websocket = accept(stream.unwrap()).unwrap();
loop {
- let msg = websocket.read_message().unwrap();
+ let msg = websocket.read().unwrap();
// We do not want to send back ping/pong messages.
if msg.is_binary() || msg.is_text() {
- websocket.write_message(msg).unwrap();
+ websocket.send(msg).unwrap();
}
}
});
@@ -36,7 +36,7 @@ take a look at [`tokio-tungstenite`](https://github.com/snapview/tokio-tungsteni
[![MIT licensed](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE-MIT)
[![Apache-2.0 licensed](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE)
[![Crates.io](https://img.shields.io/crates/v/tungstenite.svg?maxAge=2592000)](https://crates.io/crates/tungstenite)
-[![Build Status](https://travis-ci.org/snapview/tungstenite-rs.svg?branch=master)](https://travis-ci.org/snapview/tungstenite-rs)
+[![Build Status](https://github.com/snapview/tungstenite-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/snapview/tungstenite-rs/actions)
[Documentation](https://docs.rs/tungstenite)
@@ -77,7 +77,7 @@ There is no support for permessage-deflate at the moment, but the PRs are welcom
Testing
-------
-Tungstenite is thoroughly tested and passes the [Autobahn Test Suite](https://crossbar.io/autobahn/) for
+Tungstenite is thoroughly tested and passes the [Autobahn Test Suite](https://github.com/crossbario/autobahn-testsuite) for
WebSockets. It is also covered by internal unit tests as well as possible.
Contributing
diff --git a/benches/buffer.rs b/benches/buffer.rs
index 4f50649..9f15d13 100644
--- a/benches/buffer.rs
+++ b/benches/buffer.rs
@@ -1,5 +1,4 @@
-use std::io::Result as IoResult;
-use std::io::{Cursor, Read};
+use std::io::{Cursor, Read, Result as IoResult};
use bytes::Buf;
use criterion::*;
diff --git a/benches/write.rs b/benches/write.rs
new file mode 100644
index 0000000..7908818
--- /dev/null
+++ b/benches/write.rs
@@ -0,0 +1,75 @@
+//! Benchmarks for write performance.
+use criterion::{BatchSize, Criterion};
+use std::{
+ hint,
+ io::{self, Read, Write},
+ time::{Duration, Instant},
+};
+use tungstenite::{Message, WebSocket};
+
+const MOCK_WRITE_LEN: usize = 8 * 1024 * 1024;
+
+/// `Write` impl that simulates slowish writes and slow flushes.
+///
+/// Each `write` can buffer up to 8 MiB before flushing but takes an additional **~80ns**
+/// to simulate stuff going on in the underlying stream.
+/// Each `flush` takes **~8µs** to simulate flush io.
+struct MockWrite(Vec<u8>);
+
+impl Read for MockWrite {
+ fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
+ Err(io::Error::new(io::ErrorKind::WouldBlock, "reads not supported"))
+ }
+}
+impl Write for MockWrite {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ if self.0.len() + buf.len() > MOCK_WRITE_LEN {
+ self.flush()?;
+ }
+ // simulate io
+ spin(Duration::from_nanos(80));
+ self.0.extend(buf);
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ if !self.0.is_empty() {
+ // simulate io
+ spin(Duration::from_micros(8));
+ self.0.clear();
+ }
+ Ok(())
+ }
+}
+
+fn spin(duration: Duration) {
+ let a = Instant::now();
+ while a.elapsed() < duration {
+ hint::spin_loop();
+ }
+}
+
+fn benchmark(c: &mut Criterion) {
+ // Writes 100k small json text messages then flushes
+ c.bench_function("write 100k small texts then flush", |b| {
+ let mut ws = WebSocket::from_raw_socket(
+ MockWrite(Vec::with_capacity(MOCK_WRITE_LEN)),
+ tungstenite::protocol::Role::Server,
+ None,
+ );
+
+ b.iter_batched(
+ || (0..100_000).map(|i| Message::Text(format!("{{\"id\":{i}}}"))),
+ |batch| {
+ for msg in batch {
+ ws.write(msg).unwrap();
+ }
+ ws.flush().unwrap();
+ },
+ BatchSize::SmallInput,
+ )
+ });
+}
+
+criterion::criterion_group!(write_benches, benchmark);
+criterion::criterion_main!(write_benches);
diff --git a/examples/autobahn-client.rs b/examples/autobahn-client.rs
new file mode 100644
index 0000000..ac7a7d1
--- /dev/null
+++ b/examples/autobahn-client.rs
@@ -0,0 +1,53 @@
+use log::*;
+use url::Url;
+
+use tungstenite::{connect, Error, Message, Result};
+
+const AGENT: &str = "Tungstenite";
+
+fn get_case_count() -> Result<u32> {
+ let (mut socket, _) = connect(Url::parse("ws://localhost:9001/getCaseCount").unwrap())?;
+ let msg = socket.read()?;
+ socket.close(None)?;
+ Ok(msg.into_text()?.parse::<u32>().unwrap())
+}
+
+fn update_reports() -> Result<()> {
+ let (mut socket, _) = connect(
+ Url::parse(&format!("ws://localhost:9001/updateReports?agent={}", AGENT)).unwrap(),
+ )?;
+ socket.close(None)?;
+ Ok(())
+}
+
+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)).unwrap();
+ let (mut socket, _) = connect(case_url)?;
+ loop {
+ match socket.read()? {
+ msg @ Message::Text(_) | msg @ Message::Binary(_) => {
+ socket.send(msg)?;
+ }
+ Message::Ping(_) | Message::Pong(_) | Message::Close(_) | Message::Frame(_) => {}
+ }
+ }
+}
+
+fn main() {
+ env_logger::init();
+
+ let total = get_case_count().unwrap();
+
+ for case in 1..=total {
+ if let Err(e) = run_test(case) {
+ match e {
+ Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (),
+ err => error!("test: {}", err),
+ }
+ }
+ }
+
+ update_reports().unwrap();
+}
diff --git a/examples/autobahn-server.rs b/examples/autobahn-server.rs
new file mode 100644
index 0000000..dafe37b
--- /dev/null
+++ b/examples/autobahn-server.rs
@@ -0,0 +1,47 @@
+use std::{
+ net::{TcpListener, TcpStream},
+ thread::spawn,
+};
+
+use log::*;
+use tungstenite::{accept, handshake::HandshakeRole, Error, HandshakeError, Message, Result};
+
+fn must_not_block<Role: HandshakeRole>(err: HandshakeError<Role>) -> Error {
+ match err {
+ HandshakeError::Interrupted(_) => panic!("Bug: blocking socket would block"),
+ HandshakeError::Failure(f) => f,
+ }
+}
+
+fn handle_client(stream: TcpStream) -> Result<()> {
+ let mut socket = accept(stream).map_err(must_not_block)?;
+ info!("Running test");
+ loop {
+ match socket.read()? {
+ msg @ Message::Text(_) | msg @ Message::Binary(_) => {
+ socket.send(msg)?;
+ }
+ Message::Ping(_) | Message::Pong(_) | Message::Close(_) | Message::Frame(_) => {}
+ }
+ }
+}
+
+fn main() {
+ env_logger::init();
+
+ let server = TcpListener::bind("127.0.0.1:9002").unwrap();
+
+ for stream in server.incoming() {
+ spawn(move || match stream {
+ Ok(stream) => {
+ if let Err(err) = handle_client(stream) {
+ match err {
+ Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (),
+ e => error!("test: {}", e),
+ }
+ }
+ }
+ Err(e) => error!("Error accepting stream: {}", e),
+ });
+ }
+}
diff --git a/examples/callback-error.rs b/examples/callback-error.rs
new file mode 100644
index 0000000..cf78a2e
--- /dev/null
+++ b/examples/callback-error.rs
@@ -0,0 +1,23 @@
+use std::{net::TcpListener, thread::spawn};
+
+use tungstenite::{
+ accept_hdr,
+ handshake::server::{Request, Response},
+ http::StatusCode,
+};
+
+fn main() {
+ let server = TcpListener::bind("127.0.0.1:3012").unwrap();
+ for stream in server.incoming() {
+ spawn(move || {
+ let callback = |_req: &Request, _resp| {
+ let resp = Response::builder()
+ .status(StatusCode::FORBIDDEN)
+ .body(Some("Access denied".into()))
+ .unwrap();
+ Err(resp)
+ };
+ accept_hdr(stream.unwrap(), callback).unwrap_err();
+ });
+ }
+}
diff --git a/examples/client.rs b/examples/client.rs
new file mode 100644
index 0000000..a24f316
--- /dev/null
+++ b/examples/client.rs
@@ -0,0 +1,23 @@
+use tungstenite::{connect, Message};
+use url::Url;
+
+fn main() {
+ env_logger::init();
+
+ let (mut socket, response) =
+ connect(Url::parse("ws://localhost:3012/socket").unwrap()).expect("Can't connect");
+
+ println!("Connected to the server");
+ println!("Response HTTP code: {}", response.status());
+ println!("Response contains the following headers:");
+ for (ref header, _value) in response.headers() {
+ println!("* {}", header);
+ }
+
+ socket.send(Message::Text("Hello WebSocket".into())).unwrap();
+ loop {
+ let msg = socket.read().expect("Error reading message");
+ println!("Received: {}", msg);
+ }
+ // socket.close(None);
+}
diff --git a/examples/server.rs b/examples/server.rs
new file mode 100644
index 0000000..2183b96
--- /dev/null
+++ b/examples/server.rs
@@ -0,0 +1,38 @@
+use std::{net::TcpListener, thread::spawn};
+
+use tungstenite::{
+ accept_hdr,
+ handshake::server::{Request, Response},
+};
+
+fn main() {
+ env_logger::init();
+ let server = TcpListener::bind("127.0.0.1:3012").unwrap();
+ for stream in server.incoming() {
+ spawn(move || {
+ let callback = |req: &Request, mut response: Response| {
+ println!("Received a new 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's add an additional header to our response to the client.
+ let headers = response.headers_mut();
+ headers.append("MyCustomHeader", ":)".parse().unwrap());
+ headers.append("SOME_TUNGSTENITE_HEADER", "header_value".parse().unwrap());
+
+ Ok(response)
+ };
+ let mut websocket = accept_hdr(stream.unwrap(), callback).unwrap();
+
+ loop {
+ let msg = websocket.read().unwrap();
+ if msg.is_binary() || msg.is_text() {
+ websocket.send(msg).unwrap();
+ }
+ }
+ });
+ }
+}
diff --git a/examples/srv_accept_unmasked_frames.rs b/examples/srv_accept_unmasked_frames.rs
new file mode 100644
index 0000000..b65e4f7
--- /dev/null
+++ b/examples/srv_accept_unmasked_frames.rs
@@ -0,0 +1,48 @@
+use std::{net::TcpListener, thread::spawn};
+use tungstenite::{
+ accept_hdr_with_config,
+ handshake::server::{Request, Response},
+ protocol::WebSocketConfig,
+};
+
+fn main() {
+ env_logger::init();
+ let server = TcpListener::bind("127.0.0.1:3012").unwrap();
+ for stream in server.incoming() {
+ spawn(move || {
+ let callback = |req: &Request, mut response: Response| {
+ println!("Received a new 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's add an additional header to our response to the client.
+ let headers = response.headers_mut();
+ headers.append("MyCustomHeader", ":)".parse().unwrap());
+ headers.append("SOME_TUNGSTENITE_HEADER", "header_value".parse().unwrap());
+
+ Ok(response)
+ };
+
+ let config = Some(WebSocketConfig {
+ // This setting allows to accept client frames which are not masked
+ // This is not in compliance with RFC 6455 but might be handy in some
+ // rare cases where it is necessary to integrate with existing/legacy
+ // clients which are sending unmasked frames
+ accept_unmasked_frames: true,
+ ..<_>::default()
+ });
+
+ let mut websocket = accept_hdr_with_config(stream.unwrap(), callback, config).unwrap();
+
+ loop {
+ let msg = websocket.read().unwrap();
+ if msg.is_binary() || msg.is_text() {
+ println!("received message {}", msg);
+ }
+ }
+ });
+ }
+}
diff --git a/src/client.rs b/src/client.rs
index 9301939..9b30037 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -54,6 +54,7 @@ pub fn connect_with_config<Req: IntoClientRequest>(
let uri = request.uri();
let mode = uri_mode(uri)?;
let host = request.uri().host().ok_or(Error::Url(UrlError::NoHostName))?;
+ let host = if host.starts_with('[') { &host[1..host.len() - 1] } else { host };
let port = uri.port_u16().unwrap_or(match mode {
Mode::Plain => 80,
Mode::Tls => 443,
diff --git a/src/error.rs b/src/error.rs
index c830024..faea80b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -53,12 +53,15 @@ pub enum Error {
/// Protocol violation.
#[error("WebSocket protocol error: {0}")]
Protocol(#[from] ProtocolError),
- /// Message send queue full.
- #[error("Send queue is full")]
- SendQueueFull(Message),
+ /// Message write buffer is full.
+ #[error("Write buffer is full")]
+ WriteBufferFull(Message),
/// UTF coding error.
#[error("UTF-8 encoding error")]
Utf8,
+ /// Attack attempt detected.
+ #[error("Attack attempt detected")]
+ AttackAttempt,
/// Invalid URL.
#[error("URL error: {0}")]
Url(#[from] UrlError),
@@ -271,10 +274,6 @@ pub enum TlsError {
#[cfg(feature = "__rustls-tls")]
#[error("rustls error: {0}")]
Rustls(#[from] rustls::Error),
- /// Webpki error.
- #[cfg(feature = "__rustls-tls")]
- #[error("webpki error: {0}")]
- Webpki(#[from] webpki::Error),
/// DNS name resolution error.
#[cfg(feature = "__rustls-tls")]
#[error("Invalid DNS name")]
diff --git a/src/handshake/client.rs b/src/handshake/client.rs
index a6d9f1c..08cc7b2 100644
--- a/src/handshake/client.rs
+++ b/src/handshake/client.rs
@@ -87,8 +87,8 @@ impl<S: Read + Write> HandshakeRole for ClientHandshake<S> {
Ok(r) => r,
Err(Error::Http(mut e)) => {
*e.body_mut() = Some(tail);
- return Err(Error::Http(e))
- },
+ return Err(Error::Http(e));
+ }
Err(e) => return Err(e),
};
@@ -258,7 +258,7 @@ impl TryParse for Response {
impl<'h, 'b: 'h> FromHttparse<httparse::Response<'h, 'b>> for Response {
fn from_httparse(raw: httparse::Response<'h, 'b>) -> Result<Self> {
if raw.version.expect("Bug: no HTTP version") < /*1.*/1 {
- return Err(Error::Protocol(ProtocolError::WrongHttpMethod));
+ return Err(Error::Protocol(ProtocolError::WrongHttpVersion));
}
let headers = HeaderMap::from_httparse(raw.headers)?;
diff --git a/src/handshake/machine.rs b/src/handshake/machine.rs
index eacb4bf..2e3f2cb 100644
--- a/src/handshake/machine.rs
+++ b/src/handshake/machine.rs
@@ -20,7 +20,7 @@ pub struct HandshakeMachine<Stream> {
impl<Stream> HandshakeMachine<Stream> {
/// Start reading data from the peer.
pub fn start_read(stream: Stream) -> Self {
- HandshakeMachine { stream, state: HandshakeState::Reading(ReadBuffer::new()) }
+ Self { stream, state: HandshakeState::Reading(ReadBuffer::new(), AttackCheck::new()) }
}
/// Start writing data to the peer.
pub fn start_write<D: Into<Vec<u8>>>(stream: Stream, data: D) -> Self {
@@ -41,25 +41,31 @@ impl<Stream: Read + Write> HandshakeMachine<Stream> {
pub fn single_round<Obj: TryParse>(mut self) -> Result<RoundResult<Obj, Stream>> {
trace!("Doing handshake round.");
match self.state {
- HandshakeState::Reading(mut buf) => {
+ HandshakeState::Reading(mut buf, mut attack_check) => {
let read = buf.read_from(&mut self.stream).no_block()?;
match read {
Some(0) => Err(Error::Protocol(ProtocolError::HandshakeIncomplete)),
- Some(_) => Ok(if let Some((size, obj)) = Obj::try_parse(Buf::chunk(&buf))? {
- buf.advance(size);
- RoundResult::StageFinished(StageResult::DoneReading {
- result: obj,
- stream: self.stream,
- tail: buf.into_vec(),
+ Some(count) => {
+ attack_check.check_incoming_packet_size(count)?;
+ // TODO: this is slow for big headers with too many small packets.
+ // The parser has to be reworked in order to work on streams instead
+ // of buffers.
+ Ok(if let Some((size, obj)) = Obj::try_parse(Buf::chunk(&buf))? {
+ buf.advance(size);
+ RoundResult::StageFinished(StageResult::DoneReading {
+ result: obj,
+ stream: self.stream,
+ tail: buf.into_vec(),
+ })
+ } else {
+ RoundResult::Incomplete(HandshakeMachine {
+ state: HandshakeState::Reading(buf, attack_check),
+ ..self
+ })
})
- } else {
- RoundResult::Incomplete(HandshakeMachine {
- state: HandshakeState::Reading(buf),
- ..self
- })
- }),
+ }
None => Ok(RoundResult::WouldBlock(HandshakeMachine {
- state: HandshakeState::Reading(buf),
+ state: HandshakeState::Reading(buf, attack_check),
..self
})),
}
@@ -119,7 +125,54 @@ pub trait TryParse: Sized {
#[derive(Debug)]
enum HandshakeState {
/// Reading data from the peer.
- Reading(ReadBuffer),
+ Reading(ReadBuffer, AttackCheck),
/// Sending data to the peer.
Writing(Cursor<Vec<u8>>),
}
+
+/// Attack mitigation. Contains counters needed to prevent DoS attacks
+/// and reject valid but useless headers.
+#[derive(Debug)]
+pub(crate) struct AttackCheck {
+ /// Number of HTTP header successful reads (TCP packets).
+ number_of_packets: usize,
+ /// Total number of bytes in HTTP header.
+ number_of_bytes: usize,
+}
+
+impl AttackCheck {
+ /// Initialize attack checking for incoming buffer.
+ fn new() -> Self {
+ Self { number_of_packets: 0, number_of_bytes: 0 }
+ }
+
+ /// Check the size of an incoming packet. To be called immediately after `read()`
+ /// passing its returned bytes count as `size`.
+ fn check_incoming_packet_size(&mut self, size: usize) -> Result<()> {
+ self.number_of_packets += 1;
+ self.number_of_bytes += size;
+
+ // TODO: these values are hardcoded. Instead of making them configurable,
+ // rework the way HTTP header is parsed to remove this check at all.
+ const MAX_BYTES: usize = 65536;
+ const MAX_PACKETS: usize = 512;
+ const MIN_PACKET_SIZE: usize = 128;
+ const MIN_PACKET_CHECK_THRESHOLD: usize = 64;
+
+ if self.number_of_bytes > MAX_BYTES {
+ return Err(Error::AttackAttempt);
+ }
+
+ if self.number_of_packets > MAX_PACKETS {
+ return Err(Error::AttackAttempt);
+ }
+
+ if self.number_of_packets > MIN_PACKET_CHECK_THRESHOLD
+ && self.number_of_packets * MIN_PACKET_SIZE > self.number_of_bytes
+ {
+ return Err(Error::AttackAttempt);
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index ec2f828..4fdf0a6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,7 +25,7 @@ pub mod protocol;
#[cfg(feature = "handshake")]
mod server;
pub mod stream;
-#[cfg(any(feature = "native-tls", feature = "__rustls-tls"))]
+#[cfg(all(any(feature = "native-tls", feature = "__rustls-tls"), feature = "handshake"))]
mod tls;
pub mod util;
@@ -44,5 +44,5 @@ pub use crate::{
server::{accept, accept_hdr, accept_hdr_with_config, accept_with_config},
};
-#[cfg(any(feature = "native-tls", feature = "__rustls-tls"))]
+#[cfg(all(any(feature = "native-tls", feature = "__rustls-tls"), feature = "handshake"))]
pub use tls::{client_tls, client_tls_with_config, Connector};
diff --git a/src/protocol/frame/frame.rs b/src/protocol/frame/frame.rs
index 08def34..6b797a9 100644
--- a/src/protocol/frame/frame.rs
+++ b/src/protocol/frame/frame.rs
@@ -1,4 +1,4 @@
-use byteorder::{ByteOrder, NetworkEndian, ReadBytesExt, WriteBytesExt};
+use byteorder::{NetworkEndian, ReadBytesExt};
use log::*;
use std::{
borrow::Cow,
@@ -108,8 +108,12 @@ impl FrameHeader {
output.write_all(&[one, two])?;
match lenfmt {
LengthFormat::U8(_) => (),
- LengthFormat::U16 => output.write_u16::<NetworkEndian>(length as u16)?,
- LengthFormat::U64 => output.write_u64::<NetworkEndian>(length)?,
+ LengthFormat::U16 => {
+ output.write_all(&(length as u16).to_be_bytes())?;
+ }
+ LengthFormat::U64 => {
+ output.write_all(&length.to_be_bytes())?;
+ }
}
if let Some(ref mask) = self.mask {
@@ -295,7 +299,7 @@ impl Frame {
1 => Err(Error::Protocol(ProtocolError::InvalidCloseSequence)),
_ => {
let mut data = self.payload;
- let code = NetworkEndian::read_u16(&data[0..2]).into();
+ let code = u16::from_be_bytes([data[0], data[1]]).into();
data.drain(0..2);
let text = String::from_utf8(data)?;
Ok(Some(CloseFrame { code, reason: text.into() }))
@@ -340,7 +344,7 @@ impl Frame {
pub fn close(msg: Option<CloseFrame>) -> Frame {
let payload = if let Some(CloseFrame { code, reason }) = msg {
let mut p = Vec::with_capacity(reason.as_bytes().len() + 2);
- p.write_u16::<NetworkEndian>(code.into()).unwrap(); // can't fail
+ p.extend(u16::from(code).to_be_bytes());
p.extend_from_slice(reason.as_bytes());
p
} else {
@@ -366,6 +370,8 @@ impl Frame {
impl fmt::Display for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use std::fmt::Write;
+
write!(
f,
"
@@ -385,7 +391,10 @@ payload: 0x{}
// self.mask.map(|mask| format!("{:?}", mask)).unwrap_or("NONE".into()),
self.len(),
self.payload.len(),
- self.payload.iter().map(|byte| format!("{:02x}", byte)).collect::<String>()
+ self.payload.iter().fold(String::new(), |mut output, byte| {
+ _ = write!(output, "{byte:02x}");
+ output
+ })
)
}
}
diff --git a/src/protocol/frame/mask.rs b/src/protocol/frame/mask.rs
index 31ff264..ff6eb75 100644
--- a/src/protocol/frame/mask.rs
+++ b/src/protocol/frame/mask.rs
@@ -48,7 +48,7 @@ mod tests {
#[test]
fn test_apply_mask() {
let mask = [0x6d, 0xb6, 0xb2, 0x80];
- let unmasked = vec![
+ let unmasked = [
0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9,
0x12, 0x03,
];
diff --git a/src/protocol/frame/mod.rs b/src/protocol/frame/mod.rs
index 39066be..7d2ee41 100644
--- a/src/protocol/frame/mod.rs
+++ b/src/protocol/frame/mod.rs
@@ -6,15 +6,14 @@ pub mod coding;
mod frame;
mod mask;
-use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write};
-
-use log::*;
-
-pub use self::frame::{CloseFrame, Frame, FrameHeader};
use crate::{
error::{CapacityError, Error, Result},
- ReadBuffer,
+ Message, ReadBuffer,
};
+use log::*;
+use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write};
+
+pub use self::frame::{CloseFrame, Frame, FrameHeader};
/// A reader and writer for WebSocket frames.
#[derive(Debug)]
@@ -57,7 +56,7 @@ where
Stream: Read,
{
/// Read a frame from stream.
- pub fn read_frame(&mut self, max_size: Option<usize>) -> Result<Option<Frame>> {
+ pub fn read(&mut self, max_size: Option<usize>) -> Result<Option<Frame>> {
self.codec.read_frame(&mut self.stream, max_size)
}
}
@@ -66,18 +65,28 @@ impl<Stream> FrameSocket<Stream>
where
Stream: Write,
{
+ /// Writes and immediately flushes a frame.
+ /// Equivalent to calling [`write`](Self::write) then [`flush`](Self::flush).
+ pub fn send(&mut self, frame: Frame) -> Result<()> {
+ self.write(frame)?;
+ self.flush()
+ }
+
/// Write a frame to stream.
///
- /// This function guarantees that the frame is queued regardless of any errors.
- /// There is no need to resend the frame. In order to handle WouldBlock or Incomplete,
- /// call write_pending() afterwards.
- pub fn write_frame(&mut self, frame: Frame) -> Result<()> {
- self.codec.write_frame(&mut self.stream, frame)
+ /// A subsequent call should be made to [`flush`](Self::flush) to flush writes.
+ ///
+ /// This function guarantees that the frame is queued unless [`Error::WriteBufferFull`]
+ /// is returned.
+ /// In order to handle WouldBlock or Incomplete, call [`flush`](Self::flush) afterwards.
+ pub fn write(&mut self, frame: Frame) -> Result<()> {
+ self.codec.buffer_frame(&mut self.stream, frame)
}
- /// Complete pending write, if any.
- pub fn write_pending(&mut self) -> Result<()> {
- self.codec.write_pending(&mut self.stream)
+ /// Flush writes.
+ pub fn flush(&mut self) -> Result<()> {
+ self.codec.write_out_buffer(&mut self.stream)?;
+ Ok(self.stream.flush()?)
}
}
@@ -88,6 +97,14 @@ pub(super) struct FrameCodec {
in_buffer: ReadBuffer,
/// Buffer to send packets to the network.
out_buffer: Vec<u8>,
+ /// Capacity limit for `out_buffer`.
+ max_out_buffer_len: usize,
+ /// Buffer target length to reach before writing to the stream
+ /// on calls to `buffer_frame`.
+ ///
+ /// Setting this to non-zero will buffer small writes from hitting
+ /// the stream.
+ out_buffer_write_len: usize,
/// Header and remaining size of the incoming packet being processed.
header: Option<(FrameHeader, u64)>,
}
@@ -95,7 +112,13 @@ pub(super) struct FrameCodec {
impl FrameCodec {
/// Create a new frame codec.
pub(super) fn new() -> Self {
- Self { in_buffer: ReadBuffer::new(), out_buffer: Vec::new(), header: None }
+ Self {
+ in_buffer: ReadBuffer::new(),
+ out_buffer: Vec::new(),
+ max_out_buffer_len: usize::MAX,
+ out_buffer_write_len: 0,
+ header: None,
+ }
}
/// Create a new frame codec from partially read data.
@@ -103,10 +126,23 @@ impl FrameCodec {
Self {
in_buffer: ReadBuffer::from_partially_read(part),
out_buffer: Vec::new(),
+ max_out_buffer_len: usize::MAX,
+ out_buffer_write_len: 0,
header: None,
}
}
+ /// Sets a maximum size for the out buffer.
+ pub(super) fn set_max_out_buffer_len(&mut self, max: usize) {
+ self.max_out_buffer_len = max;
+ }
+
+ /// Sets [`Self::buffer_frame`] buffer target length to reach before
+ /// writing to the stream.
+ pub(super) fn set_out_buffer_write_len(&mut self, len: usize) {
+ self.out_buffer_write_len = len;
+ }
+
/// Read a frame from the provided stream.
pub(super) fn read_frame<Stream>(
&mut self,
@@ -165,19 +201,37 @@ impl FrameCodec {
Ok(Some(frame))
}
- /// Write a frame to the provided stream.
- pub(super) fn write_frame<Stream>(&mut self, stream: &mut Stream, frame: Frame) -> Result<()>
+ /// Writes a frame into the `out_buffer`.
+ /// If the out buffer size is over the `out_buffer_write_len` will also write
+ /// the out buffer into the provided `stream`.
+ ///
+ /// To ensure buffered frames are written call [`Self::write_out_buffer`].
+ ///
+ /// May write to the stream, will **not** flush.
+ pub(super) fn buffer_frame<Stream>(&mut self, stream: &mut Stream, frame: Frame) -> Result<()>
where
Stream: Write,
{
+ if frame.len() + self.out_buffer.len() > self.max_out_buffer_len {
+ return Err(Error::WriteBufferFull(Message::Frame(frame)));
+ }
+
trace!("writing frame {}", frame);
+
self.out_buffer.reserve(frame.len());
frame.format(&mut self.out_buffer).expect("Bug: can't write to vector");
- self.write_pending(stream)
+
+ if self.out_buffer.len() > self.out_buffer_write_len {
+ self.write_out_buffer(stream)
+ } else {
+ Ok(())
+ }
}
- /// Complete pending write, if any.
- pub(super) fn write_pending<Stream>(&mut self, stream: &mut Stream) -> Result<()>
+ /// Writes the out_buffer to the provided stream.
+ ///
+ /// Does **not** flush.
+ pub(super) fn write_out_buffer<Stream>(&mut self, stream: &mut Stream) -> Result<()>
where
Stream: Write,
{
@@ -193,16 +247,8 @@ impl FrameCodec {
}
self.out_buffer.drain(0..len);
}
- stream.flush()?;
- Ok(())
- }
-}
-#[cfg(test)]
-impl FrameCodec {
- /// Returns the size of the output buffer.
- pub(super) fn output_buffer_len(&self) -> usize {
- self.out_buffer.len()
+ Ok(())
}
}
@@ -224,11 +270,11 @@ mod tests {
let mut sock = FrameSocket::new(raw);
assert_eq!(
- sock.read_frame(None).unwrap().unwrap().into_data(),
+ sock.read(None).unwrap().unwrap().into_data(),
vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
);
- assert_eq!(sock.read_frame(None).unwrap().unwrap().into_data(), vec![0x03, 0x02, 0x01]);
- assert!(sock.read_frame(None).unwrap().is_none());
+ assert_eq!(sock.read(None).unwrap().unwrap().into_data(), vec![0x03, 0x02, 0x01]);
+ assert!(sock.read(None).unwrap().is_none());
let (_, rest) = sock.into_inner();
assert_eq!(rest, vec![0x99]);
@@ -239,7 +285,7 @@ mod tests {
let raw = Cursor::new(vec![0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
let mut sock = FrameSocket::from_partially_read(raw, vec![0x82, 0x07, 0x01]);
assert_eq!(
- sock.read_frame(None).unwrap().unwrap().into_data(),
+ sock.read(None).unwrap().unwrap().into_data(),
vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
);
}
@@ -249,10 +295,10 @@ mod tests {
let mut sock = FrameSocket::new(Vec::new());
let frame = Frame::ping(vec![0x04, 0x05]);
- sock.write_frame(frame).unwrap();
+ sock.send(frame).unwrap();
let frame = Frame::pong(vec![0x01]);
- sock.write_frame(frame).unwrap();
+ sock.send(frame).unwrap();
let (buf, _) = sock.into_inner();
assert_eq!(buf, vec![0x89, 0x02, 0x04, 0x05, 0x8a, 0x01, 0x01]);
@@ -264,7 +310,7 @@ mod tests {
0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
]);
let mut sock = FrameSocket::new(raw);
- let _ = sock.read_frame(None); // should not crash
+ let _ = sock.read(None); // should not crash
}
#[test]
@@ -272,7 +318,7 @@ mod tests {
let raw = Cursor::new(vec![0x82, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
let mut sock = FrameSocket::new(raw);
assert!(matches!(
- sock.read_frame(Some(5)),
+ sock.read(Some(5)),
Err(Error::Capacity(CapacityError::MessageTooLong { size: 7, max_size: 5 }))
));
}
diff --git a/src/protocol/message.rs b/src/protocol/message.rs
index cdebabc..2b2ed0b 100644
--- a/src/protocol/message.rs
+++ b/src/protocol/message.rs
@@ -185,7 +185,7 @@ impl Message {
Message::Text(string.into())
}
- /// Create a new binary WebSocket message by converting to Vec<u8>.
+ /// Create a new binary WebSocket message by converting to `Vec<u8>`.
pub fn binary<B>(bin: B) -> Message
where
B: Into<Vec<u8>>,
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 94397e9..21c996a 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -6,13 +6,6 @@ mod message;
pub use self::{frame::CloseFrame, message::Message};
-use log::*;
-use std::{
- collections::VecDeque,
- io::{ErrorKind as IoErrorKind, Read, Write},
- mem::replace,
-};
-
use self::{
frame::{
coding::{CloseCode, Control as OpCtl, Data as OpData, OpCode},
@@ -20,9 +13,11 @@ use self::{
},
message::{IncompleteMessage, IncompleteMessageType},
};
-use crate::{
- error::{Error, ProtocolError, Result},
- util::NonBlockingResult,
+use crate::error::{Error, ProtocolError, Result};
+use log::*;
+use std::{
+ io::{self, Read, Write},
+ mem::replace,
};
/// Indicates a Client or Server role of the websocket
@@ -37,15 +32,34 @@ pub enum Role {
/// The configuration for WebSocket connection.
#[derive(Debug, Clone, Copy)]
pub struct WebSocketConfig {
- /// The size of the send queue. You can use it to turn on/off the backpressure features. `None`
- /// means here that the size of the queue is unlimited. The default value is the unlimited
- /// queue.
+ /// Does nothing, instead use `max_write_buffer_size`.
+ #[deprecated]
pub max_send_queue: Option<usize>,
- /// The maximum size of a message. `None` means no size limit. The default value is 64 MiB
+ /// The target minimum size of the write buffer to reach before writing the data
+ /// to the underlying stream.
+ /// The default value is 128 KiB.
+ ///
+ /// If set to `0` each message will be eagerly written to the underlying stream.
+ /// It is often more optimal to allow them to buffer a little, hence the default value.
+ ///
+ /// Note: [`flush`](WebSocket::flush) will always fully write the buffer regardless.
+ pub write_buffer_size: usize,
+ /// The max size of the write buffer in bytes. Setting this can provide backpressure
+ /// in the case the write buffer is filling up due to write errors.
+ /// The default value is unlimited.
+ ///
+ /// Note: The write buffer only builds up past [`write_buffer_size`](Self::write_buffer_size)
+ /// when writes to the underlying stream are failing. So the **write buffer can not
+ /// fill up if you are not observing write errors even if not flushing**.
+ ///
+ /// Note: Should always be at least [`write_buffer_size + 1 message`](Self::write_buffer_size)
+ /// and probably a little more depending on error handling strategy.
+ pub max_write_buffer_size: usize,
+ /// The maximum size of an incoming message. `None` means no size limit. The default value is 64 MiB
/// which should be reasonably big for all normal use-cases but small enough to prevent
/// memory eating by a malicious user.
pub max_message_size: Option<usize>,
- /// The maximum size of a single message frame. `None` means no size limit. The limit is for
+ /// The maximum size of a single incoming message frame. `None` means no size limit. The limit is for
/// frame payload NOT including the frame header. The default value is 16 MiB which should
/// be reasonably big for all normal use-cases but small enough to prevent memory eating
/// by a malicious user.
@@ -60,8 +74,11 @@ pub struct WebSocketConfig {
impl Default for WebSocketConfig {
fn default() -> Self {
+ #[allow(deprecated)]
WebSocketConfig {
max_send_queue: None,
+ write_buffer_size: 128 * 1024,
+ max_write_buffer_size: usize::MAX,
max_message_size: Some(64 << 20),
max_frame_size: Some(16 << 20),
accept_unmasked_frames: false,
@@ -69,10 +86,23 @@ impl Default for WebSocketConfig {
}
}
+impl WebSocketConfig {
+ /// Panic if values are invalid.
+ pub(crate) fn assert_valid(&self) {
+ assert!(
+ self.max_write_buffer_size > self.write_buffer_size,
+ "WebSocketConfig::max_write_buffer_size must be greater than write_buffer_size, \
+ see WebSocketConfig docs`"
+ );
+ }
+}
+
/// WebSocket input-output stream.
///
/// This is THE structure you want to create to be able to speak the WebSocket protocol.
/// It may be created by calling `connect`, `accept` or `client` functions.
+///
+/// Use [`WebSocket::read`], [`WebSocket::send`] to received and send messages.
#[derive(Debug)]
pub struct WebSocket<Stream> {
/// The underlying socket.
@@ -87,6 +117,9 @@ impl<Stream> WebSocket<Stream> {
/// Call this function if you're using Tungstenite as a part of a web framework
/// or together with an existing one. If you need an initial handshake, use
/// `connect()` or `accept()` functions of the crate to construct a websocket.
+ ///
+ /// # Panics
+ /// Panics if config is invalid e.g. `max_write_buffer_size <= write_buffer_size`.
pub fn from_raw_socket(stream: Stream, role: Role, config: Option<WebSocketConfig>) -> Self {
WebSocket { socket: stream, context: WebSocketContext::new(role, config) }
}
@@ -96,6 +129,9 @@ impl<Stream> WebSocket<Stream> {
/// Call this function if you're using Tungstenite as a part of a web framework
/// or together with an existing one. If you need an initial handshake, use
/// `connect()` or `accept()` functions of the crate to construct a websocket.
+ ///
+ /// # Panics
+ /// Panics if config is invalid e.g. `max_write_buffer_size <= write_buffer_size`.
pub fn from_partially_read(
stream: Stream,
part: Vec<u8>,
@@ -118,6 +154,9 @@ impl<Stream> WebSocket<Stream> {
}
/// Change the configuration.
+ ///
+ /// # Panics
+ /// Panics if config is invalid e.g. `max_write_buffer_size <= write_buffer_size`.
pub fn set_config(&mut self, set_func: impl FnOnce(&mut WebSocketConfig)) {
self.context.set_config(set_func)
}
@@ -146,82 +185,116 @@ impl<Stream> WebSocket<Stream> {
impl<Stream: Read + Write> WebSocket<Stream> {
/// Read a message from stream, if possible.
///
- /// This will queue responses to ping and close messages to be sent. It will call
- /// `write_pending` before trying to read in order to make sure that those responses
- /// make progress even if you never call `write_pending`. That does mean that they
- /// get sent out earliest on the next call to `read_message`, `write_message` or `write_pending`.
+ /// This will also queue responses to ping and close messages. These responses
+ /// will be written and flushed on the next call to [`read`](Self::read),
+ /// [`write`](Self::write) or [`flush`](Self::flush).
///
- /// ## Closing the connection
+ /// # Closing the connection
/// When the remote endpoint decides to close the connection this will return
/// the close message with an optional close frame.
///
- /// You should continue calling `read_message`, `write_message` or `write_pending` to drive
- /// the reply to the close frame until [Error::ConnectionClosed] is returned. Once that happens
- /// it is safe to drop the underlying connection.
- pub fn read_message(&mut self) -> Result<Message> {
- self.context.read_message(&mut self.socket)
+ /// You should continue calling [`read`](Self::read), [`write`](Self::write) or
+ /// [`flush`](Self::flush) to drive the reply to the close frame until [`Error::ConnectionClosed`]
+ /// is returned. Once that happens it is safe to drop the underlying connection.
+ pub fn read(&mut self) -> Result<Message> {
+ self.context.read(&mut self.socket)
}
- /// Send a message to stream, if possible.
+ /// Writes and immediately flushes a message.
+ /// Equivalent to calling [`write`](Self::write) then [`flush`](Self::flush).
+ pub fn send(&mut self, message: Message) -> Result<()> {
+ self.write(message)?;
+ self.flush()
+ }
+
+ /// Write a message to the provided stream, if possible.
+ ///
+ /// A subsequent call should be made to [`flush`](Self::flush) to flush writes.
///
- /// WebSocket will buffer a configurable number of messages at a time, except to reply to Ping
- /// requests. A Pong reply will jump the queue because the
- /// [websocket RFC](https://tools.ietf.org/html/rfc6455#section-5.5.2) specifies it should be sent
- /// as soon as is practical.
+ /// In the event of stream write failure the message frame will be stored
+ /// in the write buffer and will try again on the next call to [`write`](Self::write)
+ /// or [`flush`](Self::flush).
///
- /// Note that upon receiving a ping message, tungstenite cues a pong reply automatically.
- /// When you call either `read_message`, `write_message` or `write_pending` next it will try to send
- /// that pong out if the underlying connection can take more data. This means you should not
- /// respond to ping frames manually.
+ /// If the write buffer would exceed the configured [`WebSocketConfig::max_write_buffer_size`]
+ /// [`Err(WriteBufferFull(msg_frame))`](Error::WriteBufferFull) is returned.
+ ///
+ /// This call will generally not flush. However, if there are queued automatic messages
+ /// they will be written and eagerly flushed.
+ ///
+ /// For example, upon receiving ping messages tungstenite queues pong replies automatically.
+ /// The next call to [`read`](Self::read), [`write`](Self::write) or [`flush`](Self::flush)
+ /// will write & flush the pong reply. This means you should not respond to ping frames manually.
///
/// You can however send pong frames manually in order to indicate a unidirectional heartbeat
/// as described in [RFC 6455](https://tools.ietf.org/html/rfc6455#section-5.5.3). Note that
- /// if `read_message` returns a ping, you should call `write_pending` until it doesn't return
- /// WouldBlock before passing a pong to `write_message`, otherwise the response to the
- /// ping will not be sent, but rather replaced by your custom pong message.
- ///
- /// ## Errors
- /// - If the WebSocket's send queue is full, `SendQueueFull` will be returned
- /// along with the passed message. Otherwise, the message is queued and Ok(()) is returned.
- /// - If the connection is closed and should be dropped, this will return [Error::ConnectionClosed].
- /// - If you try again after [Error::ConnectionClosed] was returned either from here or from `read_message`,
- /// [Error::AlreadyClosed] will be returned. This indicates a program error on your part.
- /// - [Error::Io] is returned if the underlying connection returns an error
+ /// if [`read`](Self::read) returns a ping, you should [`flush`](Self::flush) before passing
+ /// a custom pong to [`write`](Self::write), otherwise the automatic queued response to the
+ /// ping will not be sent as it will be replaced by your custom pong message.
+ ///
+ /// # Errors
+ /// - If the WebSocket's write buffer is full, [`Error::WriteBufferFull`] will be returned
+ /// along with the equivalent passed message frame.
+ /// - If the connection is closed and should be dropped, this will return [`Error::ConnectionClosed`].
+ /// - If you try again after [`Error::ConnectionClosed`] was returned either from here or from
+ /// [`read`](Self::read), [`Error::AlreadyClosed`] will be returned. This indicates a program
+ /// error on your part.
+ /// - [`Error::Io`] is returned if the underlying connection returns an error
/// (consider these fatal except for WouldBlock).
- /// - [Error::Capacity] if your message size is bigger than the configured max message size.
- pub fn write_message(&mut self, message: Message) -> Result<()> {
- self.context.write_message(&mut self.socket, message)
+ /// - [`Error::Capacity`] if your message size is bigger than the configured max message size.
+ pub fn write(&mut self, message: Message) -> Result<()> {
+ self.context.write(&mut self.socket, message)
}
- /// Flush the pending send queue.
- pub fn write_pending(&mut self) -> Result<()> {
- self.context.write_pending(&mut self.socket)
+ /// Flush writes.
+ ///
+ /// Ensures all messages previously passed to [`write`](Self::write) and automatic
+ /// queued pong responses are written & flushed into the underlying stream.
+ pub fn flush(&mut self) -> Result<()> {
+ self.context.flush(&mut self.socket)
}
/// Close the connection.
///
/// This function guarantees that the close frame will be queued.
/// There is no need to call it again. Calling this function is
- /// the same as calling `write_message(Message::Close(..))`.
+ /// the same as calling `write(Message::Close(..))`.
///
- /// After queuing the close frame you should continue calling `read_message` or
- /// `write_pending` to drive the close handshake to completion.
+ /// After queuing the close frame you should continue calling [`read`](Self::read) or
+ /// [`flush`](Self::flush) to drive the close handshake to completion.
///
/// The websocket RFC defines that the underlying connection should be closed
/// by the server. Tungstenite takes care of this asymmetry for you.
///
/// When the close handshake is finished (we have both sent and received
- /// a close message), `read_message` or `write_pending` will return
+ /// a close message), [`read`](Self::read) or [`flush`](Self::flush) will return
/// [Error::ConnectionClosed] if this endpoint is the server.
///
/// If this endpoint is a client, [Error::ConnectionClosed] will only be
/// returned after the server has closed the underlying connection.
///
/// It is thus safe to drop the underlying connection as soon as [Error::ConnectionClosed]
- /// is returned from `read_message` or `write_pending`.
+ /// is returned from [`read`](Self::read) or [`flush`](Self::flush).
pub fn close(&mut self, code: Option<CloseFrame>) -> Result<()> {
self.context.close(&mut self.socket, code)
}
+
+ /// Old name for [`read`](Self::read).
+ #[deprecated(note = "Use `read`")]
+ pub fn read_message(&mut self) -> Result<Message> {
+ self.read()
+ }
+
+ /// Old name for [`send`](Self::send).
+ #[deprecated(note = "Use `send`")]
+ pub fn write_message(&mut self, message: Message) -> Result<()> {
+ self.send(message)
+ }
+
+ /// Old name for [`flush`](Self::flush).
+ #[deprecated(note = "Use `flush`")]
+ pub fn write_pending(&mut self) -> Result<()> {
+ self.flush()
+ }
}
/// A context for managing WebSocket stream.
@@ -235,39 +308,56 @@ pub struct WebSocketContext {
state: WebSocketState,
/// Receive: an incomplete message being processed.
incomplete: Option<IncompleteMessage>,
- /// Send: a data send queue.
- send_queue: VecDeque<Frame>,
- /// Send: an OOB pong message.
- pong: Option<Frame>,
+ /// Send in addition to regular messages E.g. "pong" or "close".
+ additional_send: Option<Frame>,
+ /// True indicates there is an additional message (like a pong)
+ /// that failed to flush previously and we should try again.
+ unflushed_additional: bool,
/// The configuration for the websocket session.
config: WebSocketConfig,
}
impl WebSocketContext {
/// Create a WebSocket context that manages a post-handshake stream.
+ ///
+ /// # Panics
+ /// Panics if config is invalid e.g. `max_write_buffer_size <= write_buffer_size`.
pub fn new(role: Role, config: Option<WebSocketConfig>) -> Self {
- WebSocketContext {
- role,
- frame: FrameCodec::new(),
- state: WebSocketState::Active,
- incomplete: None,
- send_queue: VecDeque::new(),
- pong: None,
- config: config.unwrap_or_default(),
- }
+ Self::_new(role, FrameCodec::new(), config.unwrap_or_default())
}
/// Create a WebSocket context that manages an post-handshake stream.
+ ///
+ /// # Panics
+ /// Panics if config is invalid e.g. `max_write_buffer_size <= write_buffer_size`.
pub fn from_partially_read(part: Vec<u8>, role: Role, config: Option<WebSocketConfig>) -> Self {
- WebSocketContext {
- frame: FrameCodec::from_partially_read(part),
- ..WebSocketContext::new(role, config)
+ Self::_new(role, FrameCodec::from_partially_read(part), config.unwrap_or_default())
+ }
+
+ fn _new(role: Role, mut frame: FrameCodec, config: WebSocketConfig) -> Self {
+ config.assert_valid();
+ frame.set_max_out_buffer_len(config.max_write_buffer_size);
+ frame.set_out_buffer_write_len(config.write_buffer_size);
+ Self {
+ role,
+ frame,
+ state: WebSocketState::Active,
+ incomplete: None,
+ additional_send: None,
+ unflushed_additional: false,
+ config,
}
}
/// Change the configuration.
+ ///
+ /// # Panics
+ /// Panics if config is invalid e.g. `max_write_buffer_size <= write_buffer_size`.
pub fn set_config(&mut self, set_func: impl FnOnce(&mut WebSocketConfig)) {
- set_func(&mut self.config)
+ set_func(&mut self.config);
+ self.config.assert_valid();
+ self.frame.set_max_out_buffer_len(self.config.max_write_buffer_size);
+ self.frame.set_out_buffer_write_len(self.config.write_buffer_size);
}
/// Read the configuration.
@@ -294,17 +384,29 @@ impl WebSocketContext {
///
/// This function sends pong and close responses automatically.
/// However, it never blocks on write.
- pub fn read_message<Stream>(&mut self, stream: &mut Stream) -> Result<Message>
+ pub fn read<Stream>(&mut self, stream: &mut Stream) -> Result<Message>
where
Stream: Read + Write,
{
// Do not read from already closed connections.
- self.state.check_active()?;
+ self.state.check_not_terminated()?;
loop {
- // Since we may get ping or close, we need to reply to the messages even during read.
- // Thus we call write_pending() but ignore its blocking.
- self.write_pending(stream).no_block()?;
+ if self.additional_send.is_some() || self.unflushed_additional {
+ // Since we may get ping or close, we need to reply to the messages even during read.
+ match self.flush(stream) {
+ Ok(_) => {}
+ Err(Error::Io(err)) if err.kind() == io::ErrorKind::WouldBlock => {
+ // If blocked continue reading, but try again later
+ self.unflushed_additional = true;
+ }
+ Err(err) => return Err(err),
+ }
+ } else if self.role == Role::Server && !self.state.can_read() {
+ self.state = WebSocketState::Terminated;
+ return Err(Error::ConnectionClosed);
+ }
+
// If we get here, either write blocks or we have nothing to write.
// Thus if read blocks, just let it return WouldBlock.
if let Some(message) = self.read_message_frame(stream)? {
@@ -314,78 +416,96 @@ impl WebSocketContext {
}
}
- /// Send a message to the provided stream, if possible.
+ /// Write a message to the provided stream.
///
- /// WebSocket will buffer a configurable number of messages at a time, except to reply to Ping
- /// and Close requests. If the WebSocket's send queue is full, `SendQueueFull` will be returned
- /// along with the passed message. Otherwise, the message is queued and Ok(()) is returned.
+ /// A subsequent call should be made to [`flush`](Self::flush) to flush writes.
///
- /// Note that only the last pong frame is stored to be sent, and only the
- /// most recent pong frame is sent if multiple pong frames are queued.
- pub fn write_message<Stream>(&mut self, stream: &mut Stream, message: Message) -> Result<()>
+ /// In the event of stream write failure the message frame will be stored
+ /// in the write buffer and will try again on the next call to [`write`](Self::write)
+ /// or [`flush`](Self::flush).
+ ///
+ /// If the write buffer would exceed the configured [`WebSocketConfig::max_write_buffer_size`]
+ /// [`Err(WriteBufferFull(msg_frame))`](Error::WriteBufferFull) is returned.
+ pub fn write<Stream>(&mut self, stream: &mut Stream, message: Message) -> Result<()>
where
Stream: Read + Write,
{
// When terminated, return AlreadyClosed.
- self.state.check_active()?;
+ self.state.check_not_terminated()?;
// Do not write after sending a close frame.
if !self.state.is_active() {
return Err(Error::Protocol(ProtocolError::SendAfterClosing));
}
- if let Some(max_send_queue) = self.config.max_send_queue {
- if self.send_queue.len() >= max_send_queue {
- // Try to make some room for the new message.
- // Do not return here if write would block, ignore WouldBlock silently
- // since we must queue the message anyway.
- self.write_pending(stream).no_block()?;
- }
-
- if self.send_queue.len() >= max_send_queue {
- return Err(Error::SendQueueFull(message));
- }
- }
-
let frame = match message {
Message::Text(data) => Frame::message(data.into(), OpCode::Data(OpData::Text), true),
Message::Binary(data) => Frame::message(data, OpCode::Data(OpData::Binary), true),
Message::Ping(data) => Frame::ping(data),
Message::Pong(data) => {
- self.pong = Some(Frame::pong(data));
- return self.write_pending(stream);
+ self.set_additional(Frame::pong(data));
+ // Note: user pongs can be user flushed so no need to flush here
+ return self._write(stream, None).map(|_| ());
}
Message::Close(code) => return self.close(stream, code),
Message::Frame(f) => f,
};
- self.send_queue.push_back(frame);
- self.write_pending(stream)
+ let should_flush = self._write(stream, Some(frame))?;
+ if should_flush {
+ self.flush(stream)?;
+ }
+ Ok(())
}
- /// Flush the pending send queue.
- pub fn write_pending<Stream>(&mut self, stream: &mut Stream) -> Result<()>
+ /// Flush writes.
+ ///
+ /// Ensures all messages previously passed to [`write`](Self::write) and automatically
+ /// queued pong responses are written & flushed into the `stream`.
+ #[inline]
+ pub fn flush<Stream>(&mut self, stream: &mut Stream) -> Result<()>
where
Stream: Read + Write,
{
- // First, make sure we have no pending frame sending.
- self.frame.write_pending(stream)?;
+ self._write(stream, None)?;
+ self.frame.write_out_buffer(stream)?;
+ stream.flush()?;
+ self.unflushed_additional = false;
+ Ok(())
+ }
+
+ /// Writes any data in the out_buffer, `additional_send` and given `data`.
+ ///
+ /// Does **not** flush.
+ ///
+ /// Returns true if the write contents indicate we should flush immediately.
+ fn _write<Stream>(&mut self, stream: &mut Stream, data: Option<Frame>) -> Result<bool>
+ where
+ Stream: Read + Write,
+ {
+ if let Some(data) = data {
+ self.buffer_frame(stream, data)?;
+ }
// Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
// response, unless it already received a Close frame. It SHOULD
// respond with Pong frame as soon as is practical. (RFC 6455)
- if let Some(pong) = self.pong.take() {
- trace!("Sending pong reply");
- self.send_one_frame(stream, pong)?;
- }
- // If we have any unsent frames, send them.
- trace!("Frames still in queue: {}", self.send_queue.len());
- while let Some(data) = self.send_queue.pop_front() {
- self.send_one_frame(stream, data)?;
- }
-
- // If we get to this point, the send queue is empty and the underlying socket is still
- // willing to take more data.
+ let should_flush = if let Some(msg) = self.additional_send.take() {
+ trace!("Sending pong/close");
+ match self.buffer_frame(stream, msg) {
+ Err(Error::WriteBufferFull(Message::Frame(msg))) => {
+ // if an system message would exceed the buffer put it back in
+ // `additional_send` for retry. Otherwise returning this error
+ // may not make sense to the user, e.g. calling `flush`.
+ self.set_additional(msg);
+ false
+ }
+ Err(err) => return Err(err),
+ Ok(_) => true,
+ }
+ } else {
+ self.unflushed_additional
+ };
// If we're closing and there is nothing to send anymore, we should close the connection.
if self.role == Role::Server && !self.state.can_read() {
@@ -395,10 +515,11 @@ impl WebSocketContext {
// maximum segment lifetimes (2MSL), while there is no corresponding
// server impact as a TIME_WAIT connection is immediately reopened upon
// a new SYN with a higher seq number). (RFC 6455)
+ self.frame.write_out_buffer(stream)?;
self.state = WebSocketState::Terminated;
Err(Error::ConnectionClosed)
} else {
- Ok(())
+ Ok(should_flush)
}
}
@@ -406,7 +527,7 @@ impl WebSocketContext {
///
/// This function guarantees that the close frame will be queued.
/// There is no need to call it again. Calling this function is
- /// the same as calling `write(Message::Close(..))`.
+ /// the same as calling `send(Message::Close(..))`.
pub fn close<Stream>(&mut self, stream: &mut Stream, code: Option<CloseFrame>) -> Result<()>
where
Stream: Read + Write,
@@ -414,11 +535,9 @@ impl WebSocketContext {
if let WebSocketState::Active = self.state {
self.state = WebSocketState::ClosedByUs;
let frame = Frame::close(code);
- self.send_queue.push_back(frame);
- } else {
- // Already closed, nothing to do.
+ self._write(stream, Some(frame))?;
}
- self.write_pending(stream)
+ self.flush(stream)
}
/// Try to decode one message frame. May return None.
@@ -487,7 +606,7 @@ impl WebSocketContext {
let data = frame.into_data();
// No ping processing after we sent a close frame.
if self.state.is_active() {
- self.pong = Some(Frame::pong(data.clone()));
+ self.set_additional(Frame::pong(data.clone()));
}
Ok(Some(Message::Ping(data)))
}
@@ -571,7 +690,7 @@ impl WebSocketContext {
let reply = Frame::close(close.clone());
debug!("Replying to close with {:?}", reply);
- self.send_queue.push_back(reply);
+ self.set_additional(reply);
Some(close)
}
@@ -588,8 +707,8 @@ impl WebSocketContext {
}
}
- /// Send a single pending frame.
- fn send_one_frame<Stream>(&mut self, stream: &mut Stream, mut frame: Frame) -> Result<()>
+ /// Write a single frame into the write-buffer.
+ fn buffer_frame<Stream>(&mut self, stream: &mut Stream, mut frame: Frame) -> Result<()>
where
Stream: Read + Write,
{
@@ -603,7 +722,18 @@ impl WebSocketContext {
}
trace!("Sending frame: {:?}", frame);
- self.frame.write_frame(stream, frame).check_connection_reset(self.state)
+ self.frame.buffer_frame(stream, frame).check_connection_reset(self.state)
+ }
+
+ /// Replace `additional_send` if it is currently a `Pong` message.
+ fn set_additional(&mut self, add: Frame) {
+ let empty_or_pong = self
+ .additional_send
+ .as_ref()
+ .map_or(true, |f| f.header().opcode == OpCode::Control(OpCtl::Pong));
+ if empty_or_pong {
+ self.additional_send.replace(add);
+ }
}
}
@@ -636,7 +766,7 @@ impl WebSocketState {
}
/// Check if the state is active, return error if not.
- fn check_active(self) -> Result<()> {
+ fn check_not_terminated(self) -> Result<()> {
match self {
WebSocketState::Terminated => Err(Error::AlreadyClosed),
_ => Ok(()),
@@ -653,7 +783,7 @@ impl<T> CheckConnectionReset for Result<T> {
fn check_connection_reset(self, state: WebSocketState) -> Self {
match self {
Err(Error::Io(io_error)) => Err({
- if !state.can_read() && io_error.kind() == IoErrorKind::ConnectionReset {
+ if !state.can_read() && io_error.kind() == io::ErrorKind::ConnectionReset {
Error::ConnectionClosed
} else {
Error::Io(io_error)
@@ -688,64 +818,6 @@ mod tests {
}
}
- struct WouldBlockStreamMoc;
-
- impl io::Write for WouldBlockStreamMoc {
- fn write(&mut self, _: &[u8]) -> io::Result<usize> {
- Err(io::Error::new(io::ErrorKind::WouldBlock, "would block"))
- }
- fn flush(&mut self) -> io::Result<()> {
- Err(io::Error::new(io::ErrorKind::WouldBlock, "would block"))
- }
- }
-
- impl io::Read for WouldBlockStreamMoc {
- fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
- Err(io::Error::new(io::ErrorKind::WouldBlock, "would block"))
- }
- }
-
- #[test]
- fn queue_logic() {
- // Create a socket with the queue size of 1.
- let mut socket = WebSocket::from_raw_socket(
- WouldBlockStreamMoc,
- Role::Client,
- Some(WebSocketConfig { max_send_queue: Some(1), ..Default::default() }),
- );
-
- // Test message that we're going to send.
- let message = Message::Binary(vec![0xFF; 1024]);
-
- // Helper to check the error.
- let assert_would_block = |error| {
- if let Error::Io(io_error) = error {
- assert_eq!(io_error.kind(), io::ErrorKind::WouldBlock);
- } else {
- panic!("Expected WouldBlock error");
- }
- };
-
- // The first attempt of writing must not fail, since the queue is empty at start.
- // But since the underlying mock object always returns `WouldBlock`, so is the result.
- assert_would_block(dbg!(socket.write_message(message.clone()).unwrap_err()));
-
- // Any subsequent attempts must return an error telling that the queue is full.
- for _i in 0..100 {
- assert!(matches!(
- socket.write_message(message.clone()).unwrap_err(),
- Error::SendQueueFull(..)
- ));
- }
-
- // The size of the output buffer must not be bigger than the size of that message
- // that we managed to write to the output buffer at first. Since we could not make
- // any progress (because of the logic of the moc buffer), the size remains unchanged.
- if socket.context.frame.output_buffer_len() > message.len() {
- panic!("Too many frames in the queue");
- }
- }
-
#[test]
fn receive_messages() {
let incoming = Cursor::new(vec![
@@ -754,10 +826,10 @@ mod tests {
0x03,
]);
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, None);
- assert_eq!(socket.read_message().unwrap(), Message::Ping(vec![1, 2]));
- assert_eq!(socket.read_message().unwrap(), Message::Pong(vec![3]));
- assert_eq!(socket.read_message().unwrap(), Message::Text("Hello, World!".into()));
- assert_eq!(socket.read_message().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03]));
+ assert_eq!(socket.read().unwrap(), Message::Ping(vec![1, 2]));
+ assert_eq!(socket.read().unwrap(), Message::Pong(vec![3]));
+ assert_eq!(socket.read().unwrap(), Message::Text("Hello, World!".into()));
+ assert_eq!(socket.read().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03]));
}
#[test]
@@ -770,7 +842,7 @@ mod tests {
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit));
assert!(matches!(
- socket.read_message(),
+ socket.read(),
Err(Error::Capacity(CapacityError::MessageTooLong { size: 13, max_size: 10 }))
));
}
@@ -782,7 +854,7 @@ mod tests {
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit));
assert!(matches!(
- socket.read_message(),
+ socket.read(),
Err(Error::Capacity(CapacityError::MessageTooLong { size: 3, max_size: 2 }))
));
}
diff --git a/src/tls.rs b/src/tls.rs
index 0bca333..836b7ae 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -70,7 +70,8 @@ mod encryption {
#[cfg(feature = "__rustls-tls")]
pub mod rustls {
- use rustls::{ClientConfig, ClientConnection, RootCertStore, ServerName, StreamOwned};
+ use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned};
+ use rustls_pki_types::ServerName;
use std::{
convert::TryFrom,
@@ -104,35 +105,27 @@ mod encryption {
#[cfg(feature = "rustls-tls-native-roots")]
{
- for cert in rustls_native_certs::load_native_certs()? {
- root_store
- .add(&rustls::Certificate(cert.0))
- .map_err(TlsError::Rustls)?;
- }
+ let native_certs = rustls_native_certs::load_native_certs()?;
+ let total_number = native_certs.len();
+ let (number_added, number_ignored) =
+ root_store.add_parsable_certificates(native_certs);
+ log::debug!("Added {number_added}/{total_number} native root certificates (ignored {number_ignored})");
}
#[cfg(feature = "rustls-tls-webpki-roots")]
{
- root_store.add_server_trust_anchors(
- webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
- rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
- ta.subject,
- ta.spki,
- ta.name_constraints,
- )
- })
- );
+ root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
}
Arc::new(
ClientConfig::builder()
- .with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth(),
)
}
};
- let domain =
- ServerName::try_from(domain).map_err(|_| TlsError::InvalidDnsName)?;
+ let domain = ServerName::try_from(domain)
+ .map_err(|_| TlsError::InvalidDnsName)?
+ .to_owned();
let client = ClientConnection::new(config, domain).map_err(TlsError::Rustls)?;
let stream = StreamOwned::new(client, socket);