Skip to content

Commit 338d540

Browse files
committed
Merge branch 'tunnel' into master
2 parents 6829dae + b15c316 commit 338d540

23 files changed

+3882
-6
lines changed

README.md

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# tcp-reverse-proxy
2-
基于[Vert.x](https://vertx.io/)实现的TCP反向代理与HTTP反向代理。
2+
基于[Vert.x](https://vertx.io/)实现的HTTP反向代理与TCP反向代理、内网穿透
33

44
开发环境
55

@@ -19,14 +19,27 @@
1919
<artifactId>vertx-core</artifactId>
2020
<version>4.5.10</version>
2121
</dependency>
22+
<!-- 若不使用http反向代理,可不加此依赖 -->
2223
<dependency>
2324
<groupId>io.vertx</groupId>
2425
<artifactId>vertx-web</artifactId>
2526
<version>4.5.10</version>
2627
</dependency>
28+
<!-- 若不想添加日志,可只添加slf4j-api -->
29+
<dependency>
30+
<groupId>ch.qos.logback</groupId>
31+
<artifactId>logback-classic</artifactId>
32+
<version>1.2.12</version>
33+
</dependency>
34+
<!-- 若不使用TCP内网穿透,可不加此依赖 -->
35+
<dependency>
36+
<groupId>com.google.protobuf</groupId>
37+
<artifactId>protobuf-javalite</artifactId>
38+
<version>4.30.2</version>
39+
</dependency>
2740
```
2841

29-
## TCP反向代理
42+
## 一、TCP反向代理
3043

3144
实现TCP反向代理:`0.0.0.0:22`↔️`10.0.0.1:8080`
3245

@@ -36,7 +49,83 @@ ReverseTcpProxy.create(Vertx.vertx(), "10.0.0.1", 8080)
3649
.start();
3750
```
3851

39-
## HTTP反向代理
52+
## 二、TCP内网穿透
53+
54+
虚线表示进程内部通信。实线表示外部通信。
55+
56+
一些代码上的设计思路,参考[socket.io-client-java](https://github.com/socketio/socket.io-client-java/blob/socket.io-client-2.1.0/src/main/java/io/socket/client/Socket.java)
57+
58+
```mermaid
59+
sequenceDiagram
60+
autonumber
61+
participant u as User
62+
participant dps as DataProxyServer
63+
participant ts as TunnelServer
64+
participant tc as TunnelClient
65+
participant bs as BackendServer
66+
67+
ts-->ts: 监听44444端口
68+
tc->>ts: 建立控制连接、发送鉴权密钥、申请启用22端口数据服务
69+
ts-->ts: 鉴权校验通过
70+
ts-->>dps: 你要开启22端口
71+
dps-->dps: 监听22端口
72+
dps-->>ts: 已开启
73+
ts->>tc: 成功
74+
tc-->tc: 开启与控制服务的周期心跳
75+
loop 控制连接保活
76+
tc->>ts: 发送心跳
77+
ts->>tc: 响应心跳
78+
end
79+
u->>dps: 建立数据连接
80+
dps-->>ts: 通知
81+
ts->>tc: 你需要主动与22数据服务建立数据连接
82+
tc->>dps: 建立数据连接
83+
note left of dps: 用户连接和数据连接绑定双向生命周期、双向数据传输
84+
tc->>bs: 建立后端连接
85+
dps->>tc: 成功
86+
bs->>tc: 成功
87+
note right of tc: 数据连接和后端连接绑定双向生命周期、双向数据传输
88+
89+
u->dps: 双向传输
90+
dps->tc: 双向传输
91+
tc->bs: 双向传输
92+
93+
94+
```
95+
96+
假如我有一个内网`SSH`服务`10.0.0.10:22`,需要通过`192.168.0.200:22`穿透出去。并且网络条件受限如下
97+
98+
1. `10.0.0.10`可以主动连接`192.168.0.200`
99+
2. `192.168.0.200`无法主动连接`10.0.0.10`
100+
3. 只要双方建立连接,即可实现双向数据传输
101+
102+
这就需要TCP内网穿透了。假设你内网穿透使用的控制端口为`44444`
103+
104+
首先,在`192.168.0.200`这台机器,使用如下代码启动`TunnelServer`
105+
106+
```java
107+
ReverseTcpProxyTunnelServer.create(Vertx.vertx())
108+
.port(44444)
109+
// 用于用户连接和数据连接的延迟判定,如果网络较差/DNS解析较慢的情况下,建议将该参数调大
110+
.judgeDelay(2000)
111+
.start();
112+
```
113+
114+
`10.0.0.10`这台机器,使用如下代码启动`TunnelClient`
115+
116+
```java
117+
ReverseTcpProxyTunnelClient.create(Vertx.vertx())
118+
.backendHost("10.0.0.10")
119+
.backendPort(22)
120+
.dataProxyName("ssh-proxy")
121+
.dataProxyHost("192.168.0.200")
122+
.dataProxyPort(22)
123+
.connect("192.168.0.200", 44444);
124+
```
125+
126+
127+
128+
## 三、HTTP反向代理
40129

41130
实现HTTP反向代理,代理路由优先级如下
42131

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
<version>4.5.10</version>
4141
<scope>provided</scope>
4242
</dependency>
43+
<dependency>
44+
<groupId>com.google.protobuf</groupId>
45+
<artifactId>protobuf-javalite</artifactId>
46+
<version>4.30.2</version>
47+
<scope>provided</scope>
48+
</dependency>
4349
</dependencies>
4450

4551

src/main/java/top/meethigher/proxy/http/ReverseHttpProxy.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ protected static String generateName() {
254254
/**
255255
* 更新路由内数据。
256256
* 该内容以路由为单位。
257+
*
258+
* @param route 路由对象
259+
* @param key 元数据键
260+
* @param value 元数据值
257261
*/
258262
protected void setRouteMetadata(Route route, String key, Object value) {
259263
route.putMetadata(key, value == null ? "" : value);
@@ -268,6 +272,10 @@ protected Object getRouteMetadata(Route route, String key) {
268272
/**
269273
* 更新请求上下文内数据。
270274
* 该内容以请求为单位。对于请求来说,是线程安全的。
275+
*
276+
* @param ctx 路由上下文
277+
* @param key 数据键
278+
* @param value 数据值
271279
*/
272280
protected void setContextData(RoutingContext ctx, String key, Object value) {
273281
ctx.put(key, value == null ? "" : value);
@@ -348,7 +356,12 @@ public ReverseHttpProxy addRoute(ProxyRoute proxyRoute, Integer order) {
348356
}
349357

350358
/**
351-
* order越小,优先级越高
359+
* 添加路由
360+
*
361+
* @param proxyRoute 路由信息
362+
* @param order order越小,优先级越高
363+
* @param printLog true表示打印日志
364+
* @return 实例本身
352365
*/
353366
public ReverseHttpProxy addRoute(
354367
ProxyRoute proxyRoute,
@@ -406,6 +419,9 @@ public List<Route> getRoutes() {
406419
/**
407420
* 将标头转为小写后,判断是否是逐跳标头
408421
* 时间复杂度为 O(1)
422+
*
423+
* @param headerName 标头名称
424+
* @return 是否是逐跳标头
409425
*/
410426
protected boolean isHopByHopHeader(String headerName) {
411427
return headerName != null && HOP_BY_HOP_HEADERS_SET.contains(headerName.toLowerCase());
@@ -414,6 +430,10 @@ protected boolean isHopByHopHeader(String headerName) {
414430

415431
/**
416432
* 复制请求头。复制的过程中忽略逐跳标头
433+
*
434+
* @param ctx 路由上下文
435+
* @param realReq 真实请求
436+
* @param proxyReq 代理请求
417437
*/
418438
protected void copyRequestHeaders(RoutingContext ctx, HttpServerRequest realReq, HttpClientRequest proxyReq) {
419439
proxyReq.headers().clear();
@@ -451,6 +471,11 @@ protected void copyRequestHeaders(RoutingContext ctx, HttpServerRequest realReq,
451471

452472
/**
453473
* 复制响应头。复制的过程中忽略逐跳标头
474+
*
475+
* @param ctx 路由上下文
476+
* @param realReq 真实请求
477+
* @param realResp 真实响应
478+
* @param proxyResp 代理响应
454479
*/
455480
protected void copyResponseHeaders(RoutingContext ctx, HttpServerRequest realReq, HttpServerResponse realResp, HttpClientResponse proxyResp) {
456481
realResp.headers().clear();
@@ -510,6 +535,11 @@ else if ("Location".equalsIgnoreCase(headerName)) {
510535

511536
/**
512537
* 重写Location
538+
*
539+
* @param ctx 路由上下文
540+
* @param url 原始URL
541+
* @param location 重定向位置
542+
* @return 重写后的Location
513543
*/
514544
protected String rewriteLocation(RoutingContext ctx, String url, String location) {
515545
// 若重定向的地址,在反向代理的范围内,则进行重写
@@ -525,6 +555,12 @@ protected String rewriteLocation(RoutingContext ctx, String url, String location
525555

526556
/**
527557
* 发起请求Handler
558+
*
559+
* @param ctx 路由上下文
560+
* @param serverReq 服务端请求
561+
* @param serverResp 服务端响应
562+
* @param proxyUrl 代理URL
563+
* @return 异步处理Handler
528564
*/
529565
protected Handler<AsyncResult<HttpClientResponse>> sendRequestHandler(RoutingContext ctx, HttpServerRequest serverReq, HttpServerResponse serverResp, String proxyUrl) {
530566
return ar -> {
@@ -560,6 +596,12 @@ protected Handler<AsyncResult<HttpClientResponse>> sendRequestHandler(RoutingCon
560596

561597
/**
562598
* 建立连接Handler
599+
*
600+
* @param ctx 路由上下文
601+
* @param serverReq 服务端请求
602+
* @param serverResp 服务端响应
603+
* @param proxyUrl 代理URL
604+
* @return 异步处理Handler
563605
*/
564606
protected Handler<AsyncResult<HttpClientRequest>> connectHandler(RoutingContext ctx, HttpServerRequest serverReq, HttpServerResponse serverResp, String proxyUrl) {
565607
return ar -> {
@@ -611,6 +653,9 @@ protected void badGateway(RoutingContext ctx, HttpServerResponse serverResp) {
611653

612654
/**
613655
* 路由处理Handler
656+
*
657+
* @param httpClient HTTP客户端
658+
* @return 路由处理Handler
614659
*/
615660
protected Handler<RoutingContext> routingContextHandler(HttpClient httpClient) {
616661
return ctx -> {
@@ -685,6 +730,11 @@ protected Handler<RoutingContext> routingContextHandler(HttpClient httpClient) {
685730
/**
686731
* 获取代理后的完整proxyUrl,不区分代理目标路径是否以/结尾。
687732
* 处理逻辑为删除掉匹配的路径,并将剩下的内容追加到代理目标路径后面。
733+
*
734+
* @param ctx 路由上下文
735+
* @param serverReq 服务端请求
736+
* @param serverResp 服务端响应
737+
* @return 代理后的完整URL
688738
*/
689739
protected String getProxyUrl(RoutingContext ctx, HttpServerRequest serverReq, HttpServerResponse serverResp) {
690740
String targetUrl = getContextData(ctx, P_TARGET_URL).toString();

src/main/java/top/meethigher/proxy/http/UrlParser.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ public class UrlParser {
77

88
/**
99
* 在压测时,性能比String.replace略优
10+
* @param text 需要替换的文本
11+
* @param search 要搜索的字符串
12+
* @param replacement 替换的字符串
13+
* @return 替换后的文本
1014
*/
1115
public static String fastReplace(String text, String search, String replacement) {
1216
if (text == null || search == null || replacement == null || search.isEmpty()) {
@@ -25,6 +29,9 @@ public static String fastReplace(String text, String search, String replacement)
2529

2630
/**
2731
* 拼接两个uri。不要求uri以"/"结尾
32+
* @param uri1 第一个URI字符串
33+
* @param uri2 第二个URI字符串
34+
* @return 拼接后的URI字符串
2835
*/
2936
public static String joinURI(String uri1, String uri2) {
3037
if (uri1.endsWith("/") && uri2.startsWith("/")) {

0 commit comments

Comments
 (0)