Просмотр исходного кода

feat(transport): add websocket transport (#290)

I Putu Ariyasa 2 лет назад
Родитель
Сommit
5946a18370

+ 260 - 62
Cargo.lock

@@ -2,6 +2,15 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
 [[package]]
 name = "adler"
 version = "1.0.2"
@@ -90,7 +99,7 @@ checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -101,7 +110,7 @@ checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -180,6 +189,21 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide 0.7.1",
+ "object",
+ "rustc-demangle",
+]
+
 [[package]]
 name = "base64"
 version = "0.13.1"
@@ -310,7 +334,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -444,6 +468,12 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
 [[package]]
 name = "digest"
 version = "0.10.5"
@@ -478,7 +508,7 @@ checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -518,7 +548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
 dependencies = [
  "crc32fast",
- "miniz_oxide",
+ "miniz_oxide 0.5.4",
 ]
 
 [[package]]
@@ -586,9 +616,9 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.25"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
 
 [[package]]
 name = "futures-io"
@@ -598,32 +628,32 @@ checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.25"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.37",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.25"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
 
 [[package]]
 name = "futures-task"
-version = "0.3.25"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
 
 [[package]]
 name = "futures-util"
-version = "0.3.25"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
 dependencies = [
  "futures-core",
  "futures-macro",
@@ -664,7 +694,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -677,6 +707,12 @@ dependencies = [
  "polyval",
 ]
 
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
 [[package]]
 name = "git2"
 version = "0.14.4"
@@ -797,9 +833,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
-version = "0.14.22"
+version = "0.14.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064"
+checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -812,7 +848,7 @@ dependencies = [
  "httpdate",
  "itoa",
  "pin-project-lite",
- "socket2",
+ "socket2 0.4.7",
  "tokio",
  "tower-service",
  "tracing",
@@ -932,9 +968,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.137"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 
 [[package]]
 name = "libgit2-sys"
@@ -1021,16 +1057,25 @@ dependencies = [
  "adler",
 ]
 
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
 [[package]]
 name = "mio"
-version = "0.8.5"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
+checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
 dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys 0.42.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1117,6 +1162,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "once_cell"
 version = "1.16.0"
@@ -1152,7 +1206,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1257,14 +1311,14 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.9"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
 
 [[package]]
 name = "pin-utils"
@@ -1316,7 +1370,7 @@ dependencies = [
  "proc-macro-error-attr",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
  "version_check",
 ]
 
@@ -1333,9 +1387,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
 dependencies = [
  "unicode-ident",
 ]
@@ -1360,7 +1414,7 @@ dependencies = [
  "itertools",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1375,9 +1429,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.21"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
@@ -1428,6 +1482,8 @@ dependencies = [
  "clap",
  "console-subscriber",
  "fdlimit",
+ "futures-core",
+ "futures-sink",
  "hex",
  "lazy_static",
  "notify",
@@ -1435,9 +1491,11 @@ dependencies = [
  "serde",
  "sha2",
  "snowstorm",
- "socket2",
+ "socket2 0.4.7",
  "tokio",
  "tokio-native-tls",
+ "tokio-tungstenite",
+ "tokio-util",
  "toml",
  "tracing",
  "tracing-subscriber",
@@ -1487,6 +1545,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
 [[package]]
 name = "rustc_version"
 version = "0.4.0"
@@ -1564,22 +1628,22 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
 
 [[package]]
 name = "serde"
-version = "1.0.147"
+version = "1.0.188"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.147"
+version = "1.0.188"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.37",
 ]
 
 [[package]]
@@ -1593,6 +1657,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "sha1"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.6"
@@ -1677,6 +1752,16 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "socket2"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "strsim"
 version = "0.10.0"
@@ -1700,11 +1785,22 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "syn"
+version = "2.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
 [[package]]
 name = "sync_wrapper"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
 
 [[package]]
 name = "tempfile"
@@ -1737,22 +1833,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
 
 [[package]]
 name = "thiserror"
-version = "1.0.37"
+version = "1.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.37"
+version = "1.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.37",
 ]
 
 [[package]]
@@ -1810,23 +1906,22 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.24.2"
+version = "1.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
+checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
 dependencies = [
- "autocfg",
+ "backtrace",
  "bytes",
  "libc",
- "memchr",
  "mio",
  "num_cpus",
  "parking_lot 0.12.1",
  "pin-project-lite",
  "signal-hook-registry",
- "socket2",
+ "socket2 0.5.4",
  "tokio-macros",
  "tracing",
- "windows-sys 0.42.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1841,13 +1936,13 @@ dependencies = [
 
 [[package]]
 name = "tokio-macros"
-version = "1.8.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.37",
 ]
 
 [[package]]
@@ -1871,11 +1966,23 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "tokio-tungstenite"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
+dependencies = [
+ "futures-util",
+ "log",
+ "tokio",
+ "tungstenite",
+]
+
 [[package]]
 name = "tokio-util"
-version = "0.7.4"
+version = "0.7.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
+checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d"
 dependencies = [
  "bytes",
  "futures-core",
@@ -1998,7 +2105,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2057,6 +2164,25 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
+[[package]]
+name = "tungstenite"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
+dependencies = [
+ "byteorder",
+ "bytes",
+ "data-encoding",
+ "http",
+ "httparse",
+ "log",
+ "rand",
+ "sha1",
+ "thiserror",
+ "url",
+ "utf-8",
+]
+
 [[package]]
 name = "typenum"
 version = "1.15.0"
@@ -2106,6 +2232,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
 [[package]]
 name = "valuable"
 version = "0.1.0"
@@ -2217,21 +2349,51 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
 dependencies = [
- "windows_aarch64_gnullvm",
+ "windows_aarch64_gnullvm 0.42.0",
  "windows_aarch64_msvc 0.42.0",
  "windows_i686_gnu 0.42.0",
  "windows_i686_msvc 0.42.0",
  "windows_x86_64_gnu 0.42.0",
- "windows_x86_64_gnullvm",
+ "windows_x86_64_gnullvm 0.42.0",
  "windows_x86_64_msvc 0.42.0",
 ]
 
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "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_aarch64_gnullvm"
 version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.36.1"
@@ -2244,6 +2406,12 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.36.1"
@@ -2256,6 +2424,12 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.36.1"
@@ -2268,6 +2442,12 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.36.1"
@@ -2280,12 +2460,24 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.36.1"
@@ -2298,6 +2490,12 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
 [[package]]
 name = "zeroize"
 version = "1.3.0"

+ 7 - 1
Cargo.toml

@@ -11,7 +11,7 @@ build = "build.rs"
 include = ["src/**/*", "LICENSE", "README.md", "build.rs"]
 
 [features]
-default = ["server", "client", "tls", "noise", "hot-reload"]
+default = ["server", "client", "tls", "noise", "websocket", "hot-reload"]
 
 # Run as a server
 server = []
@@ -21,6 +21,8 @@ client = []
 tls = ["tokio-native-tls"]
 # Noise support
 noise = ["snowstorm", "base64"]
+# Websocket support
+websocket = ["tokio-tungstenite", "tokio-util", "futures-core", "futures-sink", "tls"]
 # Configuration hot-reload support
 hot-reload = ["notify"]
 
@@ -74,6 +76,10 @@ atty = "0.2"
 async-http-proxy = { version = "1.2", features = ["runtime-tokio", "basic-auth"] }
 async-socks5 = "0.5"
 url = { version = "2.2", features = ["serde"] }
+tokio-tungstenite = { version="0.20.1", optional = true}
+tokio-util = { version="0.7.9", optional = true, features = ["io"] }
+futures-core = { version="0.3.28", optional = true  }
+futures-sink = { version="0.3.28", optional = true }
 
 [build-dependencies]
 vergen = { version = "7.4.2", default-features = false, features = ["build", "git", "cargo"] }

+ 7 - 4
Dockerfile

@@ -1,12 +1,15 @@
-FROM rust:alpine as builder
-RUN apk add --no-cache musl-dev openssl openssl-dev pkgconfig
+FROM rust:bookworm as builder
+RUN apt update && apt install -y libssl-dev
 WORKDIR /home/rust/src
 COPY . .
-RUN cargo build --locked --release --features client,server,noise,hot-reload
+ARG FEATURES
+RUN cargo build --locked --release --features ${FEATURES:-default}
 RUN mkdir -p build-out/
 RUN cp target/release/rathole build-out/
 
-FROM scratch
+
+
+FROM gcr.io/distroless/cc-debian12
 WORKDIR /app
 COPY --from=builder /home/rust/src/build-out/rathole .
 USER 1000:1000

+ 16 - 10
README-zh.md

@@ -17,20 +17,20 @@ rathole,类似于 [frp](https://github.com/fatedier/frp) 和 [ngrok](https://g
 <!-- TOC -->
 
 - [rathole](#rathole)
-    - [Features](#features)
-    - [Quickstart](#quickstart)
-    - [Configuration](#configuration)
-        - [Logging](#logging)
-        - [Tuning](#tuning)
-    - [Benchmark](#benchmark)
-    - [Development Status](#development-status)
+  - [Features](#features)
+  - [Quickstart](#quickstart)
+  - [Configuration](#configuration)
+    - [Logging](#logging)
+    - [Tuning](#tuning)
+  - [Benchmark](#benchmark)
+  - [Development Status](#development-status)
 
 <!-- /TOC -->
 
 ## Features
 
-- **高性能** 具有更高的吞吐量,高并发下更稳定。见[Benchmark](#Benchmark)
-- **低资源消耗** 内存占用远低于同类工具。见[Benchmark](#Benchmark)。[二进制文件最小](docs/build-guide.md)可以到 **~500KiB**,可以部署在嵌入式设备如路由器上。
+- **高性能** 具有更高的吞吐量,高并发下更稳定。见[Benchmark](#benchmark)
+- **低资源消耗** 内存占用远低于同类工具。见[Benchmark](#benchmark)。[二进制文件最小](docs/build-guide.md)可以到 **~500KiB**,可以部署在嵌入式设备如路由器上。
 - **安全性** 每个服务单独强制鉴权。Server 和 Client 负责各自的配置。使用 Noise Protocol 可以简单地配置传输加密,而不需要自签证书。同时也支持 TLS。
 - **热重载** 支持配置文件热重载,动态修改端口转发服务。HTTP API 正在开发中。
 
@@ -91,7 +91,7 @@ local_addr = "127.0.0.1:22" # 需要被转发的服务的地址
 
 ## Configuration
 
-如果只有一个 `[server]` 和 `[client]` 块存在的话,`rathole` 可以根据配置文件的内容自动决定在服务器模式或客户端模式下运行,就像 [Quickstart](#Quickstart) 中的例子。
+如果只有一个 `[server]` 和 `[client]` 块存在的话,`rathole` 可以根据配置文件的内容自动决定在服务器模式或客户端模式下运行,就像 [Quickstart](#quickstart) 中的例子。
 
 但 `[client]` 和 `[server]` 块也可以放在一个文件中。然后在服务器端,运行 `rathole --server config.toml`。在客户端,运行 `rathole --client config.toml` 来明确告诉 `rathole` 运行模式。
 
@@ -126,6 +126,9 @@ pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s" # Optional. Default value as shown
 local_private_key = "key_encoded_in_base64" # Optional
 remote_public_key = "key_encoded_in_base64" # Optional
 
+[client.transport.websocket] # Necessary if `type` is "websocket"
+tls = true # If `true` then it will use settings in `client.transport.tls`
+
 [client.services.service1] # A service that needs forwarding. The name `service1` can change arbitrarily, as long as identical to the name in the server's configuration
 type = "tcp" # Optional. The protocol that needs forwarding. Possible values: ["tcp", "udp"]. Default: "tcp"
 token = "whatever" # Necessary if `client.default_token` not set
@@ -158,6 +161,9 @@ pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s"
 local_private_key = "key_encoded_in_base64"
 remote_public_key = "key_encoded_in_base64"
 
+[server.transport.websocket] # Necessary if `type` is "websocket"
+tls = true # If `true` then it will use settings in `server.transport.tls`
+
 [server.services.service1] # The service name must be identical to the client side
 type = "tcp" # Optional. Same as the client `[client.services.X.type]
 token = "whatever" # Necessary if `server.default_token` not set

+ 15 - 9
README.md

@@ -18,20 +18,20 @@ rathole, like [frp](https://github.com/fatedier/frp) and [ngrok](https://github.
 <!-- TOC -->
 
 - [rathole](#rathole)
-    - [Features](#features)
-    - [Quickstart](#quickstart)
-    - [Configuration](#configuration)
-        - [Logging](#logging)
-        - [Tuning](#tuning)
-    - [Benchmark](#benchmark)
+  - [Features](#features)
+  - [Quickstart](#quickstart)
+  - [Configuration](#configuration)
+    - [Logging](#logging)
+    - [Tuning](#tuning)
+  - [Benchmark](#benchmark)
   - [Planning](#planning)
 
 <!-- /TOC -->
 
 ## Features
 
-- **High Performance** Much higher throughput can be achieved than frp, and more stable when handling a large volume of connections. See [Benchmark](#Benchmark)
-- **Low Resource Consumption** Consumes much fewer memory than similar tools. See [Benchmark](#Benchmark). [The binary can be](docs/build-guide.md) **as small as ~500KiB** to fit the constraints of devices, like embedded devices as routers.
+- **High Performance** Much higher throughput can be achieved than frp, and more stable when handling a large volume of connections. See [Benchmark](#benchmark)
+- **Low Resource Consumption** Consumes much fewer memory than similar tools. See [Benchmark](#benchmark). [The binary can be](docs/build-guide.md) **as small as ~500KiB** to fit the constraints of devices, like embedded devices as routers.
 - **Security** Tokens of services are mandatory and service-wise. The server and clients are responsible for their own configs. With the optional Noise Protocol, encryption can be configured at ease. No need to create a self-signed certificate! TLS is also supported.
 - **Hot Reload** Services can be added or removed dynamically by hot-reloading the configuration file. HTTP API is WIP.
 
@@ -93,7 +93,7 @@ To run `rathole` run as a background service on Linux, checkout the [systemd exa
 
 ## Configuration
 
-`rathole` can automatically determine to run in the server mode or the client mode, according to the content of the configuration file, if only one of `[server]` and `[client]` block is present, like the example in [Quickstart](#Quickstart).
+`rathole` can automatically determine to run in the server mode or the client mode, according to the content of the configuration file, if only one of `[server]` and `[client]` block is present, like the example in [Quickstart](#quickstart).
 
 But the `[client]` and `[server]` block can also be put in one file. Then on the server side, run `rathole --server config.toml` and on the client side, run `rathole --client config.toml` to explicitly tell `rathole` the running mode.
 
@@ -128,6 +128,9 @@ pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s" # Optional. Default value as shown
 local_private_key = "key_encoded_in_base64" # Optional
 remote_public_key = "key_encoded_in_base64" # Optional
 
+[client.transport.websocket] # Necessary if `type` is "websocket"
+tls = true # If `true` then it will use settings in `client.transport.tls`
+
 [client.services.service1] # A service that needs forwarding. The name `service1` can change arbitrarily, as long as identical to the name in the server's configuration
 type = "tcp" # Optional. The protocol that needs forwarding. Possible values: ["tcp", "udp"]. Default: "tcp"
 token = "whatever" # Necessary if `client.default_token` not set
@@ -160,6 +163,9 @@ pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s"
 local_private_key = "key_encoded_in_base64"
 remote_public_key = "key_encoded_in_base64"
 
+[server.transport.websocket] # Necessary if `type` is "websocket"
+tls = true # If `true` then it will use settings in `server.transport.tls`
+
 [server.services.service1] # The service name must be identical to the client side
 type = "tcp" # Optional. Same as the client `[client.services.X.type]
 token = "whatever" # Necessary if `server.default_token` not set

+ 11 - 0
src/client.rs

@@ -24,6 +24,8 @@ use tracing::{debug, error, info, instrument, trace, warn, Instrument, Span};
 use crate::transport::NoiseTransport;
 #[cfg(feature = "tls")]
 use crate::transport::TlsTransport;
+#[cfg(feature = "websocket")]
+use crate::transport::WebsocketTransport;
 
 use crate::constants::{run_control_chan_backoff, UDP_BUFFER_SIZE, UDP_SENDQ_SIZE, UDP_TIMEOUT};
 
@@ -62,6 +64,15 @@ pub async fn run_client(
             #[cfg(not(feature = "noise"))]
             crate::helper::feature_not_compile("noise")
         }
+        TransportType::Websocket => {
+            #[cfg(feature = "websocket")]
+            {
+                let mut client = Client::<WebsocketTransport>::from(config).await?;
+                client.run(shutdown_rx, update_rx).await
+            }
+            #[cfg(not(feature = "websocket"))]
+            crate::helper::feature_not_compile("websocket")
+        }
     }
 }
 

+ 11 - 4
src/config.rs

@@ -49,6 +49,8 @@ pub enum TransportType {
     Tls,
     #[serde(rename = "noise")]
     Noise,
+    #[serde(rename = "websocket")]
+    Websocket,
 }
 
 /// Per service config
@@ -75,8 +77,7 @@ impl ClientServiceConfig {
     }
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
-#[derive(Default)]
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
 pub enum ServiceType {
     #[serde(rename = "tcp")]
     #[default]
@@ -85,8 +86,6 @@ pub enum ServiceType {
     Udp,
 }
 
-
-
 fn default_service_type() -> ServiceType {
     Default::default()
 }
@@ -136,6 +135,12 @@ pub struct NoiseConfig {
     // TODO: Maybe psk can be added
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
+pub struct WebsocketConfig {
+    pub tls: bool,
+}
+
 fn default_nodelay() -> bool {
     DEFAULT_NODELAY
 }
@@ -180,6 +185,7 @@ pub struct TransportConfig {
     pub tcp: TcpConfig,
     pub tls: Option<TlsConfig>,
     pub noise: Option<NoiseConfig>,
+    pub websocket: Option<WebsocketConfig>,
 }
 
 fn default_heartbeat_timeout() -> u64 {
@@ -313,6 +319,7 @@ impl Config {
                 // The check is done in transport
                 Ok(())
             }
+            TransportType::Websocket => Ok(()),
         }
     }
 

+ 11 - 0
src/server.rs

@@ -27,6 +27,8 @@ use tracing::{debug, error, info, info_span, instrument, warn, Instrument, Span}
 use crate::transport::NoiseTransport;
 #[cfg(feature = "tls")]
 use crate::transport::TlsTransport;
+#[cfg(feature = "websocket")]
+use crate::transport::WebsocketTransport;
 
 type ServiceDigest = protocol::Digest; // SHA256 of a service name
 type Nonce = protocol::Digest; // Also called `session_key`
@@ -72,6 +74,15 @@ pub async fn run_server(
             #[cfg(not(feature = "noise"))]
             crate::helper::feature_not_compile("noise")
         }
+        TransportType::Websocket => {
+            #[cfg(feature = "websocket")]
+            {
+                let mut server = Server::<WebsocketTransport>::from(config).await?;
+                server.run(shutdown_rx, update_rx).await?;
+            }
+            #[cfg(not(feature = "websocket"))]
+            crate::helper::feature_not_compile("websocket")
+        }
     }
 
     Ok(())

+ 5 - 0
src/transport/mod.rs

@@ -79,6 +79,11 @@ mod noise;
 #[cfg(feature = "noise")]
 pub use noise::NoiseTransport;
 
+#[cfg(feature = "websocket")]
+mod websocket;
+#[cfg(feature = "websocket")]
+pub use websocket::WebsocketTransport;
+
 #[derive(Debug, Clone, Copy)]
 struct Keepalive {
     // tcp_keepalive_time if the underlying protocol is TCP

+ 1 - 1
src/transport/tls.rs

@@ -46,7 +46,7 @@ impl Transport for TlsTransport {
                 // if no trusted_root is specified, allow TlsConnector to use system default
                 let connector = native_tls::TlsConnector::builder().build()?;
                 Some(TlsConnector::from(connector))
-            },
+            }
         };
 
         let tls_acceptor = match config.pkcs12.as_ref() {

+ 250 - 0
src/transport/websocket.rs

@@ -0,0 +1,250 @@
+use core::result::Result;
+use std::io::{Error, ErrorKind};
+use std::net::SocketAddr;
+use std::pin::Pin;
+use std::task::{ready, Context, Poll};
+
+use super::{AddrMaybeCached, SocketOpts, TcpTransport, TlsTransport, Transport};
+use crate::config::TransportConfig;
+use anyhow::anyhow;
+use async_trait::async_trait;
+use bytes::Bytes;
+use futures_core::stream::Stream;
+use futures_sink::Sink;
+use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
+use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
+use tokio_native_tls::TlsStream;
+use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
+use tokio_tungstenite::{accept_async_with_config, client_async_with_config};
+use tokio_tungstenite::{tungstenite::protocol::Message, WebSocketStream};
+use tokio_util::io::StreamReader;
+use url::Url;
+
+#[derive(Debug)]
+enum TransportStream {
+    Insecure(TcpStream),
+    Secure(TlsStream<TcpStream>),
+}
+
+impl TransportStream {
+    fn get_tcpstream(&self) -> &TcpStream {
+        match self {
+            TransportStream::Insecure(s) => s,
+            TransportStream::Secure(s) => s.get_ref().get_ref().get_ref(),
+        }
+    }
+}
+
+impl AsyncRead for TransportStream {
+    fn poll_read(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut ReadBuf<'_>,
+    ) -> Poll<std::io::Result<()>> {
+        match self.get_mut() {
+            TransportStream::Insecure(s) => Pin::new(s).poll_read(cx, buf),
+            TransportStream::Secure(s) => Pin::new(s).poll_read(cx, buf),
+        }
+    }
+}
+
+impl AsyncWrite for TransportStream {
+    fn poll_write(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &[u8],
+    ) -> Poll<Result<usize, std::io::Error>> {
+        match self.get_mut() {
+            TransportStream::Insecure(s) => Pin::new(s).poll_write(cx, buf),
+            TransportStream::Secure(s) => Pin::new(s).poll_write(cx, buf),
+        }
+    }
+
+    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
+        match self.get_mut() {
+            TransportStream::Insecure(s) => Pin::new(s).poll_flush(cx),
+            TransportStream::Secure(s) => Pin::new(s).poll_flush(cx),
+        }
+    }
+
+    fn poll_shutdown(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+    ) -> Poll<Result<(), std::io::Error>> {
+        match self.get_mut() {
+            TransportStream::Insecure(s) => Pin::new(s).poll_shutdown(cx),
+            TransportStream::Secure(s) => Pin::new(s).poll_shutdown(cx),
+        }
+    }
+}
+
+#[derive(Debug)]
+struct StreamWrapper {
+    inner: WebSocketStream<TransportStream>,
+}
+
+impl Stream for StreamWrapper {
+    type Item = Result<Bytes, Error>;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        match Pin::new(&mut self.get_mut().inner).poll_next(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(None) => Poll::Ready(None),
+            Poll::Ready(Some(Err(err))) => {
+                Poll::Ready(Some(Err(Error::new(ErrorKind::Other, err))))
+            }
+            Poll::Ready(Some(Ok(res))) => {
+                if let Message::Binary(b) = res {
+                    Poll::Ready(Some(Ok(Bytes::from(b))))
+                } else {
+                    Poll::Ready(Some(Err(Error::new(
+                        ErrorKind::InvalidData,
+                        "unexpected frame",
+                    ))))
+                }
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+}
+
+#[derive(Debug)]
+pub struct WebsocketTunnel {
+    inner: StreamReader<StreamWrapper, Bytes>,
+}
+
+impl AsyncRead for WebsocketTunnel {
+    fn poll_read(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut ReadBuf<'_>,
+    ) -> Poll<std::io::Result<()>> {
+        Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
+    }
+}
+
+impl AsyncBufRead for WebsocketTunnel {
+    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<&[u8]>> {
+        Pin::new(&mut self.get_mut().inner).poll_fill_buf(cx)
+    }
+
+    fn consume(self: Pin<&mut Self>, amt: usize) {
+        Pin::new(&mut self.get_mut().inner).consume(amt)
+    }
+}
+
+impl AsyncWrite for WebsocketTunnel {
+    fn poll_write(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &[u8],
+    ) -> Poll<Result<usize, std::io::Error>> {
+        let sw = self.get_mut().inner.get_mut();
+        ready!(Pin::new(&mut sw.inner)
+            .poll_ready(cx)
+            .map_err(|err| Error::new(ErrorKind::Other, err)))?;
+
+        match Pin::new(&mut sw.inner).start_send(Message::Binary(buf.to_vec())) {
+            Ok(()) => Poll::Ready(Ok(buf.len())),
+            Err(e) => Poll::Ready(Err(Error::new(ErrorKind::Other, e))),
+        }
+    }
+
+    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
+        Pin::new(&mut self.get_mut().inner.get_mut().inner)
+            .poll_flush(cx)
+            .map_err(|err| Error::new(ErrorKind::Other, err))
+    }
+
+    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
+        Pin::new(&mut self.get_mut().inner.get_mut().inner)
+            .poll_close(cx)
+            .map_err(|err| Error::new(ErrorKind::Other, err))
+    }
+}
+
+#[derive(Debug)]
+enum SubTransport {
+    Secure(TlsTransport),
+    Insecure(TcpTransport),
+}
+
+#[derive(Debug)]
+pub struct WebsocketTransport {
+    sub: SubTransport,
+    conf: WebSocketConfig,
+}
+
+#[async_trait]
+impl Transport for WebsocketTransport {
+    type Acceptor = TcpListener;
+    type RawStream = TcpStream;
+    type Stream = WebsocketTunnel;
+
+    fn new(config: &TransportConfig) -> anyhow::Result<Self> {
+        let wsconfig = config
+            .websocket
+            .as_ref()
+            .ok_or_else(|| anyhow!("Missing websocket config"))?;
+
+        let conf = WebSocketConfig {
+            write_buffer_size: 0,
+            ..WebSocketConfig::default()
+        };
+        let sub = match wsconfig.tls {
+            true => SubTransport::Secure(TlsTransport::new(config)?),
+            false => SubTransport::Insecure(TcpTransport::new(config)?),
+        };
+        Ok(WebsocketTransport { sub, conf })
+    }
+
+    fn hint(conn: &Self::Stream, opt: SocketOpts) {
+        opt.apply(conn.inner.get_ref().inner.get_ref().get_tcpstream())
+    }
+
+    async fn bind<A: ToSocketAddrs + Send + Sync>(
+        &self,
+        addr: A,
+    ) -> anyhow::Result<Self::Acceptor> {
+        TcpListener::bind(addr).await.map_err(Into::into)
+    }
+
+    async fn accept(&self, a: &Self::Acceptor) -> anyhow::Result<(Self::RawStream, SocketAddr)> {
+        let (s, addr) = match &self.sub {
+            SubTransport::Insecure(t) => t.accept(a).await?,
+            SubTransport::Secure(t) => t.accept(a).await?,
+        };
+        Ok((s, addr))
+    }
+
+    async fn handshake(&self, conn: Self::RawStream) -> anyhow::Result<Self::Stream> {
+        let tsream = match &self.sub {
+            SubTransport::Insecure(t) => TransportStream::Insecure(t.handshake(conn).await?),
+            SubTransport::Secure(t) => TransportStream::Secure(t.handshake(conn).await?),
+        };
+        let wsstream = accept_async_with_config(tsream, Some(self.conf)).await?;
+        let tun = WebsocketTunnel {
+            inner: StreamReader::new(StreamWrapper { inner: wsstream }),
+        };
+        Ok(tun)
+    }
+
+    async fn connect(&self, addr: &AddrMaybeCached) -> anyhow::Result<Self::Stream> {
+        let u = format!("ws://{}", &addr.addr.as_str());
+        let url = Url::parse(&u).unwrap();
+        let tstream = match &self.sub {
+            SubTransport::Insecure(t) => TransportStream::Insecure(t.connect(addr).await?),
+            SubTransport::Secure(t) => TransportStream::Secure(t.connect(addr).await?),
+        };
+        let (wsstream, _) = client_async_with_config(url, tstream, Some(self.conf))
+            .await
+            .expect("failed to connect");
+        let tun = WebsocketTunnel {
+            inner: StreamReader::new(StreamWrapper { inner: wsstream }),
+        };
+        Ok(tun)
+    }
+}

+ 33 - 0
tests/for_tcp/websocket_tls_transport.toml

@@ -0,0 +1,33 @@
+[client]
+remote_addr = "127.0.0.1:2333" 
+default_token = "default_token_if_not_specify" 
+
+[client.transport]
+type = "websocket" 
+[client.transport.tls]
+trusted_root = "examples/tls/rootCA.crt"
+hostname = "localhost"
+[client.transport.websocket] 
+tls = true
+
+[client.services.echo] 
+local_addr = "127.0.0.1:8080" 
+[client.services.pingpong] 
+local_addr = "127.0.0.1:8081" 
+
+[server]
+bind_addr = "0.0.0.0:2333" 
+default_token = "default_token_if_not_specify" 
+
+[server.transport]
+type = "websocket" 
+[server.transport.tls]
+pkcs12 = "examples/tls/identity.pfx"
+pkcs12_password = "1234"
+[server.transport.websocket] 
+tls = true
+
+[server.services.echo] 
+bind_addr = "0.0.0.0:2334" 
+[server.services.pingpong] 
+bind_addr = "0.0.0.0:2335" 

+ 27 - 0
tests/for_tcp/websocket_transport.toml

@@ -0,0 +1,27 @@
+[client]
+remote_addr = "127.0.0.1:2333" 
+default_token = "default_token_if_not_specify" 
+
+[client.transport]
+type = "websocket" 
+[client.transport.websocket] 
+tls = false
+
+[client.services.echo] 
+local_addr = "127.0.0.1:8080" 
+[client.services.pingpong] 
+local_addr = "127.0.0.1:8081" 
+
+[server]
+bind_addr = "0.0.0.0:2333" 
+default_token = "default_token_if_not_specify" 
+
+[server.transport]
+type = "websocket" 
+[server.transport.websocket] 
+tls = false
+
+[server.services.echo] 
+bind_addr = "0.0.0.0:2334" 
+[server.services.pingpong] 
+bind_addr = "0.0.0.0:2335" 

+ 37 - 0
tests/for_udp/websocket_tls_transport.toml

@@ -0,0 +1,37 @@
+[client]
+remote_addr = "127.0.0.1:2332" 
+default_token = "default_token_if_not_specify" 
+
+[client.transport]
+type = "websocket"
+[client.transport.tls]
+trusted_root = "examples/tls/rootCA.crt"
+hostname = "localhost"
+[client.transport.websocket] 
+tls = true
+
+[client.services.echo] 
+type = "udp"
+local_addr = "127.0.0.1:8080" 
+[client.services.pingpong] 
+type = "udp"
+local_addr = "127.0.0.1:8081" 
+
+[server]
+bind_addr = "0.0.0.0:2332" 
+default_token = "default_token_if_not_specify" 
+
+[server.transport]
+type = "websocket" 
+[server.transport.tls]
+pkcs12 = "examples/tls/identity.pfx"
+pkcs12_password = "1234"
+[server.transport.websocket] 
+tls = true
+
+[server.services.echo] 
+type = "udp"
+bind_addr = "0.0.0.0:2334" 
+[server.services.pingpong] 
+type = "udp"
+bind_addr = "0.0.0.0:2335" 

+ 31 - 0
tests/for_udp/websocket_transport.toml

@@ -0,0 +1,31 @@
+[client]
+remote_addr = "127.0.0.1:2332" 
+default_token = "default_token_if_not_specify" 
+
+[client.transport]
+type = "websocket"
+[client.transport.websocket] 
+tls = false
+
+[client.services.echo] 
+type = "udp"
+local_addr = "127.0.0.1:8080" 
+[client.services.pingpong] 
+type = "udp"
+local_addr = "127.0.0.1:8081" 
+
+[server]
+bind_addr = "0.0.0.0:2332" 
+default_token = "default_token_if_not_specify" 
+
+[server.transport]
+type = "websocket" 
+[server.transport.websocket] 
+tls = false
+
+[server.services.echo] 
+type = "udp"
+bind_addr = "0.0.0.0:2334" 
+[server.services.pingpong] 
+type = "udp"
+bind_addr = "0.0.0.0:2335" 

+ 6 - 0
tests/integration_test.rs

@@ -59,6 +59,9 @@ async fn tcp() -> Result<()> {
     #[cfg(not(target_os = "macos"))]
     test("tests/for_tcp/tls_transport.toml", Type::Tcp).await?;
     test("tests/for_tcp/noise_transport.toml", Type::Tcp).await?;
+    test("tests/for_tcp/websocket_transport.toml", Type::Tcp).await?;
+    #[cfg(not(target_os = "macos"))]
+    test("tests/for_tcp/websocket_tls_transport.toml", Type::Tcp).await?;
 
     Ok(())
 }
@@ -86,6 +89,9 @@ async fn udp() -> Result<()> {
     #[cfg(not(target_os = "macos"))]
     test("tests/for_udp/tls_transport.toml", Type::Udp).await?;
     test("tests/for_udp/noise_transport.toml", Type::Udp).await?;
+    test("tests/for_udp/websocket_transport.toml", Type::Udp).await?;
+    #[cfg(not(target_os = "macos"))]
+    test("tests/for_udp/websocket_tls_transport.toml", Type::Udp).await?;
 
     Ok(())
 }