Upgrade tungstenite to 0.21.0

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

Test: TreeHugger
Change-Id: I9970d3a006e438c1ea8c54cb6d3b7a41daa21ae9
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 @@
     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 @@
 
 [[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 @@
 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 @@
 
 [[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 @@
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
-name = "block-buffer"
-version = "0.10.2"
+name = "bitflags"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
 dependencies = [
  "generic-array",
 ]
 
 [[package]]
 name = "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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
+dependencies = [
+ "anstyle",
  "clap_lex",
- "indexmap",
- "textwrap",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.2.4"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
-dependencies = [
- "os_str_bytes",
-]
+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 @@
 
 [[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 @@
 ]
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[package]]
 name = "errno"
-version = "0.3.0"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
 dependencies = [
- "errno-dragonfly",
  "libc",
- "windows-sys 0.45.0",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-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 @@
 
 [[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 @@
 
 [[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 @@
 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 @@
 
 [[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 @@
 
 [[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 @@
 ]
 
 [[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 @@
 
 [[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"
+version = "0.4.20"
 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"
-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 @@
 ]
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 ]
 
 [[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 @@
 
 [[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,51 +661,59 @@
 
 [[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-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -823,96 +722,96 @@
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.27"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-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",
-]
-
-[[package]]
-name = "rustix"
-version = "0.37.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3eb76a3b09109e78c52d45979fea3cd8ddaadb223531d0846bedb60e72c3e99"
-dependencies = [
- "bitflags",
- "errno",
- "io-lifetimes",
- "libc",
- "linux-raw-sys",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
-name = "rustls"
-version = "0.21.0"
+name = "rustix"
+version = "0.38.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d"
+checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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-webpki"
-version = "0.100.1"
+name = "rustls-pki-types"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
+checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 = "spin"
-version = "0.5.2"
+name = "socket2"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "spin"
+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 = "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 @@
 
 [[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 @@
 
 [[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 @@
  "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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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 @@
 
 [[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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
-[[package]]
-name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.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"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.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"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.36.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.42.2"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.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 @@
 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 @@
 [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 @@
 optional = true
 
 [dependencies.http]
-version = "0.2"
+version = "1.0"
 optional = true
 
 [dependencies.httparse]
@@ -78,11 +107,15 @@
 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 @@
 [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 @@
 [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 @@
 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-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 @@
 
 [dependencies.rustls]
 optional = true
-version = "0.21.0"
+version = "0.22.0"
+
+[dependencies.rustls-pki-types]
+optional = true
+version = "1.0"
 
 [dependencies.rustls-native-certs]
 optional = true
-version = "0.6.0"
-
-[dependencies.webpki]
-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 @@
         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 @@
 [![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 @@
 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 @@
         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 @@
     /// 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 @@
     #[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 @@
                     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<'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 @@
 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 @@
     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 @@
 #[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 @@
 #[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 @@
     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 @@
         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 @@
             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 @@
     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 fmt::Display for Frame {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use std::fmt::Write;
+
         write!(
             f,
             "
@@ -385,7 +391,10 @@
             // 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 @@
     #[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 @@
 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 @@
     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 @@
 where
     Stream: Write,
 {
-    /// 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)
+    /// 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()
     }
 
-    /// Complete pending write, if any.
-    pub fn write_pending(&mut self) -> Result<()> {
-        self.codec.write_pending(&mut self.stream)
+    /// Write a frame to stream.
+    ///
+    /// 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)
+    }
+
+    /// Flush writes.
+    pub fn flush(&mut self) -> Result<()> {
+        self.codec.write_out_buffer(&mut self.stream)?;
+        Ok(self.stream.flush()?)
     }
 }
 
@@ -88,6 +97,14 @@
     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 @@
 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 @@
         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 @@
         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 @@
             }
             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 @@
         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 @@
         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 @@
         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 @@
             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 @@
         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 @@
         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 @@
 
 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 @@
     },
     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 @@
 /// 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 @@
 
 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 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 @@
     /// 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 @@
     /// 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 @@
     }
 
     /// 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: 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.
     ///
-    /// 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.
+    /// A subsequent call should be made to [`flush`](Self::flush) to flush writes.
     ///
-    /// 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.
+    /// 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.
+    ///
+    /// 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.
+    /// 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 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
+    /// # 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 @@
     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 @@
     ///
     /// 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 @@
         }
     }
 
-    /// 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 @@
             // 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 @@
     ///
     /// 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 @@
         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 @@
                             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 @@
 
                 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 @@
         }
     }
 
-    /// 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 @@
         }
 
         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 @@
     }
 
     /// 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 @@
     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 @@
         }
     }
 
-    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 @@
             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 @@
         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 @@
         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 @@
 
     #[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 @@
 
                             #[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);