Repository: 1c3t3a/rust-socketio (language: rust)

Issue:
Switch websocket library
This library currently uses [the websocket crate](https://github.com/websockets-rs/rust-websocket/). However the library looks kind of abandoned and might not be the best fit for future plans like `async / await` support. Currently the library provides async support based on `tokio` 0.1. This could lead to problems and there are no real plans to get the support running with websocket (see [#242](https://github.com/websockets-rs/rust-websocket/issues/242)). It's also not to be expected that the library will provide support in case we have a problem, so switching the library might be a good idea. A battle proven implementation is provided by `tungstenite-rs` (async [here](https://github.com/snapview/tokio-tungstenite), blocking [here](https://github.com/snapview/tungstenite-rs)). What do you think @nshaaban-cPacket? 


Discussion / hints:
Sounds good, might be worth doing before the engineio crate goes public? I'm not interested in implementing the change in the near future (only if it causes headache with 0.5.0).
Alright, I could implement it as soon as refactoring is merged. Also a good point that this should be done before engine.io goes public, then I am not tied to a certain API.
> Alright, I could implement it as soon as refactoring is merged. Also a good point that this should be done before engine.io goes public, then I am not tied to a certain API.

Doesn't necessary have to be before, but might make sense. Ideally what library we are using internally for websockes/polling would not be exposed anyways.
websocket *is* exposed here https://github.com/1c3t3a/rust-socketio/blob/9b1183b0313b52b82bb9e09f7c75f223cb9958ee/engineio/src/error.rs#L44

Plus we'll need the https://docs.rs/tungstenite/0.15.0/tungstenite/fn.client_tls_with_config.html to continue supporting passing in a TlsConnector
May want to escalate this @1c3t3a websocket (latest) depends on hyper 0.10.6 which is vulnerable to RUSTSEC-2021-0079 and RUSTSEC-2021-0078 (found using cargo-audit)
> May want to escalate this @1c3t3a websocket (latest) depends on hyper 0.10.6 which is vulnerable to RUSTSEC-2021-0079 and RUSTSEC-2021-0078 (found using cargo-audit)

This is actually really bad... I've looked into it and the only solution for `websocket` would be to update to a `
hyper version `>= 0.14.10`. But this introduces quite massive changes as hyper changed it's entire API around headers and header parsing and multiple other stuff. I started to fix this on my fork but ran into bigger problems. For know I am going to open an issue at websocket (see [#262](https://github.com/websockets-rs/rust-websocket/issues/262)), but I wouldn't necessarily be sure that a fix will be submitted soon. For rust_socketio it would maybe make sense to switch to tungstenite, despite the loss in performance. But let's see how the reaction is at websocket.
`websocket` crate is basically unmaintained now. Switching to `tungstenite` is recommended.

---

Does it make sense to release a new version of `websocket` crate that just points to (reexports) `tungstenite` and `tokio-tungstenite`, to prevent new accidental dependencies on the unmaintained code? Would benefits outweigh the harm? Currently `websocket` crate contains a warning in README:

> Note: Maintainership of this project is slugglish. You may want to use tungstenite or tokio-tungstenite instead.

Have you missed it when you were shopping around for a Websocket library?
Hi @vi, first of all thanks for the quick help! We originally wanted to switch to tungstenite, but the problem is that the sync version of it is by far slower than websocket.

> Observed significant performance degradation (25s minimum, peak of 75s) in the following case (client::socket::test::socket_io_integration):
> - Callback mode (where there is a separate thread constantly calling poll)
> - Attempt to send packets on the clone of the socket returned
> - Observe that websocket connection is busy reading
> - When a ping packet is received it is possible that the lock switches to the write
> - Socket finally sends a message

This is due to sync tungstenite not supporting the `.split` operation on their sender / receiver stream, something that websocket is capable of. The long time goal is definitely to switch to `tokio-tungstenite` (which is way faster), but we didn't make the switch yet, as this requires quite a lot of restructuring internally.

When I started the project about a year ago I didn't see the warning on web sockets Readme (I don't know if it wasn't there of if I just missed it), but the API seemed fine and the project was a bit more vivid then now.
> This is due to sync tungstenite not supporting the .split operation on their sender / receiver stream

Note that `rust-websocket`s support for splitting sync sockets is only reliable only for non-encrypted `ws://` websockets. Maybe that's why sync `tungstenite` does not expose this function.

Splitting should work properly for async, including for `wss://`, both in `rust-websocket` and `tokio-tungstenite`.
> When I started the project about a year ago I didn't see the warning on web sockets Readme (I don't know if it wasn't there of if I just missed it)

It is more than a year ago: https://github.com/websockets-rs/rust-websocket/commit/b4f50dd7e18047bd085c8b9585cb6250ee2c7f7d
> It is more than a year ago: [websockets-rs/rust-websocket@b4f50dd](https://github.com/websockets-rs/rust-websocket/commit/b4f50dd7e18047bd085c8b9585cb6250ee2c7f7d)

Then I missed it :D
> > This is due to sync tungstenite not supporting the .split operation on their sender / receiver stream
> 
> Note that `rust-websocket`s support for splitting sync sockets is only reliable only for non-encrypted `ws://` websockets. Maybe that's why sync `tungstenite` does not expose this function.
> 
> Splitting should work properly for async, including for `wss://`, both in `rust-websocket` and `tokio-tungstenite`.

Yeah that's true, but it still feels like this is taking away potential speed. But we might change this, @nshaaban-cPacket suggested to internally use async to speed things up, that might now be the best solution.
> Yeah that's true, but it still feels like this is taking away potential speed. But we might change this, @nshaaban-cPacket suggested to internally use async to speed things up, that might now be the best solution.

Using async internally has it's drawbacks too, we'd have to use a tokio runner (term might be wrong) internally for method calls, which is far from ideal, not to mention also has a performance hit. We may be able to support roughly the same API in asnyc (as most of it is callback based anyways), would require more thought. 
There is also `async-tungstenite` [here](https://github.com/sdroege/async-tungstenite), which is not bound to any specific async runtime.

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2053fe93..fc1353d1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -41,6 +41,17 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "async-trait"
+version = "0.1.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -52,37 +63,12 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "autocfg"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
-
 [[package]]
 name = "autocfg"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
-[[package]]
-name = "base64"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
-dependencies = [
- "byteorder",
- "safemem",
-]
-
-[[package]]
-name = "base64"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
-dependencies = [
- "byteorder",
-]
-
 [[package]]
 name = "base64"
 version = "0.13.0"
@@ -97,25 +83,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "block-buffer"
-version = "0.7.3"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
 dependencies = [
- "block-padding",
- "byte-tools",
- "byteorder",
  "generic-array",
 ]
 
-[[package]]
-name = "block-padding"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
-dependencies = [
- "byte-tools",
-]
-
 [[package]]
 name = "bstr"
 version = "0.2.17"
@@ -140,12 +114,6 @@ version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7cfd4a5b862cb6bfcaf4b9803e887a22393dd5c1ac9e2d1e131d516047eabd01"
 
-[[package]]
-name = "byte-tools"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
-
 [[package]]
 name = "byteorder"
 version = "1.4.3"
@@ -278,15 +246,6 @@ dependencies = [
  "vec_map",
 ]
 
-[[package]]
-name = "cloudabi"
-version = "0.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-dependencies = [
- "bitflags",
-]
-
 [[package]]
 name = "core-foundation"
 version = "0.9.2"
@@ -317,6 +276,15 @@ dependencies = [
  "serde_json",
 ]
 
+[[package]]
+name = "cpufeatures"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "crc32fast"
 version = "1.2.1"
@@ -471,9 +439,9 @@ dependencies = [
 
 [[package]]
 name = "digest"
-version = "0.8.1"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
 dependencies = [
  "generic-array",
 ]
@@ -493,12 +461,6 @@ dependencies = [
  "cfg-if",
 ]
 
-[[package]]
-name = "fake-simd"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
-
 [[package]]
 name = "fallible-iterator"
 version = "0.2.0"
@@ -545,15 +507,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
 dependencies = [
  "matches",
- "percent-encoding 2.1.0",
+ "percent-encoding",
 ]
 
-[[package]]
-name = "fuchsia-cprng"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
-
 [[package]]
 name = "futures-channel"
 version = "0.3.17"
@@ -565,37 +521,37 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
+checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
 
 [[package]]
 name = "futures-io"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
+checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
 
 [[package]]
 name = "futures-sink"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
+checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
 
 [[package]]
 name = "futures-task"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
+checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
 
 [[package]]
 name = "futures-util"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
+checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
 dependencies = [
- "autocfg 1.0.1",
  "futures-core",
  "futures-io",
+ "futures-sink",
  "futures-task",
  "memchr",
  "pin-project-lite",
@@ -605,11 +561,12 @@ dependencies = [
 
 [[package]]
 name = "generic-array"
-version = "0.12.4"
+version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
 dependencies = [
  "typenum",
+ "version_check",
 ]
 
 [[package]]
@@ -643,10 +600,10 @@ dependencies = [
  "bitflags",
  "libc",
  "libgit2-sys",
- "log 0.4.14",
+ "log",
  "openssl-probe",
  "openssl-sys",
- "url 2.2.2",
+ "url",
 ]
 
 [[package]]
@@ -660,9 +617,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.3.6"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964"
+checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689"
 dependencies = [
  "bytes",
  "fnv",
@@ -756,28 +713,9 @@ dependencies = [
 
 [[package]]
 name = "hyper"
-version = "0.10.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
-dependencies = [
- "base64 0.9.3",
- "httparse",
- "language-tags",
- "log 0.3.9",
- "mime 0.2.6",
- "num_cpus",
- "time",
- "traitobject",
- "typeable",
- "unicase",
- "url 1.7.2",
-]
-
-[[package]]
-name = "hyper"
-version = "0.14.13"
+version = "0.14.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593"
+checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -804,23 +742,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
 dependencies = [
  "bytes",
- "hyper 0.14.13",
+ "hyper",
  "native-tls",
  "tokio",
  "tokio-native-tls",
 ]
 
-[[package]]
-name = "idna"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
-dependencies = [
- "matches",
- "unicode-bidi",
- "unicode-normalization",
-]
-
 [[package]]
 name = "idna"
 version = "0.2.3"
@@ -838,7 +765,7 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
  "hashbrown",
  "serde",
 ]
@@ -888,12 +815,6 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "language-tags"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
-
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -946,15 +867,6 @@ dependencies = [
  "vcpkg",
 ]
 
-[[package]]
-name = "log"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
-dependencies = [
- "log 0.4.14",
-]
-
 [[package]]
 name = "log"
 version = "0.4.14"
@@ -1007,16 +919,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
 dependencies = [
- "autocfg 1.0.1",
-]
-
-[[package]]
-name = "mime"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
-dependencies = [
- "log 0.3.9",
+ "autocfg",
 ]
 
 [[package]]
@@ -1032,7 +935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
 dependencies = [
  "adler",
- "autocfg 1.0.1",
+ "autocfg",
 ]
 
 [[package]]
@@ -1042,7 +945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
 dependencies = [
  "libc",
- "log 0.4.14",
+ "log",
  "miow",
  "ntapi",
  "winapi",
@@ -1065,7 +968,7 @@ checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
 dependencies = [
  "lazy_static",
  "libc",
- "log 0.4.14",
+ "log",
  "openssl",
  "openssl-probe",
  "openssl-sys",
@@ -1103,7 +1006,7 @@ version = "0.1.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
  "num-traits",
 ]
 
@@ -1113,7 +1016,7 @@ version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
 ]
 
 [[package]]
@@ -1149,9 +1052,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
 
 [[package]]
 name = "opaque-debug"
-version = "0.2.3"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
 name = "openssl"
@@ -1179,19 +1082,13 @@ version = "0.9.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
  "cc",
  "libc",
  "pkg-config",
  "vcpkg",
 ]
 
-[[package]]
-name = "percent-encoding"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
-
 [[package]]
 name = "percent-encoding"
 version = "2.1.0"
@@ -1292,25 +1189,6 @@ dependencies = [
  "proc-macro2",
 ]
 
-[[package]]
-name = "rand"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
-dependencies = [
- "autocfg 0.1.7",
- "libc",
- "rand_chacha 0.1.1",
- "rand_core 0.4.2",
- "rand_hc 0.1.0",
- "rand_isaac",
- "rand_jitter",
- "rand_os",
- "rand_pcg",
- "rand_xorshift",
- "winapi",
-]
-
 [[package]]
 name = "rand"
 version = "0.8.4"
@@ -1318,19 +1196,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
 dependencies = [
  "libc",
- "rand_chacha 0.3.1",
- "rand_core 0.6.3",
- "rand_hc 0.3.1",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
-dependencies = [
- "autocfg 0.1.7",
- "rand_core 0.3.1",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
 ]
 
 [[package]]
@@ -1340,24 +1208,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.3",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
-dependencies = [
- "rand_core 0.4.2",
+ "rand_core",
 ]
 
-[[package]]
-name = "rand_core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
-
 [[package]]
 name = "rand_core"
 version = "0.6.3"
@@ -1367,75 +1220,13 @@ dependencies = [
  "getrandom",
 ]
 
-[[package]]
-name = "rand_hc"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
-dependencies = [
- "rand_core 0.3.1",
-]
-
 [[package]]
 name = "rand_hc"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
 dependencies = [
- "rand_core 0.6.3",
-]
-
-[[package]]
-name = "rand_isaac"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
-dependencies = [
- "rand_core 0.3.1",
-]
-
-[[package]]
-name = "rand_jitter"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
-dependencies = [
- "libc",
- "rand_core 0.4.2",
- "winapi",
-]
-
-[[package]]
-name = "rand_os"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
-dependencies = [
- "cloudabi",
- "fuchsia-cprng",
- "libc",
- "rand_core 0.4.2",
- "rdrand",
- "winapi",
-]
-
-[[package]]
-name = "rand_pcg"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
-dependencies = [
- "autocfg 0.1.7",
- "rand_core 0.4.2",
-]
-
-[[package]]
-name = "rand_xorshift"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
-dependencies = [
- "rand_core 0.3.1",
+ "rand_core",
 ]
 
 [[package]]
@@ -1444,7 +1235,7 @@ version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
  "crossbeam-deque",
  "either",
  "rayon-core",
@@ -1463,15 +1254,6 @@ dependencies = [
  "num_cpus",
 ]
 
-[[package]]
-name = "rdrand"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
-dependencies = [
- "rand_core 0.3.1",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.2.10"
@@ -1522,29 +1304,29 @@ version = "0.11.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c4e0a76dc12a116108933f6301b95e83634e0c47b0afbed6abbaa0601e99258"
 dependencies = [
- "base64 0.13.0",
+ "base64",
  "bytes",
  "encoding_rs",
  "futures-core",
  "futures-util",
  "http",
  "http-body",
- "hyper 0.14.13",
+ "hyper",
  "hyper-tls",
  "ipnet",
  "js-sys",
  "lazy_static",
- "log 0.4.14",
- "mime 0.3.16",
+ "log",
+ "mime",
  "native-tls",
- "percent-encoding 2.1.0",
+ "percent-encoding",
  "pin-project-lite",
  "serde",
  "serde_json",
  "serde_urlencoded",
  "tokio",
  "tokio-native-tls",
- "url 2.2.2",
+ "url",
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
@@ -1556,18 +1338,23 @@ name = "rust_engineio"
 version = "0.3.0"
 dependencies = [
  "adler32",
- "base64 0.13.0",
+ "async-trait",
+ "base64",
  "bytes",
  "cargo-tarpaulin",
  "criterion",
  "crossbeam-utils",
+ "futures-util",
+ "http",
  "native-tls",
  "reqwest",
  "serde",
  "serde_json",
  "thiserror",
- "url 2.2.2",
- "websocket",
+ "tokio",
+ "tokio-tungstenite",
+ "tungstenite",
+ "url",
 ]
 
 [[package]]
@@ -1575,18 +1362,18 @@ name = "rust_socketio"
 version = "0.3.0"
 dependencies = [
  "adler32",
- "base64 0.13.0",
+ "base64",
  "byte",
  "bytes",
  "cargo-tarpaulin",
  "crossbeam-utils",
  "native-tls",
- "rand 0.8.4",
+ "rand",
  "regex",
  "rust_engineio",
  "serde_json",
  "thiserror",
- "url 2.2.2",
+ "url",
 ]
 
 [[package]]
@@ -1610,12 +1397,6 @@ version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 
-[[package]]
-name = "safemem"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
-
 [[package]]
 name = "same-file"
 version = "1.0.6"
@@ -1728,13 +1509,14 @@ dependencies = [
 
 [[package]]
 name = "sha-1"
-version = "0.8.2"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
 dependencies = [
  "block-buffer",
+ "cfg-if",
+ "cpufeatures",
  "digest",
- "fake-simd",
  "opaque-debug",
 ]
 
@@ -1800,7 +1582,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
 dependencies = [
  "cfg-if",
  "libc",
- "rand 0.8.4",
+ "rand",
  "redox_syscall",
  "remove_dir_all",
  "winapi",
@@ -1885,7 +1667,7 @@ version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
  "bytes",
  "libc",
  "memchr",
@@ -1905,6 +1687,20 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "tokio-tungstenite"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e80b39df6afcc12cdf752398ade96a6b9e99c903dfdc36e53ad10b9c366bca72"
+dependencies = [
+ "futures-util",
+ "log",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tungstenite",
+]
+
 [[package]]
 name = "tokio-util"
 version = "0.6.8"
@@ -1914,7 +1710,7 @@ dependencies = [
  "bytes",
  "futures-core",
  "futures-sink",
- "log 0.4.14",
+ "log",
  "pin-project-lite",
  "tokio",
 ]
@@ -1961,7 +1757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
 dependencies = [
  "lazy_static",
- "log 0.4.14",
+ "log",
  "tracing-core",
 ]
 
@@ -1984,12 +1780,6 @@ dependencies = [
  "tracing-log",
 ]
 
-[[package]]
-name = "traitobject"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
-
 [[package]]
 name = "try-lock"
 version = "0.2.3"
@@ -1997,10 +1787,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
 [[package]]
-name = "typeable"
-version = "0.1.2"
+name = "tungstenite"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
+checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1"
+dependencies = [
+ "base64",
+ "byteorder",
+ "bytes",
+ "http",
+ "httparse",
+ "log",
+ "native-tls",
+ "rand",
+ "sha-1",
+ "thiserror",
+ "url",
+ "utf-8",
+]
 
 [[package]]
 name = "typenum"
@@ -2008,15 +1812,6 @@ version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
 
-[[package]]
-name = "unicase"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
-dependencies = [
- "version_check",
-]
-
 [[package]]
 name = "unicode-bidi"
 version = "0.3.7"
@@ -2044,17 +1839,6 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
 
-[[package]]
-name = "url"
-version = "1.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
-dependencies = [
- "idna 0.1.5",
- "matches",
- "percent-encoding 1.0.1",
-]
-
 [[package]]
 name = "url"
 version = "2.2.2"
@@ -2062,11 +1846,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
 dependencies = [
  "form_urlencoded",
- "idna 0.2.3",
+ "idna",
  "matches",
- "percent-encoding 2.1.0",
+ "percent-encoding",
 ]
 
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
@@ -2081,9 +1871,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
 name = "version_check"
-version = "0.1.5"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "walkdir"
@@ -2102,7 +1892,7 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
 dependencies = [
- "log 0.4.14",
+ "log",
  "try-lock",
 ]
 
@@ -2130,7 +1920,7 @@ checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
 dependencies = [
  "bumpalo",
  "lazy_static",
- "log 0.4.14",
+ "log",
  "proc-macro2",
  "quote",
  "syn",
@@ -2188,34 +1978,6 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "websocket"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "723abe6b75286edc51d8ecabb38a2353f62a9e9b0588998b59111474f1dcd637"
-dependencies = [
- "hyper 0.10.16",
- "native-tls",
- "rand 0.6.5",
- "unicase",
- "url 1.7.2",
- "websocket-base",
-]
-
-[[package]]
-name = "websocket-base"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "403f3fd505ff930da84156389639932955fb09705b3dccd1a3d60c8e7ff62776"
-dependencies = [
- "base64 0.10.1",
- "bitflags",
- "byteorder",
- "native-tls",
- "rand 0.6.5",
- "sha-1",
-]
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
diff --git a/engineio/Cargo.toml b/engineio/Cargo.toml
index 2c9020d4..2ebbc3ca 100644
--- a/engineio/Cargo.toml
+++ b/engineio/Cargo.toml
@@ -18,7 +18,12 @@ reqwest = { version = "0.11.8", features = ["blocking", "native-tls"] }
 adler32 = "1.2.0"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
-websocket = { version = "0.26.2", features = ["sync-ssl"], default-features = false }
+http = "0.2.5"
+tokio-tungstenite = { version = "0.16.1", features = ["native-tls"] }
+tungstenite = "0.16.0"
+tokio = "1.0.0"
+futures-util = { version = "0.3", default-features = false, features = ["sink"] }
+async-trait = "0.1.51"
 thiserror = "1.0"
 native-tls = "0.2.7"
 url = "2.2.2"
diff --git a/engineio/src/async_transports/mod.rs b/engineio/src/async_transports/mod.rs
new file mode 100644
index 00000000..015e8748
--- /dev/null
+++ b/engineio/src/async_transports/mod.rs
@@ -0,0 +1,95 @@
+use std::{borrow::Cow, str::from_utf8, sync::Arc};
+
+use crate::{error::Result, Error, Packet, PacketId};
+use bytes::{BufMut, Bytes, BytesMut};
+use futures_util::{
+    stream::{SplitSink, SplitStream},
+    SinkExt, StreamExt,
+};
+use tokio::{net::TcpStream, sync::Mutex};
+use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
+use tungstenite::Message;
+
+pub(crate) mod transport;
+pub(crate) mod websocket;
+pub(crate) mod websocket_secure;
+
+/// A general purpose asynchronous websocket transport type. Holds
+/// the sender and receiver stream of a websocket connection
+/// and implements the common methods `update`, `poll` and `emit`.
+pub(crate) struct AsyncWebsocketGeneralTransport {
+    sender: Arc<Mutex<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>>,
+    receiver: Arc<Mutex<SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>>>,
+}
+
+impl AsyncWebsocketGeneralTransport {
+    pub(crate) async fn new(
+        sender: SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>,
+        receiver: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
+    ) -> Self {
+        AsyncWebsocketGeneralTransport {
+            sender: Arc::new(Mutex::new(sender)),
+            receiver: Arc::new(Mutex::new(receiver)),
+        }
+    }
+
+    /// Sends probe packet to ensure connection is valid, then sends upgrade
+    /// request
+    pub(crate) async fn upgrade(&self) -> Result<()> {
+        let mut receiver = self.receiver.lock().await;
+        let mut sender = self.sender.lock().await;
+
+        sender
+            .send(Message::text(Cow::Borrowed(from_utf8(&Bytes::from(
+                Packet::new(PacketId::Ping, Bytes::from("probe")),
+            ))?)))
+            .await?;
+
+        let msg = receiver
+            .next()
+            .await
+            .ok_or(Error::IllegalWebsocketUpgrade())??;
+
+        if msg.into_data() != Bytes::from(Packet::new(PacketId::Pong, Bytes::from("probe"))) {
+            return Err(Error::InvalidPacket());
+        }
+
+        sender
+            .send(Message::text(Cow::Borrowed(from_utf8(&Bytes::from(
+                Packet::new(PacketId::Upgrade, Bytes::from("")),
+            ))?)))
+            .await?;
+
+        Ok(())
+    }
+
+    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {
+        let mut sender = self.sender.lock().await;
+
+        let message = if is_binary_att {
+            Message::binary(Cow::Borrowed(data.as_ref()))
+        } else {
+            Message::text(Cow::Borrowed(std::str::from_utf8(data.as_ref())?))
+        };
+
+        sender.send(message).await?;
+
+        Ok(())
+    }
+
+    async fn poll(&self) -> Result<Bytes> {
+        let mut receiver = self.receiver.lock().await;
+
+        let message = receiver.next().await.ok_or(Error::IncompletePacket())??;
+        if message.is_binary() {
+            let data = message.into_data();
+            let mut msg = BytesMut::with_capacity(data.len() + 1);
+            msg.put_u8(PacketId::Message as u8);
+            msg.put(data.as_ref());
+
+            Ok(msg.freeze())
+        } else {
+            Ok(Bytes::from(message.into_data()))
+        }
+    }
+}
diff --git a/engineio/src/async_transports/transport.rs b/engineio/src/async_transports/transport.rs
new file mode 100644
index 00000000..f826d3cd
--- /dev/null
+++ b/engineio/src/async_transports/transport.rs
@@ -0,0 +1,34 @@
+use crate::error::Result;
+use adler32::adler32;
+use async_trait::async_trait;
+use bytes::Bytes;
+use std::time::SystemTime;
+use url::Url;
+
+#[async_trait]
+pub(crate) trait AsyncTransport {
+    /// Sends a packet to the server. This optionally handles sending of a
+    /// socketio binary attachment via the boolean attribute `is_binary_att`.
+    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()>;
+
+    /// Performs the server long polling procedure as long as the client is
+    /// connected. This should run separately at all time to ensure proper
+    /// response handling from the server.
+    async fn poll(&self) -> Result<Bytes>;
+
+    /// Returns start of the url. ex. http://localhost:2998/engine.io/?EIO=4&transport=polling
+    /// Must have EIO and transport already set.
+    async fn base_url(&self) -> Result<Url>;
+
+    /// Used to update the base path, like when adding the sid.
+    async fn set_base_url(&self, base_url: Url) -> Result<()>;
+
+    /// Full query address
+    async fn address(&self) -> Result<Url> {
+        let reader = format!("{:#?}", SystemTime::now());
+        let hash = adler32(reader.as_bytes()).unwrap();
+        let mut url = self.base_url().await?;
+        url.query_pairs_mut().append_pair("t", &hash.to_string());
+        Ok(url)
+    }
+}
diff --git a/engineio/src/async_transports/websocket.rs b/engineio/src/async_transports/websocket.rs
new file mode 100644
index 00000000..0616ebf7
--- /dev/null
+++ b/engineio/src/async_transports/websocket.rs
@@ -0,0 +1,68 @@
+use std::sync::Arc;
+
+use crate::error::Result;
+use async_trait::async_trait;
+use bytes::Bytes;
+use futures_util::stream::StreamExt;
+use tokio::sync::RwLock;
+use tokio_tungstenite::connect_async;
+use url::Url;
+
+use super::transport::AsyncTransport;
+use super::AsyncWebsocketGeneralTransport;
+
+/// An asynchronous websocket transport type.
+/// This type only allows for plain websocket
+/// connections ("ws://").
+pub(crate) struct AsyncWebsocketTransport {
+    inner: AsyncWebsocketGeneralTransport,
+    base_url: Arc<RwLock<Url>>,
+}
+
+impl AsyncWebsocketTransport {
+    /// Creates a new instance over a request that might hold additional headers and an URL.
+    pub async fn new(request: http::request::Request<()>, url: Url) -> Result<Self> {
+        let (ws_stream, _) = connect_async(request).await?;
+        let (sen, rec) = ws_stream.split();
+
+        let inner = AsyncWebsocketGeneralTransport::new(sen, rec).await;
+        Ok(AsyncWebsocketTransport {
+            inner,
+            base_url: Arc::new(RwLock::new(url)),
+        })
+    }
+
+    /// Sends probe packet to ensure connection is valid, then sends upgrade
+    /// request
+    pub(crate) async fn upgrade(&self) -> Result<()> {
+        self.inner.upgrade().await
+    }
+}
+
+#[async_trait]
+impl AsyncTransport for AsyncWebsocketTransport {
+    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {
+        self.inner.emit(data, is_binary_att).await
+    }
+
+    async fn poll(&self) -> Result<Bytes> {
+        self.inner.poll().await
+    }
+
+    async fn base_url(&self) -> Result<Url> {
+        Ok(self.base_url.read().await.clone())
+    }
+
+    async fn set_base_url(&self, base_url: Url) -> Result<()> {
+        let mut url = base_url;
+        if !url
+            .query_pairs()
+            .any(|(k, v)| k == "transport" && v == "websocket")
+        {
+            url.query_pairs_mut().append_pair("transport", "websocket");
+        }
+        url.set_scheme("ws").unwrap();
+        *self.base_url.write().await = url;
+        Ok(())
+    }
+}
diff --git a/engineio/src/async_transports/websocket_secure.rs b/engineio/src/async_transports/websocket_secure.rs
new file mode 100644
index 00000000..f8df79ad
--- /dev/null
+++ b/engineio/src/async_transports/websocket_secure.rs
@@ -0,0 +1,78 @@
+use std::sync::Arc;
+
+use crate::error::Result;
+use async_trait::async_trait;
+use bytes::Bytes;
+use futures_util::StreamExt;
+use native_tls::TlsConnector;
+use tokio::sync::RwLock;
+use tokio_tungstenite::connect_async_tls_with_config;
+use tokio_tungstenite::Connector;
+use url::Url;
+
+use super::transport::AsyncTransport;
+use super::AsyncWebsocketGeneralTransport;
+
+/// An asynchronous websocket transport type.
+/// This type only allows for secure websocket
+/// connections ("wss://").
+pub(crate) struct AsyncWebsocketSecureTransport {
+    inner: AsyncWebsocketGeneralTransport,
+    base_url: Arc<RwLock<url::Url>>,
+}
+
+impl AsyncWebsocketSecureTransport {
+    /// Creates a new instance over a request that might hold additional headers, a possible
+    /// Tls connector and an URL.
+    pub(crate) async fn new(
+        request: http::request::Request<()>,
+        base_url: url::Url,
+        tls_config: Option<TlsConnector>,
+    ) -> Result<Self> {
+        let (ws_stream, _) =
+            connect_async_tls_with_config(request, None, tls_config.map(Connector::NativeTls))
+                .await?;
+
+        let (sen, rec) = ws_stream.split();
+        let inner = AsyncWebsocketGeneralTransport::new(sen, rec).await;
+
+        Ok(AsyncWebsocketSecureTransport {
+            inner,
+            base_url: Arc::new(RwLock::new(base_url)),
+        })
+    }
+
+    /// Sends probe packet to ensure connection is valid, then sends upgrade
+    /// request
+    pub(crate) async fn upgrade(&self) -> Result<()> {
+        self.inner.upgrade().await
+    }
+}
+
+#[async_trait]
+impl AsyncTransport for AsyncWebsocketSecureTransport {
+    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {
+        self.inner.emit(data, is_binary_att).await
+    }
+
+    async fn poll(&self) -> Result<Bytes> {
+        self.inner.poll().await
+    }
+
+    async fn base_url(&self) -> Result<Url> {
+        Ok(self.base_url.read().await.clone())
+    }
+
+    async fn set_base_url(&self, base_url: Url) -> Result<()> {
+        let mut url = base_url;
+        if !url
+            .query_pairs()
+            .any(|(k, v)| k == "transport" && v == "websocket")
+        {
+            url.query_pairs_mut().append_pair("transport", "websocket");
+        }
+        url.set_scheme("wss").unwrap();
+        *self.base_url.write().await = url;
+        Ok(())
+    }
+}
diff --git a/engineio/src/client/client.rs b/engineio/src/client/client.rs
index 3db41b77..006a2f31 100644
--- a/engineio/src/client/client.rs
+++ b/engineio/src/client/client.rs
@@ -198,16 +198,17 @@ impl ClientBuilder {
     /// Build socket with only a websocket transport
     pub fn build_websocket(mut self) -> Result<Client> {
         // SAFETY: Already a Url
-        let url = websocket::client::Url::parse(&self.url.to_string())?;
+        let url = url::Url::parse(&self.url.to_string())?;
+
+        let headers: Option<http::HeaderMap> = if let Some(map) = self.headers.clone() {
+            Some(map.try_into()?)
+        } else {
+            None
+        };
 
         match url.scheme() {
             "http" | "ws" => {
-                let transport = WebsocketTransport::new(
-                    url,
-                    self.headers
-                        .clone()
-                        .map(|headers| headers.try_into().unwrap()),
-                )?;
+                let transport = WebsocketTransport::new(url, headers)?;
                 if self.handshake.is_some() {
                     transport.upgrade()?;
                 } else {
@@ -228,11 +229,8 @@ impl ClientBuilder {
                 })
             }
             "https" | "wss" => {
-                let transport = WebsocketSecureTransport::new(
-                    url,
-                    self.tls_config.clone(),
-                    self.headers.clone().map(|v| v.try_into().unwrap()),
-                )?;
+                let transport =
+                    WebsocketSecureTransport::new(url, self.tls_config.clone(), headers)?;
                 if self.handshake.is_some() {
                     transport.upgrade()?;
                 } else {
diff --git a/engineio/src/error.rs b/engineio/src/error.rs
index c5e9d560..cc21401e 100644
--- a/engineio/src/error.rs
+++ b/engineio/src/error.rs
@@ -4,8 +4,8 @@ use serde_json::Error as JsonError;
 use std::io::Error as IoError;
 use std::str::Utf8Error;
 use thiserror::Error;
+use tungstenite::Error as TungsteniteError;
 use url::ParseError as UrlParseError;
-use websocket::{client::ParseError, WebSocketError};
 
 /// Enumeration of all possible errors in the `socket.io` context.
 #[derive(Error, Debug)]
@@ -30,20 +30,20 @@ pub enum Error {
     InvalidUrlScheme(String),
     #[error("Error during connection via http: {0}")]
     IncompleteResponseFromReqwest(#[from] ReqwestError),
+    #[error("Error with websocket connection: {0}")]
+    WebsocketError(#[from] TungsteniteError),
     #[error("Network request returned with status code: {0}")]
     IncompleteHttp(u16),
     #[error("Got illegal handshake response: {0}")]
     InvalidHandshake(String),
     #[error("Called an action before the connection was established")]
     IllegalActionBeforeOpen(),
+    #[error("Error setting up the http request: {0}")]
+    InvalidHttpConfiguration(#[from] http::Error),
     #[error("string is not json serializable: {0}")]
     InvalidJson(#[from] JsonError),
     #[error("A lock was poisoned")]
     InvalidPoisonedLock(),
-    #[error("Got a websocket error: {0}")]
-    IncompleteResponseFromWebsocket(#[from] WebSocketError),
-    #[error("Error while parsing the url for the websocket connection: {0}")]
-    InvalidWebsocketURL(#[from] ParseError),
     #[error("Got an IO-Error: {0}")]
     IncompleteIo(#[from] IoError),
     #[error("Server did not allow upgrading to websockets")]
diff --git a/engineio/src/header.rs b/engineio/src/header.rs
index 1a538b2d..64d3a86d 100644
--- a/engineio/src/header.rs
+++ b/engineio/src/header.rs
@@ -1,13 +1,12 @@
 use crate::Error;
 use bytes::Bytes;
-use reqwest::header::{
-    HeaderMap as ReqwestHeaderMap, HeaderName as ReqwestHeaderName,
-    HeaderValue as ReqwestHeaderValue,
+use http::{
+    header::HeaderName as HttpHeaderName, HeaderMap as HttpHeaderMap,
+    HeaderValue as HttpHeaderValue,
 };
 use std::collections::HashMap;
 use std::convert::TryFrom;
 use std::str::FromStr;
-use websocket::header::Headers as WebSocketHeaderMap;
 
 #[derive(Eq, PartialEq, Hash, Debug, Clone)]
 pub struct HeaderName {
@@ -40,17 +39,17 @@ impl From<String> for HeaderName {
     }
 }
 
-impl TryFrom<HeaderName> for ReqwestHeaderName {
+impl TryFrom<HeaderName> for HttpHeaderName {
     type Error = Error;
     fn try_from(
         header: HeaderName,
     ) -> std::result::Result<Self, <Self as std::convert::TryFrom<HeaderName>>::Error> {
-        Ok(ReqwestHeaderName::from_str(&header.to_string())?)
+        Ok(HttpHeaderName::from_str(&header.to_string())?)
     }
 }
 
-impl From<ReqwestHeaderName> for HeaderName {
-    fn from(header: ReqwestHeaderName) -> Self {
+impl From<HttpHeaderName> for HeaderName {
+    fn from(header: HttpHeaderName) -> Self {
         HeaderName::from(header.to_string())
     }
 }
@@ -63,17 +62,17 @@ impl From<String> for HeaderValue {
     }
 }
 
-impl TryFrom<HeaderValue> for ReqwestHeaderValue {
+impl TryFrom<HeaderValue> for HttpHeaderValue {
     type Error = Error;
     fn try_from(
         value: HeaderValue,
     ) -> std::result::Result<Self, <Self as std::convert::TryFrom<HeaderValue>>::Error> {
-        Ok(ReqwestHeaderValue::from_bytes(&value.inner[..])?)
+        Ok(HttpHeaderValue::from_bytes(&value.inner[..])?)
     }
 }
 
-impl From<ReqwestHeaderValue> for HeaderValue {
-    fn from(value: ReqwestHeaderValue) -> Self {
+impl From<HttpHeaderValue> for HeaderValue {
+    fn from(value: HttpHeaderValue) -> Self {
         HeaderValue {
             inner: Bytes::copy_from_slice(value.as_bytes()),
         }
@@ -86,30 +85,20 @@ impl From<&str> for HeaderValue {
     }
 }
 
-impl TryFrom<HeaderMap> for ReqwestHeaderMap {
+impl TryFrom<HeaderMap> for HttpHeaderMap {
     type Error = Error;
     fn try_from(
         headers: HeaderMap,
     ) -> std::result::Result<Self, <Self as std::convert::TryFrom<HeaderMap>>::Error> {
-        Ok(headers
-            .into_iter()
-            .map(|(k, v)| {
-                (
-                    ReqwestHeaderName::try_from(k).unwrap(),
-                    ReqwestHeaderValue::try_from(v).unwrap(),
-                )
-            })
-            .collect())
-    }
-}
-
-impl From<HeaderMap> for WebSocketHeaderMap {
-    fn from(headers: HeaderMap) -> Self {
-        let mut output = WebSocketHeaderMap::new();
-        for (key, val) in headers {
-            output.append_raw(key.to_string(), val.inner[..].to_vec());
+        let mut result = HttpHeaderMap::new();
+        for (key, value) in headers {
+            result.append(
+                HttpHeaderName::try_from(key)?,
+                HttpHeaderValue::try_from(value)?,
+            );
         }
-        output
+
+        Ok(result)
     }
 }
 
diff --git a/engineio/src/lib.rs b/engineio/src/lib.rs
index 2325bb2e..edc73925 100644
--- a/engineio/src/lib.rs
+++ b/engineio/src/lib.rs
@@ -66,6 +66,7 @@ macro_rules! spawn_scoped {
         .unwrap();
     };
 }
+pub mod async_transports;
 mod callback;
 pub mod client;
 /// Generic header map
diff --git a/engineio/src/transports/websocket.rs b/engineio/src/transports/websocket.rs
index c2fdce08..f26b4375 100644
--- a/engineio/src/transports/websocket.rs
+++ b/engineio/src/transports/websocket.rs
@@ -1,122 +1,67 @@
-use crate::error::{Error, Result};
-use crate::packet::Packet;
-use crate::packet::PacketId;
-use crate::transport::Transport;
-use bytes::{BufMut, Bytes, BytesMut};
-use std::borrow::Cow;
-use std::str::from_utf8;
-use std::sync::{Arc, Mutex, RwLock};
-use websocket::{
-    client::Url, dataframe::Opcode, header::Headers, receiver::Reader, sync::stream::TcpStream,
-    sync::Writer, ws::dataframe::DataFrame, ClientBuilder as WsClientBuilder, Message,
+use crate::{
+    async_transports::{transport::AsyncTransport, websocket::AsyncWebsocketTransport},
+    error::Result,
+    transport::Transport,
 };
+use bytes::Bytes;
+use http::HeaderMap;
+use std::sync::Arc;
+use tokio::runtime::Runtime;
+use url::Url;
 
 #[derive(Clone)]
 pub struct WebsocketTransport {
-    sender: Arc<Mutex<Writer<TcpStream>>>,
-    receiver: Arc<Mutex<Reader<TcpStream>>>,
-    base_url: Arc<RwLock<url::Url>>,
+    runtime: Arc<Runtime>,
+    inner: Arc<AsyncWebsocketTransport>,
 }
 
 impl WebsocketTransport {
     /// Creates an instance of `WebsocketTransport`.
-    pub fn new(base_url: Url, headers: Option<Headers>) -> Result<Self> {
+    pub fn new(base_url: Url, headers: Option<HeaderMap>) -> Result<Self> {
+        let runtime = tokio::runtime::Builder::new_current_thread()
+            .enable_all()
+            .build()?;
+
         let mut url = base_url;
         url.query_pairs_mut().append_pair("transport", "websocket");
         url.set_scheme("ws").unwrap();
-        let mut client_builder = WsClientBuilder::new(url[..].as_ref())?;
-        if let Some(headers) = headers {
-            client_builder = client_builder.custom_headers(&headers);
-        }
-        let client = client_builder.connect_insecure()?;
 
-        client.set_nonblocking(false)?;
+        let mut req = http::Request::builder().uri(url.clone().as_str());
+        if let Some(map) = headers {
+            // SAFETY: this unwrap never panics as the underlying request is just initialized and in proper state
+            req.headers_mut().unwrap().extend(map);
+        }
 
-        let (receiver, sender) = client.split()?;
+        let inner = runtime.block_on(AsyncWebsocketTransport::new(req.body(())?, url))?;
 
         Ok(WebsocketTransport {
-            sender: Arc::new(Mutex::new(sender)),
-            receiver: Arc::new(Mutex::new(receiver)),
-            // SAFETY: already a URL parsing can't fail
-            base_url: Arc::new(RwLock::new(url::Url::parse(&url.to_string())?)),
+            runtime: Arc::new(runtime),
+            inner: Arc::new(inner),
         })
     }
 
     /// Sends probe packet to ensure connection is valid, then sends upgrade
     /// request
     pub(crate) fn upgrade(&self) -> Result<()> {
-        let mut sender = self.sender.lock()?;
-        let mut receiver = self.receiver.lock()?;
-
-        // send the probe packet, the text `2probe` represents a ping packet with
-        // the content `probe`
-        sender.send_message(&Message::text(Cow::Borrowed(from_utf8(&Bytes::from(
-            Packet::new(PacketId::Ping, Bytes::from("probe")),
-        ))?)))?;
-
-        // expect to receive a probe packet
-        let message = receiver.recv_message()?;
-        if message.take_payload() != Bytes::from(Packet::new(PacketId::Pong, Bytes::from("probe")))
-        {
-            return Err(Error::InvalidPacket());
-        }
-
-        // finally send the upgrade request. the payload `5` stands for an upgrade
-        // packet without any payload
-        sender.send_message(&Message::text(Cow::Borrowed(from_utf8(&Bytes::from(
-            Packet::new(PacketId::Upgrade, Bytes::from("")),
-        ))?)))?;
-
-        Ok(())
+        self.runtime.block_on(self.inner.upgrade())
     }
 }
 
 impl Transport for WebsocketTransport {
     fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {
-        let mut sender = self.sender.lock()?;
-
-        let message = if is_binary_att {
-            Message::binary(Cow::Borrowed(data.as_ref()))
-        } else {
-            Message::text(Cow::Borrowed(std::str::from_utf8(data.as_ref())?))
-        };
-        sender.send_message(&message)?;
-
-        Ok(())
+        self.runtime.block_on(self.inner.emit(data, is_binary_att))
     }
 
     fn poll(&self) -> Result<Bytes> {
-        let mut receiver = self.receiver.lock()?;
-
-        // if this is a binary payload, we mark it as a message
-        let received_df = receiver.recv_dataframe()?;
-        match received_df.opcode {
-            Opcode::Binary => {
-                let mut message = BytesMut::with_capacity(received_df.data.len() + 1);
-                message.put_u8(PacketId::Message as u8);
-                message.put(received_df.take_payload().as_ref());
-
-                Ok(message.freeze())
-            }
-            _ => Ok(Bytes::from(received_df.take_payload())),
-        }
+        self.runtime.block_on(self.inner.poll())
     }
 
     fn base_url(&self) -> Result<url::Url> {
-        Ok(self.base_url.read()?.clone())
+        self.runtime.block_on(self.inner.base_url())
     }
 
     fn set_base_url(&self, url: url::Url) -> Result<()> {
-        let mut url = url;
-        if !url
-            .query_pairs()
-            .any(|(k, v)| k == "transport" && v == "websocket")
-        {
-            url.query_pairs_mut().append_pair("transport", "websocket");
-        }
-        url.set_scheme("ws").unwrap();
-        *self.base_url.write()? = url;
-        Ok(())
+        self.runtime.block_on(self.inner.set_base_url(url))
     }
 }
 
@@ -177,6 +122,8 @@ mod test {
             format!("{:?}", transport),
             format!("WebsocketTransport(base_url: {:?})", transport.base_url())
         );
+        println!("{:?}", transport.poll().unwrap());
+        println!("{:?}", transport.poll().unwrap());
         Ok(())
     }
 }
diff --git a/engineio/src/transports/websocket_secure.rs b/engineio/src/transports/websocket_secure.rs
index 974a97dc..7142d089 100644
--- a/engineio/src/transports/websocket_secure.rs
+++ b/engineio/src/transports/websocket_secure.rs
@@ -1,33 +1,21 @@
-use crate::error::Error;
-use crate::error::Result;
-use crate::packet::Packet;
-use crate::packet::PacketId;
-use crate::transport::Transport;
-use bytes::{BufMut, Bytes, BytesMut};
-use native_tls::TlsConnector;
-use std::borrow::Cow;
-use std::convert::TryFrom;
-use std::io::ErrorKind;
-use std::str::from_utf8;
-use std::sync::{Arc, Mutex, RwLock};
-use std::thread::sleep;
-use std::time::Duration;
-use websocket::WebSocketError;
-use websocket::{
-    client::sync::Client as WsClient,
-    client::Url,
-    dataframe::DataFrame,
-    dataframe::Opcode,
-    header::Headers,
-    sync::stream::{TcpStream, TlsStream},
-    ws::dataframe::DataFrame as DataFrameable,
-    ClientBuilder as WsClientBuilder, Message,
+use crate::{
+    async_transports::{
+        transport::AsyncTransport, websocket_secure::AsyncWebsocketSecureTransport,
+    },
+    error::Result,
+    transport::Transport,
 };
+use bytes::Bytes;
+use http::HeaderMap;
+use native_tls::TlsConnector;
+use std::sync::Arc;
+use tokio::runtime::Runtime;
+use url::Url;
 
 #[derive(Clone)]
 pub struct WebsocketSecureTransport {
-    client: Arc<Mutex<WsClient<TlsStream<TcpStream>>>>,
-    base_url: Arc<RwLock<url::Url>>,
+    runtime: Arc<Runtime>,
+    inner: Arc<AsyncWebsocketSecureTransport>,
 }
 
 impl WebsocketSecureTransport {
@@ -35,125 +23,56 @@ impl WebsocketSecureTransport {
     pub fn new(
         base_url: Url,
         tls_config: Option<TlsConnector>,
-        headers: Option<Headers>,
+        headers: Option<HeaderMap>,
     ) -> Result<Self> {
+        let runtime = tokio::runtime::Builder::new_current_thread()
+            .enable_all()
+            .build()?;
+
         let mut url = base_url;
         url.query_pairs_mut().append_pair("transport", "websocket");
         url.set_scheme("wss").unwrap();
-        let mut client_builder = WsClientBuilder::new(url[..].as_ref())?;
-        if let Some(headers) = headers {
-            client_builder = client_builder.custom_headers(&headers);
+
+        let mut req = http::Request::builder().uri(url.clone().as_str());
+        if let Some(map) = headers {
+            // SAFETY: this unwrap never panics as the underlying request is just initialized and in proper state
+            req.headers_mut().unwrap().extend(map);
         }
-        let client = client_builder.connect_secure(tls_config)?;
 
-        client.set_nonblocking(false)?;
+        let inner = runtime.block_on(AsyncWebsocketSecureTransport::new(
+            req.body(())?,
+            url,
+            tls_config,
+        ))?;
 
         Ok(WebsocketSecureTransport {
-            client: Arc::new(Mutex::new(client)),
-            // SAFETY: already a URL parsing can't fail
-            base_url: Arc::new(RwLock::new(url::Url::parse(&url.to_string())?)),
+            runtime: Arc::new(runtime),
+            inner: Arc::new(inner),
         })
     }
 
     /// Sends probe packet to ensure connection is valid, then sends upgrade
     /// request
     pub(crate) fn upgrade(&self) -> Result<()> {
-        let mut client = self.client.lock()?;
-
-        // send the probe packet, the text `2probe` represents a ping packet with
-        // the content `probe`
-        client.send_message(&Message::text(Cow::Borrowed(from_utf8(&Bytes::from(
-            Packet::new(PacketId::Ping, Bytes::from("probe")),
-        ))?)))?;
-
-        // expect to receive a probe packet
-        let message = client.recv_message()?;
-        let payload = message.take_payload();
-        if Packet::try_from(Bytes::from(payload))?
-            != Packet::new(PacketId::Pong, Bytes::from("probe"))
-        {
-            return Err(Error::InvalidPacket());
-        }
-
-        // finally send the upgrade request. the payload `5` stands for an upgrade
-        // packet without any payload
-        client.send_message(&Message::text(Cow::Borrowed(from_utf8(&Bytes::from(
-            Packet::new(PacketId::Upgrade, Bytes::from("")),
-        ))?)))?;
-
-        Ok(())
+        self.runtime.block_on(self.inner.upgrade())
     }
 }
 
 impl Transport for WebsocketSecureTransport {
     fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {
-        let message = if is_binary_att {
-            Message::binary(Cow::Borrowed(data.as_ref()))
-        } else {
-            Message::text(Cow::Borrowed(std::str::from_utf8(data.as_ref())?))
-        };
-
-        let mut writer = self.client.lock()?;
-        writer.send_message(&message)?;
-        drop(writer);
-
-        Ok(())
+        self.runtime.block_on(self.inner.emit(data, is_binary_att))
     }
 
     fn poll(&self) -> Result<Bytes> {
-        let received_df: DataFrame;
-        loop {
-            let mut receiver = self.client.lock()?;
-            receiver.set_nonblocking(true)?;
-
-            match receiver.recv_dataframe() {
-                Ok(payload) => {
-                    received_df = payload;
-                    break;
-                }
-                // Special case to fix https://github.com/1c3t3a/rust-socketio/issues/133
-                // This error occures when the websocket connection times out on the receive method.
-                // The error kind is platform specific, on Unix systems this errors with `ErrorKind::WouldBlock`,
-                // on Windows with `ErrorKind::TimedOut`.
-                // As a result we're going to release the lock on the client,
-                // so that other threads (especially emit) are able to access the client.
-                Err(WebSocketError::IoError(err))
-                    if matches!(err.kind(), ErrorKind::WouldBlock | ErrorKind::TimedOut) => {}
-                Err(err) => return Err(err.into()),
-            }
-            receiver.set_nonblocking(false)?;
-            drop(receiver);
-            sleep(Duration::from_millis(200));
-        }
-
-        // if this is a binary payload, we mark it as a message
-        match received_df.opcode {
-            Opcode::Binary => {
-                let mut message = BytesMut::with_capacity(received_df.data.len() + 1);
-                message.put_u8(PacketId::Message as u8);
-                message.put(received_df.take_payload().as_ref());
-
-                Ok(message.freeze())
-            }
-            _ => Ok(Bytes::from(received_df.take_payload())),
-        }
+        self.runtime.block_on(self.inner.poll())
     }
 
     fn base_url(&self) -> Result<url::Url> {
-        Ok(self.base_url.read()?.clone())
+        self.runtime.block_on(self.inner.base_url())
     }
 
     fn set_base_url(&self, url: url::Url) -> Result<()> {
-        let mut url = url;
-        if !url
-            .query_pairs()
-            .any(|(k, v)| k == "transport" && v == "websocket")
-        {
-            url.query_pairs_mut().append_pair("transport", "websocket");
-        }
-        url.set_scheme("wss").unwrap();
-        *self.base_url.write()? = url;
-        Ok(())
+        self.runtime.block_on(self.inner.set_base_url(url))
     }
 }
 


Test patch: