aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar I Putu Ariyasa <rucciva@gmail.com> 2023-10-01 13:43:03 +0700
committerGravatar GitHub <noreply@github.com> 2023-10-01 14:43:03 +0800
commit5946a18370b160a6b93a8ae28b79e70129db5b68 (patch)
treee643d0486c04798a74ec1b7ba914f55cad883198
parentd2fe586f7b3caceda542ef5030be0257d6d1401c (diff)
downloadrathole-5946a18370b160a6b93a8ae28b79e70129db5b68.tar.gz
rathole-5946a18370b160a6b93a8ae28b79e70129db5b68.tar.zst
rathole-5946a18370b160a6b93a8ae28b79e70129db5b68.zip
feat(transport): add websocket transport (#290)
-rw-r--r--Cargo.lock322
-rw-r--r--Cargo.toml8
-rw-r--r--Dockerfile11
-rw-r--r--README-zh.md26
-rw-r--r--README.md24
-rw-r--r--src/client.rs11
-rw-r--r--src/config.rs15
-rw-r--r--src/server.rs11
-rw-r--r--src/transport/mod.rs5
-rw-r--r--src/transport/tls.rs2
-rw-r--r--src/transport/websocket.rs250
-rw-r--r--tests/for_tcp/websocket_tls_transport.toml33
-rw-r--r--tests/for_tcp/websocket_transport.toml27
-rw-r--r--tests/for_udp/websocket_tls_transport.toml37
-rw-r--r--tests/for_udp/websocket_transport.toml31
-rw-r--r--tests/integration_test.rs6
16 files changed, 728 insertions, 91 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 814be2f..d7b9448 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,15 @@
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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]]
@@ -181,6 +190,21 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -310,7 +334,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.103",
]
[[package]]
@@ -445,6 +469,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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]]
@@ -678,6 +708,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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"
@@ -1022,15 +1058,24 @@ dependencies = [
]
[[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]]
@@ -1118,6 +1163,15 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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",
@@ -1488,6 +1546,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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]]
@@ -1594,6 +1658,17 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1678,6 +1753,16 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1701,10 +1786,21 @@ dependencies = [
]
[[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]]
@@ -1872,10 +1967,22 @@ dependencies = [
]
[[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]]
@@ -2058,6 +2165,25 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2107,6 +2233,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2217,22 +2349,52 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2245,6 +2407,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2257,6 +2425,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2269,6 +2443,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2281,12 +2461,24 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2299,6 +2491,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index c4e0df5..117f0e7 100644
--- a/Cargo.toml
+++ b/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"] }
diff --git a/Dockerfile b/Dockerfile
index 636858e..c190596 100644
--- a/Dockerfile
+++ b/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
diff --git a/README-zh.md b/README-zh.md
index 49209ee..cb85db0 100644
--- a/README-zh.md
+++ b/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
diff --git a/README.md b/README.md
index 433eab6..18e46af 100644
--- a/README.md
+++ b/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
diff --git a/src/client.rs b/src/client.rs
index 21dbc2b..95a5a74 100644
--- a/src/client.rs
+++ b/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")
+ }
}
}
diff --git a/src/config.rs b/src/config.rs
index aa7f396..ca85fc2 100644
--- a/src/config.rs
+++ b/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(()),
}
}
diff --git a/src/server.rs b/src/server.rs
index 48381d5..a36e3c2 100644
--- a/src/server.rs
+++ b/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(())
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index 452da1a..38682a6 100644
--- a/src/transport/mod.rs
+++ b/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
diff --git a/src/transport/tls.rs b/src/transport/tls.rs
index 80d0fbd..918af04 100644
--- a/src/transport/tls.rs
+++ b/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() {
diff --git a/src/transport/websocket.rs b/src/transport/websocket.rs
new file mode 100644
index 0000000..ec6177d
--- /dev/null
+++ b/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)
+ }
+}
diff --git a/tests/for_tcp/websocket_tls_transport.toml b/tests/for_tcp/websocket_tls_transport.toml
new file mode 100644
index 0000000..849c947
--- /dev/null
+++ b/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"
diff --git a/tests/for_tcp/websocket_transport.toml b/tests/for_tcp/websocket_transport.toml
new file mode 100644
index 0000000..a007981
--- /dev/null
+++ b/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"
diff --git a/tests/for_udp/websocket_tls_transport.toml b/tests/for_udp/websocket_tls_transport.toml
new file mode 100644
index 0000000..263130b
--- /dev/null
+++ b/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"
diff --git a/tests/for_udp/websocket_transport.toml b/tests/for_udp/websocket_transport.toml
new file mode 100644
index 0000000..43186e0
--- /dev/null
+++ b/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"
diff --git a/tests/integration_test.rs b/tests/integration_test.rs
index 5265057..807f6fb 100644
--- a/tests/integration_test.rs
+++ b/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(())
}