Skip to content

Commit

Permalink
IP互助计划, 添加正向代理服务(TODO)
Browse files Browse the repository at this point in the history
  • Loading branch information
qaiu committed Nov 12, 2024
1 parent b6a9c2d commit 70b38db
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 14 deletions.
13 changes: 13 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand All @@ -84,6 +90,13 @@
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<!-- 代码生成器 -->
<annotationProcessors>
<annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
</annotationProcessors>
<generatedSourcesDirectory>
${project.basedir}/src/main/java
</generatedSourcesDirectory>
</configuration>
</plugin>
</plugins>
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/java/cn/qaiu/vx/core/Deploy.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cn.qaiu.vx.core;

import cn.qaiu.vx.core.util.CommonUtil;
import cn.qaiu.vx.core.util.ConfigUtil;
import cn.qaiu.vx.core.util.VertxHolder;
import cn.qaiu.vx.core.verticle.ReverseProxyVerticle;
Expand Down Expand Up @@ -102,7 +103,7 @@ private void outLogo(JsonObject conf) {
""";

System.out.printf(logoTemplate,
conf.getString("version_app"),
CommonUtil.getAppVersion(),
VersionCommand.getVersion(),
conf.getString("copyright"),
year
Expand All @@ -125,7 +126,9 @@ private void deployVerticle() {
vertxOptions.setAddressResolverOptions(
new AddressResolverOptions().
addServer("114.114.114.114").
addServer("114.114.115.115"));
addServer("114.114.115.115").
addServer("8.8.8.8").
addServer("8.8.4.4"));
LOGGER.info("vertxConfigEventLoopPoolSize: {}, eventLoopPoolSize: {}, workerPoolSize: {}", vertxConfigELPS,
vertxOptions.getEventLoopPoolSize(),
vertxOptions.getWorkerPoolSize());
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/java/cn/qaiu/vx/core/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* ModuleGen cn.qaiu.vx.core
*/
@ModuleGen(name = "vertx-http-proxy", groupPackage = "cn.qaiu.vx.core", useFutures = true)
package cn.qaiu.vx.core;

import io.vertx.codegen.annotations.ModuleGen;
18 changes: 18 additions & 0 deletions core/src/main/java/cn/qaiu/vx/core/util/CommonUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -139,4 +140,21 @@ public static <T> Set<T> sortClassSet(Set<Class<? extends T>> set) {
}
}).collect(Collectors.toSet());
}

private static String appVersion;

public static String getAppVersion() {
if (null == appVersion) {
Properties properties = new Properties();
try {
properties.load(CommonUtil.class.getClassLoader().getResourceAsStream("app.properties"));
if (!properties.isEmpty()) {
appVersion = properties.getProperty("app.version");
}
} catch (IOException e) {
e.printStackTrace();
}
}
return appVersion;
}
}
184 changes: 184 additions & 0 deletions core/src/main/java/cn/qaiu/vx/core/verticle/HttpProxyVerticle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package cn.qaiu.vx.core.verticle;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.dns.AddressResolverOptions;
import io.vertx.core.http.*;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.ProxyOptions;

import java.util.Base64;

/**
*
*/
public class HttpProxyVerticle extends AbstractVerticle {

private HttpClient httpClient;
private NetClient netClient;

@Override
public void start() {
ProxyOptions proxyOptions = new ProxyOptions().setHost("127.0.0.1").setPort(7890);
// 初始化 HTTP 客户端,用于向目标服务器发送 HTTP 请求
HttpClientOptions httpClientOptions = new HttpClientOptions();
httpClient = vertx.createHttpClient(httpClientOptions.setProxyOptions(proxyOptions));

// 创建并启动 HTTP 代理服务器,监听指定端口
HttpServer server = vertx.createHttpServer(new HttpServerOptions().setClientAuth(ClientAuth.REQUIRED));
server.requestHandler(this::handleClientRequest);

// 初始化 NetClient,用于在 CONNECT 请求中建立 TCP 连接隧道
netClient = vertx.createNetClient(new NetClientOptions()
.setProxyOptions(proxyOptions)
.setConnectTimeout(15000)
.setTrustAll(true));

// 启动 HTTP 代理服务器
server.listen(7891, ar -> {
if (ar.succeeded()) {
System.out.println("HTTP Proxy server started on port 7891");
} else {
System.err.println("Failed to start HTTP Proxy server: " + ar.cause());
}
});
}

// 处理 HTTP CONNECT 请求,用于代理 HTTPS 流量
private void handleConnectRequest(HttpServerRequest clientRequest) {
String[] uriParts = clientRequest.uri().split(":");
if (uriParts.length != 2) {
clientRequest.response().setStatusCode(400).end("Bad Request: Invalid URI format");
return;
}

// 解析目标主机和端口
String targetHost = uriParts[0];
int targetPort;
try {
targetPort = Integer.parseInt(uriParts[1]);
} catch (NumberFormatException e) {
clientRequest.response().setStatusCode(400).end("Bad Request: Invalid port");
return;
}
clientRequest.pause();
// 通过 NetClient 连接目标服务器并创建隧道
netClient.connect(targetPort, targetHost, connectionAttempt -> {
if (connectionAttempt.succeeded()) {
NetSocket targetSocket = connectionAttempt.result();

// 升级客户端连接到 NetSocket 并实现双向数据流
clientRequest.toNetSocket().onComplete(clientSocketAttempt -> {
if (clientSocketAttempt.succeeded()) {
NetSocket clientSocket = clientSocketAttempt.result();

// 设置双向数据流转发
clientSocket.handler(targetSocket::write);
targetSocket.handler(clientSocket::write);

// 关闭其中一方时关闭另一方
clientSocket.closeHandler(v -> targetSocket.close());
targetSocket.closeHandler(v -> clientSocket.close());
} else {
System.err.println("Failed to upgrade client connection to socket: " + clientSocketAttempt.cause().getMessage());
targetSocket.close();
clientRequest.response().setStatusCode(500).end("Internal Server Error");
}
});
} else {
System.err.println("Failed to connect to target: " + connectionAttempt.cause().getMessage());
clientRequest.response().setStatusCode(502).end("Bad Gateway: Unable to connect to target");
}
});
}

// 处理客户端的 HTTP 请求
private void handleClientRequest(HttpServerRequest clientRequest) {
String s = clientRequest.headers().get("Proxy-Authorization");
if (s == null) {
clientRequest.response().setStatusCode(403).end();
return;
}
String[] split = new String(Base64.getDecoder().decode(s.replace("Basic ", ""))).split(":");
if (split.length > 1) {
System.out.println(split[0]);
System.out.println(split[1]);
// TODO
}


if (clientRequest.method() == HttpMethod.CONNECT) {
// 处理 CONNECT 请求
handleConnectRequest(clientRequest);
} else {
// 处理普通的 HTTP 请求
handleHttpRequest(clientRequest);
}
}

// 处理 HTTP 请求,转发至目标服务器并返回响应
private void handleHttpRequest(HttpServerRequest clientRequest) {
// 获取目标主机
String hostHeader = clientRequest.getHeader("Host");
if (hostHeader == null) {
clientRequest.response().setStatusCode(400).end("Host header is missing");
return;
}

String targetHost = hostHeader.split(":")[0];
int targetPort = 80; // 默认为 HTTP 的端口
clientRequest.pause(); // 暂停客户端请求的读取,避免数据丢失

httpClient.request(clientRequest.method(), targetPort, targetHost, clientRequest.uri())
.onSuccess(request -> {
clientRequest.resume(); // 恢复客户端请求的读取

// 逐个设置请求头
clientRequest.headers().forEach(header -> request.putHeader(header.getKey(), header.getValue()));

// 将客户端请求的 body 转发给目标服务器
clientRequest.bodyHandler(body -> request.send(body, ar -> {
if (ar.succeeded()) {
var response = ar.result();
clientRequest.response().setStatusCode(response.statusCode());
clientRequest.response().headers().setAll(response.headers());
response.body().onSuccess(b-> clientRequest.response().end(b));
} else {
clientRequest.response().setStatusCode(502).end("Bad Gateway: Unable to reach target");
}
}));
})
.onFailure(err -> {
err.printStackTrace();
clientRequest.response().setStatusCode(502).end("Bad Gateway: Request failed");
});
}

@Override
public void stop() {
// 停止 HTTP 客户端以释放资源
if (httpClient != null) {
httpClient.close();
}
}

/**
* TODO add Deploy
* @param args
*/
public static void main(String[] args) {
// 配置 DNS 解析器,使用多个 DNS 服务器来提升解析速度
Vertx vertx = Vertx.vertx(new VertxOptions()
.setAddressResolverOptions(new AddressResolverOptions()
.addServer("114.114.114.114")
.addServer("114.114.115.115")
.addServer("8.8.8.8")
.addServer("8.8.4.4")));

// 部署 Verticle 并启动动态 HTTP 代理服务器
vertx.deployVerticle(new HttpProxyVerticle());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cn.qaiu.vx.core.verticle.conf;

import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.json.annotations.JsonGen;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.ProxyOptions;

import java.util.UUID;

@DataObject
@JsonGen(publicConverter = false)
public class HttpProxyConf {

public static final String DEFAULT_USERNAME = UUID.randomUUID().toString();

public static final String DEFAULT_PASSWORD = UUID.randomUUID().toString();

public static final Integer DEFAULT_PORT = 6402;

public static final Integer DEFAULT_TIMEOUT = 15000;

Integer timeout;

String username;

String password;

Integer port;

ProxyOptions preProxyOptions;

public HttpProxyConf() {
this.username = DEFAULT_USERNAME;
this.password = DEFAULT_PASSWORD;
this.timeout = DEFAULT_PORT;
this.timeout = DEFAULT_TIMEOUT;
this.preProxyOptions = new ProxyOptions();
}

public HttpProxyConf(JsonObject json) {
this();
}


public Integer getTimeout() {
return timeout;
}

public HttpProxyConf setTimeout(Integer timeout) {
this.timeout = timeout;
return this;
}

public String getUsername() {
return username;
}

public HttpProxyConf setUsername(String username) {
this.username = username;
return this;
}

public String getPassword() {
return password;
}

public HttpProxyConf setPassword(String password) {
this.password = password;
return this;
}

public Integer getPort() {
return port;
}

public HttpProxyConf setPort(Integer port) {
this.port = port;
return this;
}

public ProxyOptions getPreProxyOptions() {
return preProxyOptions;
}

public HttpProxyConf setPreProxyOptions(ProxyOptions preProxyOptions) {
this.preProxyOptions = preProxyOptions;
return this;
}
}
1 change: 1 addition & 0 deletions core/src/main/resources/app.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app.version=${project.version}
6 changes: 2 additions & 4 deletions web-service/src/main/resources/app.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# 要激活的配置: dev--连接本地数据库; prod连接线上数据库
# 要激活的配置: app-配置名称.yml
active: dev
# 版本号
version_app: 0.1.8
# 公司名称 -> LOGO版权文字
# 控制台输出的版权文字
copyright: QAIU
5 changes: 5 additions & 0 deletions web-service/src/main/resources/http-proxy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 正向代理
server-name: Vert.x-proxy-http(v4.1.2)

proxy:
- listen: 6402
8 changes: 0 additions & 8 deletions web-service/src/main/resources/server-proxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,4 @@ proxy:
# origin: 127.0.0.1:8088


#proxy:
# - listen: 8085
# static:
# path: /mu-down
# # add-headers:
# # x-token: ABC
# root: webroot/mu-down/
# index: index.html

0 comments on commit 70b38db

Please sign in to comment.