init
parent
8de47479c5
commit
7c55f8da61
|
|
@ -1,26 +1,13 @@
|
|||
# ---> Java
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
/target
|
||||
/distribution
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
.*
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
|
|
|
|||
128
README.md
128
README.md
|
|
@ -1,3 +1,127 @@
|
|||
# nwct
|
||||
lanproxy
|
||||
--------
|
||||
|
||||
内网穿透
|
||||
[README](README_en.md) | [中文文档](README.md)
|
||||
|
||||
lanproxy是一个将局域网个人电脑、服务器代理到公网的内网穿透工具,目前仅支持tcp流量转发,可支持任何tcp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面...)。目前市面上提供类似服务的有花生壳、TeamView、GoToMyCloud等等,但要使用第三方的公网服务器就必须为第三方付费,并且这些服务都有各种各样的限制,此外,由于数据包会流经第三方,因此对数据安全也是一大隐患。
|
||||
|
||||
### 相关地址
|
||||
|
||||
- 主页 https://lanproxy.thingsglobal.org
|
||||
- lanproxy-go-client https://github.com/ffay/lanproxy-go-client
|
||||
- 发布包下载地址 https://github.com/ffay/lanproxy/releases
|
||||
|
||||
### 使用
|
||||
|
||||
#### 获取发布包
|
||||
|
||||
- 拉取源码,运行 mvn package,打包后的资源放在distribution目录中,包括client和server
|
||||
- 或直接下载发布包 https://github.com/ffay/lanproxy/releases
|
||||
|
||||
#### 配置
|
||||
|
||||
##### server配置
|
||||
|
||||
server的配置文件放置在conf目录中,配置 config.properties
|
||||
|
||||
```properties
|
||||
server.bind=0.0.0.0
|
||||
|
||||
#与代理客户端通信端口
|
||||
server.port=4900
|
||||
|
||||
#ssl相关配置
|
||||
server.ssl.enable=true
|
||||
server.ssl.bind=0.0.0.0
|
||||
server.ssl.port=4993
|
||||
server.ssl.jksPath=test.jks
|
||||
server.ssl.keyStorePassword=123456
|
||||
server.ssl.keyManagerPassword=123456
|
||||
|
||||
#这个配置可以忽略
|
||||
server.ssl.needsClientAuth=false
|
||||
|
||||
#WEB在线配置管理相关信息
|
||||
config.server.bind=0.0.0.0
|
||||
config.server.port=8090
|
||||
config.admin.username=admin
|
||||
config.admin.password=admin
|
||||
```
|
||||
|
||||
代理配置,打开地址 http://ip:8090 ,使用上面配置中配置的用户名密码登录,进入如下代理配置界面
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
> 一个server可以支持多个客户端连接
|
||||
> 配置数据存放在 ~/.lanproxy/config.json 文件中
|
||||
|
||||
##### Java 客户端配置
|
||||
|
||||
> Java client的配置文件放置在conf目录中,配置 config.properties
|
||||
|
||||
```properties
|
||||
|
||||
#与在proxy-server配置后台创建客户端时填写的秘钥保持一致;
|
||||
client.key=
|
||||
ssl.enable=true
|
||||
ssl.jksPath=test.jks
|
||||
ssl.keyStorePassword=123456
|
||||
|
||||
#这里填写实际的proxy-server地址;没有服务器默认即可,自己有服务器的更换为自己的proxy-server(IP)地址
|
||||
server.host=lp.thingsglobal.org
|
||||
|
||||
#proxy-server ssl默认端口4993,默认普通端口4900
|
||||
#ssl.enable=true时这里填写ssl端口,ssl.enable=false时这里填写普通端口
|
||||
server.port=4993
|
||||
```
|
||||
|
||||
- 安装java1.7或以上环境
|
||||
- linux(mac)环境中运行bin目录下的 startup.sh
|
||||
- windows环境中运行bin目录下的 startup.bat
|
||||
|
||||
##### 其他平台客户端
|
||||
|
||||
> 不用java客户端的可以使用下面提供的各个平台的客户端,省去安装java运行环境
|
||||
|
||||
###### 源码地址
|
||||
|
||||
https://github.com/ffay/lanproxy-go-client
|
||||
|
||||
###### 发布包
|
||||
|
||||
https://github.com/ffay/lanproxy-go-client/releases
|
||||
|
||||
###### 普通端口连接
|
||||
|
||||
```shell
|
||||
# mac 64位
|
||||
nohup ./client_darwin_amd64 -s SERVER_IP -p SERVER_PORT -k CLIENT_KEY &
|
||||
|
||||
# linux 64位
|
||||
nohup ./client_linux_amd64 -s SERVER_IP -p SERVER_PORT -k CLIENT_KEY &
|
||||
|
||||
# windows 64 位
|
||||
./client_windows_amd64.exe -s SERVER_IP -p SERVER_PORT -k CLIENT_KEY
|
||||
```
|
||||
|
||||
###### SSL端口连接
|
||||
|
||||
```shell
|
||||
# mac 64位
|
||||
nohup ./client_darwin_amd64 -s SERVER_IP -p SERVER_SSL_PORT -k CLIENT_KEY -ssl true &
|
||||
|
||||
# linux 64位
|
||||
nohup ./client_linux_amd64 -s SERVER_IP -p SERVER_SSL_PORT -k CLIENT_KEY -ssl true &
|
||||
|
||||
# windows 64 位
|
||||
./client_windows_amd64.exe -s SERVER_IP -p SERVER_SSL_PORT -k CLIENT_KEY -ssl true
|
||||
```
|
||||
|
||||
#### 其他
|
||||
|
||||
- 后面是将自己电脑从VPS代理出去测试的一个地址,大家可以访问试试 http://devbook.thingsglobal.org/
|
||||
- 对于正常网站,80和443端口只有一个,可以和 https://github.com/ffay/proxygateway 这个项目或nginx配合使用,不同域名反向代理到内部其他端口上去,再由其他端口映射到内网即可;对于ssh或远程桌面等可随意选择其他未占用的端口映射到内网
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
### Lanproxy
|
||||
|
||||
[README](README_en.md) | [中文文档](README.md)
|
||||
|
||||
Lanproxy is a reverse proxy to help you expose a local server behind a NAT or firewall to the internet. it supports any protocols over tcp (http https ssh ...)
|
||||
|
||||
### Features
|
||||
|
||||
- Secure tunnels to localhost
|
||||
- Supports any protocols over tcp (http https ssh ...)
|
||||
- Supports web config pages, easy to configure management
|
||||
- Written by java netty framework, high performance
|
||||
|
||||
### What can I do with Lanproxy
|
||||
|
||||
- Demo without deploying
|
||||
- Simplify mobile device testing
|
||||
- Build webhook integrations with ease
|
||||
- Run personal cloud services from your own private network
|
||||
|
||||
### Architecture
|
||||

|
||||
|
||||
### Configure
|
||||
|
||||
#### Server
|
||||
|
||||
proxy-server config file is **conf/config.properties**
|
||||
|
||||
```properties
|
||||
server.bind=0.0.0.0
|
||||
|
||||
#Plain tcp port
|
||||
server.port=4900
|
||||
|
||||
#ssl
|
||||
server.ssl.enable=true
|
||||
server.ssl.bind=0.0.0.0
|
||||
server.ssl.port=4993
|
||||
server.ssl.jksPath=test.jks
|
||||
server.ssl.keyStorePassword=123456
|
||||
server.ssl.keyManagerPassword=123456
|
||||
server.ssl.needsClientAuth=false
|
||||
|
||||
#web config pages
|
||||
config.server.bind=0.0.0.0
|
||||
config.server.port=8090
|
||||
config.admin.username=admin
|
||||
config.admin.password=admin
|
||||
|
||||
```
|
||||
|
||||
> Visit your config web service using url http://ip:8090
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### client
|
||||
|
||||
proxy-client config file is **conf/config.properties**
|
||||
|
||||
```properties
|
||||
#get from proxy-server client list page
|
||||
client.key=
|
||||
ssl.enable=true
|
||||
ssl.jksPath=test.jks
|
||||
ssl.keyStorePassword=123456
|
||||
|
||||
#your proxy server ip
|
||||
server.host=
|
||||
|
||||
#proxy-server ssl port is 4993,plain port is 4900
|
||||
server.port=4993
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
- Get release package from https://github.com/ffay/lanproxy/releases
|
||||
- Java env is required
|
||||
- Linux(mac)run bin/startup.sh
|
||||
- Windows run bin/startup.bat
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>yxwlkjnwct</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>0.1</version>
|
||||
<name>yxwlkjnwct</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.36.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<modules>
|
||||
<module>proxy-common</module>
|
||||
<module>proxy-protocol</module>
|
||||
<module>proxy-server</module>
|
||||
<module>proxy-client</module>
|
||||
</modules>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<configuration>
|
||||
<nonFilteredFileExtensions>
|
||||
<nonFilteredFileExtension>bat</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>sh</nonFilteredFileExtension>
|
||||
</nonFilteredFileExtensions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>yxwlkjnwct</artifactId>
|
||||
<version>0.1</version>
|
||||
</parent>
|
||||
<artifactId>proxy-client</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>proxy-client</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>proxy-common</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>proxy-protocol</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>proxy-client-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>../distribution/proxy-client-${project.version}/lib</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>*.properties</exclude>
|
||||
<exclude>*.sh</exclude>
|
||||
<exclude>*.bat</exclude>
|
||||
<exclude>*.jks</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-config</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-client-${project.version}/conf</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>*.properties</include>
|
||||
<include>*.jks</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-sh</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-client-${project.version}/bin</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>*.sh</include>
|
||||
<include>*.bat</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-dist-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-client-${project.version}/lib</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>target</directory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package org.fengfei.lanproxy.client;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.fengfei.lanproxy.client.listener.ProxyChannelBorrowListener;
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.fengfei.lanproxy.protocol.Constants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
/**
|
||||
* 代理客户端与后端真实服务器连接管理
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ClientChannelMannager {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ClientChannelMannager.class);
|
||||
|
||||
private static final AttributeKey<Boolean> USER_CHANNEL_WRITEABLE = AttributeKey.newInstance("user_channel_writeable");
|
||||
|
||||
private static final AttributeKey<Boolean> CLIENT_CHANNEL_WRITEABLE = AttributeKey.newInstance("client_channel_writeable");
|
||||
|
||||
private static final int MAX_POOL_SIZE = 100;
|
||||
|
||||
private static Map<String, Channel> realServerChannels = new ConcurrentHashMap<String, Channel>();
|
||||
|
||||
private static ConcurrentLinkedQueue<Channel> proxyChannelPool = new ConcurrentLinkedQueue<Channel>();
|
||||
|
||||
private static volatile Channel cmdChannel;
|
||||
|
||||
private static Config config = Config.getInstance();
|
||||
|
||||
public static void borrowProxyChanel(Bootstrap bootstrap, final ProxyChannelBorrowListener borrowListener) {
|
||||
Channel channel = proxyChannelPool.poll();
|
||||
if (channel != null) {
|
||||
borrowListener.success(channel);
|
||||
return;
|
||||
}
|
||||
|
||||
bootstrap.connect(config.getStringValue("server.host"), config.getIntValue("server.port")).addListener(new ChannelFutureListener() {
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
borrowListener.success(future.channel());
|
||||
} else {
|
||||
logger.warn("connect proxy server failed", future.cause());
|
||||
borrowListener.error(future.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void returnProxyChanel(Channel proxyChanel) {
|
||||
if (proxyChannelPool.size() > MAX_POOL_SIZE) {
|
||||
proxyChanel.close();
|
||||
} else {
|
||||
proxyChanel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
proxyChanel.attr(Constants.NEXT_CHANNEL).remove();
|
||||
proxyChannelPool.offer(proxyChanel);
|
||||
logger.debug("return ProxyChanel to the pool, channel is {}, pool size is {} ", proxyChanel, proxyChannelPool.size());
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeProxyChanel(Channel proxyChanel) {
|
||||
proxyChannelPool.remove(proxyChanel);
|
||||
}
|
||||
|
||||
public static void setCmdChannel(Channel cmdChannel) {
|
||||
ClientChannelMannager.cmdChannel = cmdChannel;
|
||||
}
|
||||
|
||||
public static Channel getCmdChannel() {
|
||||
return cmdChannel;
|
||||
}
|
||||
|
||||
public static void setRealServerChannelUserId(Channel realServerChannel, String userId) {
|
||||
realServerChannel.attr(Constants.USER_ID).set(userId);
|
||||
}
|
||||
|
||||
public static String getRealServerChannelUserId(Channel realServerChannel) {
|
||||
return realServerChannel.attr(Constants.USER_ID).get();
|
||||
}
|
||||
|
||||
public static Channel getRealServerChannel(String userId) {
|
||||
return realServerChannels.get(userId);
|
||||
}
|
||||
|
||||
public static void addRealServerChannel(String userId, Channel realServerChannel) {
|
||||
realServerChannels.put(userId, realServerChannel);
|
||||
}
|
||||
|
||||
public static Channel removeRealServerChannel(String userId) {
|
||||
return realServerChannels.remove(userId);
|
||||
}
|
||||
|
||||
public static boolean isRealServerReadable(Channel realServerChannel) {
|
||||
return realServerChannel.attr(CLIENT_CHANNEL_WRITEABLE).get() && realServerChannel.attr(USER_CHANNEL_WRITEABLE).get();
|
||||
}
|
||||
|
||||
public static void clearRealServerChannels() {
|
||||
logger.warn("channel closed, clear real server channels");
|
||||
|
||||
Iterator<Entry<String, Channel>> ite = realServerChannels.entrySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
Channel realServerChannel = ite.next().getValue();
|
||||
if (realServerChannel.isActive()) {
|
||||
realServerChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
realServerChannels.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
package org.fengfei.lanproxy.client;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.fengfei.lanproxy.client.handlers.ClientChannelHandler;
|
||||
import org.fengfei.lanproxy.client.handlers.RealServerChannelHandler;
|
||||
import org.fengfei.lanproxy.client.listener.ChannelStatusListener;
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.fengfei.lanproxy.common.container.Container;
|
||||
import org.fengfei.lanproxy.common.container.ContainerHelper;
|
||||
import org.fengfei.lanproxy.protocol.IdleCheckHandler;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessage;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessageDecoder;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessageEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
|
||||
public class ProxyClientContainer implements Container, ChannelStatusListener {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ProxyClientContainer.class);
|
||||
|
||||
private static final int MAX_FRAME_LENGTH = 1024 * 1024;
|
||||
|
||||
private static final int LENGTH_FIELD_OFFSET = 0;
|
||||
|
||||
private static final int LENGTH_FIELD_LENGTH = 4;
|
||||
|
||||
private static final int INITIAL_BYTES_TO_STRIP = 0;
|
||||
|
||||
private static final int LENGTH_ADJUSTMENT = 0;
|
||||
|
||||
private NioEventLoopGroup workerGroup;
|
||||
|
||||
private Bootstrap bootstrap;
|
||||
|
||||
private Bootstrap realServerBootstrap;
|
||||
|
||||
private Config config = Config.getInstance();
|
||||
|
||||
private SSLContext sslContext;
|
||||
|
||||
private long sleepTimeMill = 1000;
|
||||
|
||||
public ProxyClientContainer() {
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
realServerBootstrap = new Bootstrap();
|
||||
realServerBootstrap.group(workerGroup);
|
||||
realServerBootstrap.channel(NioSocketChannel.class);
|
||||
realServerBootstrap.handler(new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast(new RealServerChannelHandler());
|
||||
}
|
||||
});
|
||||
|
||||
bootstrap = new Bootstrap();
|
||||
bootstrap.group(workerGroup);
|
||||
bootstrap.channel(NioSocketChannel.class);
|
||||
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
if (Config.getInstance().getBooleanValue("ssl.enable", false)) {
|
||||
if (sslContext == null) {
|
||||
sslContext = SslContextCreator.createSSLContext();
|
||||
}
|
||||
|
||||
ch.pipeline().addLast(createSslHandler(sslContext));
|
||||
}
|
||||
ch.pipeline().addLast(new ProxyMessageDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
|
||||
ch.pipeline().addLast(new ProxyMessageEncoder());
|
||||
ch.pipeline().addLast(new IdleCheckHandler(IdleCheckHandler.READ_IDLE_TIME, IdleCheckHandler.WRITE_IDLE_TIME - 10, 0));
|
||||
ch.pipeline().addLast(new ClientChannelHandler(realServerBootstrap, bootstrap, ProxyClientContainer.this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
connectProxyServer();
|
||||
}
|
||||
|
||||
private ChannelHandler createSslHandler(SSLContext sslContext) {
|
||||
SSLEngine sslEngine = sslContext.createSSLEngine();
|
||||
sslEngine.setUseClientMode(true);
|
||||
return new SslHandler(sslEngine);
|
||||
}
|
||||
|
||||
private void connectProxyServer() {
|
||||
|
||||
bootstrap.connect(config.getStringValue("server.host"), config.getIntValue("server.port")).addListener(new ChannelFutureListener() {
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
|
||||
// 连接成功,向服务器发送客户端认证信息(clientKey)
|
||||
ClientChannelMannager.setCmdChannel(future.channel());
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.C_TYPE_AUTH);
|
||||
proxyMessage.setUri(config.getStringValue("client.key"));
|
||||
future.channel().writeAndFlush(proxyMessage);
|
||||
sleepTimeMill = 1000;
|
||||
logger.info("connect proxy server success, {}", future.channel());
|
||||
} else {
|
||||
logger.warn("connect proxy server failed", future.cause());
|
||||
|
||||
// 连接失败,发起重连
|
||||
reconnectWait();
|
||||
connectProxyServer();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
reconnectWait();
|
||||
connectProxyServer();
|
||||
}
|
||||
|
||||
private void reconnectWait() {
|
||||
try {
|
||||
if (sleepTimeMill > 60000) {
|
||||
sleepTimeMill = 1000;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
sleepTimeMill = sleepTimeMill * 2;
|
||||
wait(sleepTimeMill);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
ContainerHelper.start(Arrays.asList(new Container[] { new ProxyClientContainer() }));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package org.fengfei.lanproxy.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SslContextCreator {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SslContextCreator.class);
|
||||
|
||||
public static SSLContext createSSLContext() {
|
||||
return new SslContextCreator().initSSLContext();
|
||||
}
|
||||
|
||||
public SSLContext initSSLContext() {
|
||||
logger.info("Checking SSL configuration properties...");
|
||||
final String jksPath = Config.getInstance().getStringValue("ssl.jksPath");
|
||||
logger.info("Initializing SSL context. KeystorePath = {}.", jksPath);
|
||||
if (jksPath == null || jksPath.isEmpty()) {
|
||||
// key_store_password or key_manager_password are empty
|
||||
logger.warn("The keystore path is null or empty. The SSL context won't be initialized.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// if we have the port also the jks then keyStorePassword and
|
||||
// keyManagerPassword
|
||||
// has to be defined
|
||||
final String keyStorePassword = Config.getInstance().getStringValue("ssl.keyStorePassword");
|
||||
// if client authentification is enabled a trustmanager needs to be
|
||||
// added to the ServerContext
|
||||
|
||||
try {
|
||||
logger.info("Loading keystore. KeystorePath = {}.", jksPath);
|
||||
InputStream jksInputStream = jksDatastore(jksPath);
|
||||
SSLContext clientSSLContext = SSLContext.getInstance("TLS");
|
||||
final KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(jksInputStream, keyStorePassword.toCharArray());
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(ks);
|
||||
TrustManager[] trustManagers = tmf.getTrustManagers();
|
||||
|
||||
// init sslContext
|
||||
logger.info("Initializing SSL context...");
|
||||
clientSSLContext.init(null, trustManagers, null);
|
||||
logger.info("The SSL context has been initialized successfully.");
|
||||
|
||||
return clientSSLContext;
|
||||
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | KeyManagementException
|
||||
| IOException ex) {
|
||||
logger.error("Unable to initialize SSL context. Cause = {}, errorMessage = {}.", ex.getCause(),
|
||||
ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream jksDatastore(String jksPath) throws FileNotFoundException {
|
||||
URL jksUrl = getClass().getClassLoader().getResource(jksPath);
|
||||
if (jksUrl != null) {
|
||||
logger.info("Starting with jks at {}, jks normal {}", jksUrl.toExternalForm(), jksUrl);
|
||||
return getClass().getClassLoader().getResourceAsStream(jksPath);
|
||||
}
|
||||
|
||||
logger.warn("No keystore has been found in the bundled resources. Scanning filesystem...");
|
||||
File jksFile = new File(jksPath);
|
||||
if (jksFile.exists()) {
|
||||
logger.info("Loading external keystore. Url = {}.", jksFile.getAbsolutePath());
|
||||
return new FileInputStream(jksFile);
|
||||
}
|
||||
|
||||
logger.warn("The keystore file does not exist. Url = {}.", jksFile.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package org.fengfei.lanproxy.client.handlers;
|
||||
|
||||
import org.fengfei.lanproxy.client.ClientChannelMannager;
|
||||
import org.fengfei.lanproxy.client.listener.ChannelStatusListener;
|
||||
import org.fengfei.lanproxy.client.listener.ProxyChannelBorrowListener;
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.fengfei.lanproxy.protocol.Constants;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ClientChannelHandler extends SimpleChannelInboundHandler<ProxyMessage> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ClientChannelHandler.class);
|
||||
|
||||
private Bootstrap bootstrap;
|
||||
|
||||
private Bootstrap proxyBootstrap;
|
||||
|
||||
private ChannelStatusListener channelStatusListener;
|
||||
|
||||
public ClientChannelHandler(Bootstrap bootstrap, Bootstrap proxyBootstrap, ChannelStatusListener channelStatusListener) {
|
||||
this.bootstrap = bootstrap;
|
||||
this.proxyBootstrap = proxyBootstrap;
|
||||
this.channelStatusListener = channelStatusListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception {
|
||||
logger.debug("recieved proxy message, type is {}", proxyMessage.getType());
|
||||
switch (proxyMessage.getType()) {
|
||||
case ProxyMessage.TYPE_CONNECT:
|
||||
handleConnectMessage(ctx, proxyMessage);
|
||||
break;
|
||||
case ProxyMessage.TYPE_DISCONNECT:
|
||||
handleDisconnectMessage(ctx, proxyMessage);
|
||||
break;
|
||||
case ProxyMessage.P_TYPE_TRANSFER:
|
||||
handleTransferMessage(ctx, proxyMessage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTransferMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (realServerChannel != null) {
|
||||
ByteBuf buf = ctx.alloc().buffer(proxyMessage.getData().length);
|
||||
buf.writeBytes(proxyMessage.getData());
|
||||
logger.debug("write data to real server, {}", realServerChannel);
|
||||
realServerChannel.writeAndFlush(buf);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDisconnectMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
logger.debug("handleDisconnectMessage, {}", realServerChannel);
|
||||
if (realServerChannel != null) {
|
||||
ctx.channel().attr(Constants.NEXT_CHANNEL).remove();
|
||||
ClientChannelMannager.returnProxyChanel(ctx.channel());
|
||||
realServerChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnectMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
final Channel cmdChannel = ctx.channel();
|
||||
final String userId = proxyMessage.getUri();
|
||||
String[] serverInfo = new String(proxyMessage.getData()).split(":");
|
||||
String ip = serverInfo[0];
|
||||
int port = Integer.parseInt(serverInfo[1]);
|
||||
bootstrap.connect(ip, port).addListener(new ChannelFutureListener() {
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
|
||||
// 连接后端服务器成功
|
||||
if (future.isSuccess()) {
|
||||
final Channel realServerChannel = future.channel();
|
||||
logger.debug("connect realserver success, {}", realServerChannel);
|
||||
|
||||
realServerChannel.config().setOption(ChannelOption.AUTO_READ, false);
|
||||
|
||||
// 获取连接
|
||||
ClientChannelMannager.borrowProxyChanel(proxyBootstrap, new ProxyChannelBorrowListener() {
|
||||
|
||||
@Override
|
||||
public void success(Channel channel) {
|
||||
// 连接绑定
|
||||
channel.attr(Constants.NEXT_CHANNEL).set(realServerChannel);
|
||||
realServerChannel.attr(Constants.NEXT_CHANNEL).set(channel);
|
||||
|
||||
// 远程绑定
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_CONNECT);
|
||||
proxyMessage.setUri(userId + "@" + Config.getInstance().getStringValue("client.key"));
|
||||
channel.writeAndFlush(proxyMessage);
|
||||
|
||||
realServerChannel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
ClientChannelMannager.addRealServerChannel(userId, realServerChannel);
|
||||
ClientChannelMannager.setRealServerChannelUserId(realServerChannel, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Throwable cause) {
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_DISCONNECT);
|
||||
proxyMessage.setUri(userId);
|
||||
cmdChannel.writeAndFlush(proxyMessage);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_DISCONNECT);
|
||||
proxyMessage.setUri(userId);
|
||||
cmdChannel.writeAndFlush(proxyMessage);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (realServerChannel != null) {
|
||||
realServerChannel.config().setOption(ChannelOption.AUTO_READ, ctx.channel().isWritable());
|
||||
}
|
||||
|
||||
super.channelWritabilityChanged(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
// 控制连接
|
||||
if (ClientChannelMannager.getCmdChannel() == ctx.channel()) {
|
||||
ClientChannelMannager.setCmdChannel(null);
|
||||
ClientChannelMannager.clearRealServerChannels();
|
||||
channelStatusListener.channelInactive(ctx);
|
||||
} else {
|
||||
// 数据传输连接
|
||||
Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (realServerChannel != null && realServerChannel.isActive()) {
|
||||
realServerChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
ClientChannelMannager.removeProxyChanel(ctx.channel());
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
logger.error("exception caught", cause);
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package org.fengfei.lanproxy.client.handlers;
|
||||
|
||||
import org.fengfei.lanproxy.client.ClientChannelMannager;
|
||||
import org.fengfei.lanproxy.protocol.Constants;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
* 处理服务端 channel.
|
||||
*/
|
||||
public class RealServerChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(RealServerChannelHandler.class);
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
|
||||
Channel realServerChannel = ctx.channel();
|
||||
Channel channel = realServerChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (channel == null) {
|
||||
// 代理客户端连接断开
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
byte[] bytes = new byte[buf.readableBytes()];
|
||||
buf.readBytes(bytes);
|
||||
String userId = ClientChannelMannager.getRealServerChannelUserId(realServerChannel);
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.P_TYPE_TRANSFER);
|
||||
proxyMessage.setUri(userId);
|
||||
proxyMessage.setData(bytes);
|
||||
channel.writeAndFlush(proxyMessage);
|
||||
logger.debug("write data to proxy server, {}, {}", realServerChannel, channel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
Channel realServerChannel = ctx.channel();
|
||||
String userId = ClientChannelMannager.getRealServerChannelUserId(realServerChannel);
|
||||
ClientChannelMannager.removeRealServerChannel(userId);
|
||||
Channel channel = realServerChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (channel != null) {
|
||||
logger.debug("channelInactive, {}", realServerChannel);
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_DISCONNECT);
|
||||
proxyMessage.setUri(userId);
|
||||
channel.writeAndFlush(proxyMessage);
|
||||
}
|
||||
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
Channel realServerChannel = ctx.channel();
|
||||
Channel proxyChannel = realServerChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (proxyChannel != null) {
|
||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, realServerChannel.isWritable());
|
||||
}
|
||||
|
||||
super.channelWritabilityChanged(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
logger.error("exception caught", cause);
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.fengfei.lanproxy.client.listener;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
public interface ChannelStatusListener {
|
||||
|
||||
void channelInactive(ChannelHandlerContext ctx);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.fengfei.lanproxy.client.listener;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public interface ProxyChannelBorrowListener {
|
||||
|
||||
void success(Channel channel);
|
||||
|
||||
void error(Throwable cause);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#由服务端生成的key
|
||||
client.key=client
|
||||
#ssl是否开启,配置应与服务端一致
|
||||
ssl.enable=false
|
||||
ssl.jksPath=test.jks
|
||||
ssl.keyStorePassword=123456
|
||||
#服务端的ip
|
||||
server.host=127.0.0.1
|
||||
#服务端的端口
|
||||
#default ssl port is 4993
|
||||
server.port=4900
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
log4j.rootLogger=info,R
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.R.DatePattern='_'yyyy-MM-dd'.log'
|
||||
log4j.appender.R.File=${app.home}/logs/client.log
|
||||
log4j.appender.R.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.R.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.io.netty=warn
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
@echo off & setlocal enabledelayedexpansion
|
||||
title lanproxy-client
|
||||
cd %~dp0
|
||||
|
||||
set LIB_JARS=""
|
||||
cd ..\lib
|
||||
for %%i in (*) do set LIB_JARS=!LIB_JARS!;..\lib\%%i
|
||||
cd ..\bin
|
||||
|
||||
java -Dapp.home=../ -Xms64m -Xmx1024m -classpath ..\conf;%LIB_JARS% org.fengfei.lanproxy.client.ProxyClientContainer
|
||||
goto end
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
CONF_DIR=$DEPLOY_DIR/conf
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
|
||||
APP_MAINCLASS=org.fengfei.lanproxy.client.ProxyClientContainer
|
||||
|
||||
PIDS=`ps -ef | grep -v grep | grep "$CONF_DIR" |awk '{print $2}'`
|
||||
if [ -n "$PIDS" ]; then
|
||||
echo "ERROR: already started!"
|
||||
echo "PID: $PIDS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
CLOG_FILE=$LOGS_DIR/gc.log
|
||||
|
||||
LIB_DIR=$DEPLOY_DIR/lib
|
||||
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'| xargs | sed "s/ /:/g"`
|
||||
|
||||
JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
|
||||
JAVA_DEBUG_OPTS=""
|
||||
if [ "$1" = "debug" ]; then
|
||||
JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
|
||||
fi
|
||||
JAVA_JMX_OPTS=""
|
||||
if [ "$1" = "jmx" ]; then
|
||||
JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
|
||||
fi
|
||||
|
||||
JAVA_MEM_OPTS=""
|
||||
#JAVA_MEM_OPTS="-server -Xms5120M -Xmx5120M -Xmn1024M -Xnoclassgc -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:$CLOG_FILE"
|
||||
echo -e "Starting the proxy client ...\c"
|
||||
nohup java -Dapp.home=$DEPLOY_DIR $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS $APP_MAINCLASS >$STDOUT_FILE 2>&1 &
|
||||
sleep 1
|
||||
echo "started"
|
||||
PIDS=`ps -ef | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'`
|
||||
echo "PID: $PIDS"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
BIN_DIR=`pwd`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
|
||||
PID=`ps -ef | grep -v grep | grep "$DEPLOY_DIR/conf" | awk '{print $2}'`
|
||||
echo "PID: $PID"
|
||||
if [ -z "$PID" ]; then
|
||||
echo "ERROR: The proxy client does not started!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Stopping the proxy client...\c"
|
||||
kill $PID > $STDOUT_FILE 2>&1
|
||||
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 1 ]; do
|
||||
echo -e ".\c"
|
||||
sleep 1
|
||||
COUNT=1
|
||||
PID_EXIST=`ps -f -p $PID | grep java`
|
||||
if [ -n "$PID_EXIST" ]; then
|
||||
COUNT=0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "stopped"
|
||||
echo "PID: $PID"
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,15 @@
|
|||
package org.fengfei.lanparoxy.client.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.fengfei.lanproxy.client.ProxyClientContainer;
|
||||
import org.fengfei.lanproxy.common.container.Container;
|
||||
import org.fengfei.lanproxy.common.container.ContainerHelper;
|
||||
|
||||
public class TestMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ContainerHelper.start(Arrays.asList(new Container[] { new ProxyClientContainer() }));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
client.key=f49bfbca84e646a5ba4ce51ad9d8c14e
|
||||
ssl.enable=false
|
||||
ssl.jksPath=test.jks
|
||||
ssl.keyStorePassword=123456
|
||||
|
||||
server.host=127.0.0.1
|
||||
|
||||
#default ssl port is 4993
|
||||
server.port=4900
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
log4j.rootLogger=debug,stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.io.netty=warn
|
||||
log4j.logger.org.fengfei.lanproxy.client.handlers.ClientChannelHandler=info
|
||||
log4j.logger.org.fengfei.lanproxy.protocol.IdleCheckHandler=warn
|
||||
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
|||
#由服务端生成的key
|
||||
client.key=client
|
||||
#ssl是否开启,配置应与服务端一致
|
||||
ssl.enable=false
|
||||
ssl.jksPath=test.jks
|
||||
ssl.keyStorePassword=123456
|
||||
#服务端的ip
|
||||
server.host=127.0.0.1
|
||||
#服务端的端口
|
||||
#default ssl port is 4993
|
||||
server.port=4900
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
log4j.rootLogger=info,R
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.R.DatePattern='_'yyyy-MM-dd'.log'
|
||||
log4j.appender.R.File=${app.home}/logs/client.log
|
||||
log4j.appender.R.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.R.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.io.netty=warn
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
@echo off & setlocal enabledelayedexpansion
|
||||
title lanproxy-client
|
||||
cd %~dp0
|
||||
|
||||
set LIB_JARS=""
|
||||
cd ..\lib
|
||||
for %%i in (*) do set LIB_JARS=!LIB_JARS!;..\lib\%%i
|
||||
cd ..\bin
|
||||
|
||||
java -Dapp.home=../ -Xms64m -Xmx1024m -classpath ..\conf;%LIB_JARS% org.fengfei.lanproxy.client.ProxyClientContainer
|
||||
goto end
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
CONF_DIR=$DEPLOY_DIR/conf
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
|
||||
APP_MAINCLASS=org.fengfei.lanproxy.client.ProxyClientContainer
|
||||
|
||||
PIDS=`ps -ef | grep -v grep | grep "$CONF_DIR" |awk '{print $2}'`
|
||||
if [ -n "$PIDS" ]; then
|
||||
echo "ERROR: already started!"
|
||||
echo "PID: $PIDS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
CLOG_FILE=$LOGS_DIR/gc.log
|
||||
|
||||
LIB_DIR=$DEPLOY_DIR/lib
|
||||
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'| xargs | sed "s/ /:/g"`
|
||||
|
||||
JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
|
||||
JAVA_DEBUG_OPTS=""
|
||||
if [ "$1" = "debug" ]; then
|
||||
JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
|
||||
fi
|
||||
JAVA_JMX_OPTS=""
|
||||
if [ "$1" = "jmx" ]; then
|
||||
JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
|
||||
fi
|
||||
|
||||
JAVA_MEM_OPTS=""
|
||||
#JAVA_MEM_OPTS="-server -Xms5120M -Xmx5120M -Xmn1024M -Xnoclassgc -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:$CLOG_FILE"
|
||||
echo -e "Starting the proxy client ...\c"
|
||||
nohup java -Dapp.home=$DEPLOY_DIR $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS $APP_MAINCLASS >$STDOUT_FILE 2>&1 &
|
||||
sleep 1
|
||||
echo "started"
|
||||
PIDS=`ps -ef | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'`
|
||||
echo "PID: $PIDS"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
BIN_DIR=`pwd`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
|
||||
PID=`ps -ef | grep -v grep | grep "$DEPLOY_DIR/conf" | awk '{print $2}'`
|
||||
echo "PID: $PID"
|
||||
if [ -z "$PID" ]; then
|
||||
echo "ERROR: The proxy client does not started!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Stopping the proxy client...\c"
|
||||
kill $PID > $STDOUT_FILE 2>&1
|
||||
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 1 ]; do
|
||||
echo -e ".\c"
|
||||
sleep 1
|
||||
COUNT=1
|
||||
PID_EXIST=`ps -f -p $PID | grep java`
|
||||
if [ -n "$PID_EXIST" ]; then
|
||||
COUNT=0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "stopped"
|
||||
echo "PID: $PID"
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
|||
#Generated by Maven
|
||||
#Wed Jul 05 11:03:49 CST 2023
|
||||
groupId=org.fengfei
|
||||
artifactId=proxy-client
|
||||
version=0.1
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
org\fengfei\lanproxy\client\handlers\ClientChannelHandler$1$1.class
|
||||
org\fengfei\lanproxy\client\handlers\ClientChannelHandler$1.class
|
||||
org\fengfei\lanproxy\client\ClientChannelMannager.class
|
||||
org\fengfei\lanproxy\client\ProxyClientContainer.class
|
||||
org\fengfei\lanproxy\client\ClientChannelMannager$1.class
|
||||
org\fengfei\lanproxy\client\handlers\RealServerChannelHandler.class
|
||||
org\fengfei\lanproxy\client\listener\ChannelStatusListener.class
|
||||
org\fengfei\lanproxy\client\SslContextCreator.class
|
||||
org\fengfei\lanproxy\client\ProxyClientContainer$3.class
|
||||
org\fengfei\lanproxy\client\handlers\ClientChannelHandler.class
|
||||
org\fengfei\lanproxy\client\listener\ProxyChannelBorrowListener.class
|
||||
org\fengfei\lanproxy\client\ProxyClientContainer$1.class
|
||||
org\fengfei\lanproxy\client\ProxyClientContainer$2.class
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\ProxyClientContainer.java
|
||||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\handlers\ClientChannelHandler.java
|
||||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\listener\ProxyChannelBorrowListener.java
|
||||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\listener\ChannelStatusListener.java
|
||||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\SslContextCreator.java
|
||||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\ClientChannelMannager.java
|
||||
E:\yx\lanproxy-0.1\proxy-client\src\main\java\org\fengfei\lanproxy\client\handlers\RealServerChannelHandler.java
|
||||
|
|
@ -0,0 +1 @@
|
|||
org\fengfei\lanparoxy\client\test\TestMain.class
|
||||
|
|
@ -0,0 +1 @@
|
|||
E:\yx\lanproxy-0.1\proxy-client\src\test\java\org\fengfei\lanparoxy\client\test\TestMain.java
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
client.key=f49bfbca84e646a5ba4ce51ad9d8c14e
|
||||
ssl.enable=false
|
||||
ssl.jksPath=test.jks
|
||||
ssl.keyStorePassword=123456
|
||||
|
||||
server.host=127.0.0.1
|
||||
|
||||
#default ssl port is 4993
|
||||
server.port=4900
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
log4j.rootLogger=debug,stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.io.netty=warn
|
||||
log4j.logger.org.fengfei.lanproxy.client.handlers.ClientChannelHandler=info
|
||||
log4j.logger.org.fengfei.lanproxy.protocol.IdleCheckHandler=warn
|
||||
Binary file not shown.
|
|
@ -0,0 +1,20 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>yxwlkjnwct</artifactId>
|
||||
<version>0.1</version>
|
||||
</parent>
|
||||
<artifactId>proxy-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>proxy-common</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package org.fengfei.lanproxy.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 读取配置文件 默认的config.properties 和自定义都支持
|
||||
*
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
private static final String DEFAULT_CONF = "config.properties";
|
||||
|
||||
private static Map<String, Config> instances = new ConcurrentHashMap<String, Config>();
|
||||
|
||||
private Properties configuration = new Properties();
|
||||
|
||||
private Config() {
|
||||
initConfig(DEFAULT_CONF);
|
||||
}
|
||||
|
||||
private Config(String configFile) {
|
||||
initConfig(configFile);
|
||||
}
|
||||
|
||||
private void initConfig(String configFile) {
|
||||
InputStream is = Config.class.getClassLoader().getResourceAsStream(configFile);
|
||||
try {
|
||||
configuration.load(is);
|
||||
is.close();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Configuration实例。 默认为config.property
|
||||
*
|
||||
* @return Configuration实例
|
||||
*/
|
||||
public static Config getInstance() {
|
||||
return getInstance(DEFAULT_CONF);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义文件解析**.property
|
||||
*
|
||||
* @param configFile
|
||||
* @return
|
||||
*/
|
||||
public static Config getInstance(String configFile) {
|
||||
Config config = instances.get(configFile);
|
||||
if (config == null) {
|
||||
synchronized (instances) {
|
||||
config = instances.get(configFile);
|
||||
if (config == null) {
|
||||
config = new Config(configFile);
|
||||
instances.put(configFile, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得配置项。
|
||||
*
|
||||
* @param key 配置关键字
|
||||
*
|
||||
* @return 配置项
|
||||
*/
|
||||
public String getStringValue(String key) {
|
||||
return configuration.getProperty(key);
|
||||
}
|
||||
|
||||
public String getStringValue(String key, String defaultValue) {
|
||||
String value = this.getStringValue(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIntValue(String key, int defaultValue) {
|
||||
return LangUtil.parseInt(configuration.getProperty(key), defaultValue);
|
||||
}
|
||||
|
||||
public int getIntValue(String key) {
|
||||
return LangUtil.parseInt(configuration.getProperty(key));
|
||||
}
|
||||
|
||||
public double getDoubleValue(String key, Double defaultValue) {
|
||||
return LangUtil.parseDouble(configuration.getProperty(key), defaultValue);
|
||||
}
|
||||
|
||||
public double getDoubleValue(String key) {
|
||||
return LangUtil.parseDouble(configuration.getProperty(key));
|
||||
}
|
||||
|
||||
public double getLongValue(String key, Long defaultValue) {
|
||||
return LangUtil.parseLong(configuration.getProperty(key), defaultValue);
|
||||
}
|
||||
|
||||
public double getLongValue(String key) {
|
||||
return LangUtil.parseLong(configuration.getProperty(key));
|
||||
}
|
||||
|
||||
public Boolean getBooleanValue(String key, Boolean defaultValue) {
|
||||
return LangUtil.parseBoolean(configuration.getProperty(key), defaultValue);
|
||||
}
|
||||
|
||||
public Boolean getBooleanValue(String key) {
|
||||
return LangUtil.parseBoolean(configuration.getProperty(key));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package org.fengfei.lanproxy.common;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* JSON与POJO转换工具类.
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class JsonUtil {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T json2object(String json, TypeToken<T> typeToken) {
|
||||
try {
|
||||
Gson gson = new Gson();
|
||||
return (T) gson.fromJson(json, typeToken.getType());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* java对象转为json对象
|
||||
*
|
||||
*/
|
||||
public static String object2json(Object obj) {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
package org.fengfei.lanproxy.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* 类型转换工具类.
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class LangUtil {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(LangUtil.class);
|
||||
|
||||
public static Boolean parseBoolean(Object value) {
|
||||
if (value != null) {
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
} else if (value instanceof String) {
|
||||
return Boolean.valueOf((String) value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean parseBoolean(Object value, boolean defaultValue) {
|
||||
if (value != null) {
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Boolean.valueOf((String) value);
|
||||
} catch (Exception e) {
|
||||
logger.warn("parse boolean value({}) failed.", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: parseInt
|
||||
* @Description: Int解析方法,可传入Integer或String值
|
||||
* @param value Integer或String值
|
||||
* @return Integer 返回类型
|
||||
*/
|
||||
public static Integer parseInt(Object value) {
|
||||
if (value != null) {
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
} else if (value instanceof String) {
|
||||
return Integer.valueOf((String) value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Integer parseInt(Object value, Integer defaultValue) {
|
||||
if (value != null) {
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Integer.valueOf((String) value);
|
||||
} catch (Exception e) {
|
||||
logger.warn("parse Integer value({}) failed.", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @Title: parseLong
|
||||
* @Description: long解析方法,可传入Long或String值
|
||||
* @param value Integer或String值
|
||||
* @param @return
|
||||
* @return Long 返回类型
|
||||
*/
|
||||
public static Long parseLong(Object value) {
|
||||
if (value != null) {
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
} else if (value instanceof String) {
|
||||
return Long.valueOf((String) value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Long parseLong(Object value, Long defaultValue) {
|
||||
if (value != null) {
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Long.valueOf((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("parse Long value({}) failed.", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: parseDouble
|
||||
* @Description: Double解析方法,可传入Double或String值
|
||||
* @param value Double或String值
|
||||
* @return Double 返回类型
|
||||
*/
|
||||
public static Double parseDouble(Object value) {
|
||||
if (value != null) {
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
} else if (value instanceof String) {
|
||||
return Double.valueOf((String) value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: parseDouble
|
||||
* @Description: Double解析方法,可传入Double或String值
|
||||
* @param value Double或String值
|
||||
* @return Double 返回类型
|
||||
*/
|
||||
public static Double parseDouble(Object value, Double defaultValue) {
|
||||
if (value != null) {
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Double.valueOf((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("parse Double value({}) failed.", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.fengfei.lanproxy.common.container;
|
||||
|
||||
public interface Container {
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package org.fengfei.lanproxy.common.container;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 容器启动工具类.
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ContainerHelper {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ContainerHelper.class);
|
||||
|
||||
private static volatile boolean running = true;
|
||||
|
||||
private static List<Container> cachedContainers;
|
||||
|
||||
public static void start(List<Container> containers) {
|
||||
|
||||
cachedContainers = containers;
|
||||
|
||||
// 启动所有容器
|
||||
startContainers();
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
synchronized (ContainerHelper.class) {
|
||||
|
||||
// 停止所有容器.
|
||||
stopContainers();
|
||||
running = false;
|
||||
ContainerHelper.class.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
synchronized (ContainerHelper.class) {
|
||||
while (running) {
|
||||
try {
|
||||
ContainerHelper.class.wait();
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void startContainers() {
|
||||
for (Container container : cachedContainers) {
|
||||
logger.info("starting container [{}]", container.getClass().getName());
|
||||
container.start();
|
||||
logger.info("container [{}] started", container.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static void stopContainers() {
|
||||
for (Container container : cachedContainers) {
|
||||
logger.info("stopping container [{}]", container.getClass().getName());
|
||||
try {
|
||||
container.stop();
|
||||
logger.info("container [{}] stopped", container.getClass().getName());
|
||||
} catch (Exception ex) {
|
||||
logger.warn("container stopped with error", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#Generated by Maven
|
||||
#Wed Jul 05 11:03:47 CST 2023
|
||||
groupId=org.fengfei
|
||||
artifactId=proxy-common
|
||||
version=0.1
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
org\fengfei\lanproxy\common\container\Container.class
|
||||
org\fengfei\lanproxy\common\JsonUtil.class
|
||||
org\fengfei\lanproxy\common\LangUtil.class
|
||||
org\fengfei\lanproxy\common\container\ContainerHelper$1.class
|
||||
org\fengfei\lanproxy\common\Config.class
|
||||
org\fengfei\lanproxy\common\container\ContainerHelper.class
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
E:\yx\lanproxy-0.1\proxy-common\src\main\java\org\fengfei\lanproxy\common\LangUtil.java
|
||||
E:\yx\lanproxy-0.1\proxy-common\src\main\java\org\fengfei\lanproxy\common\container\Container.java
|
||||
E:\yx\lanproxy-0.1\proxy-common\src\main\java\org\fengfei\lanproxy\common\container\ContainerHelper.java
|
||||
E:\yx\lanproxy-0.1\proxy-common\src\main\java\org\fengfei\lanproxy\common\Config.java
|
||||
E:\yx\lanproxy-0.1\proxy-common\src\main\java\org\fengfei\lanproxy\common\JsonUtil.java
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package org.fengfei.lanproxy.server;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
public interface Constants {
|
||||
|
||||
public static final AttributeKey<Channel> NEXT_CHANNEL = AttributeKey.newInstance("nxt_channel");
|
||||
|
||||
public static final AttributeKey<String> USER_ID = AttributeKey.newInstance("user_id");
|
||||
|
||||
public static final AttributeKey<String> CLIENT_KEY = AttributeKey.newInstance("client_key");
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>yxwlkjnwct</artifactId>
|
||||
<version>0.1</version>
|
||||
</parent>
|
||||
<artifactId>proxy-protocol</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>proxy-protocol</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package org.fengfei.lanproxy.protocol;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
public interface Constants {
|
||||
|
||||
public static final AttributeKey<Channel> NEXT_CHANNEL = AttributeKey.newInstance("nxt_channel");
|
||||
|
||||
public static final AttributeKey<String> USER_ID = AttributeKey.newInstance("user_id");
|
||||
|
||||
public static final AttributeKey<String> CLIENT_KEY = AttributeKey.newInstance("client_key");
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package org.fengfei.lanproxy.protocol;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
/**
|
||||
* check idle chanel.
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class IdleCheckHandler extends IdleStateHandler {
|
||||
|
||||
public static final int USER_CHANNEL_READ_IDLE_TIME = 1200;
|
||||
|
||||
public static final int READ_IDLE_TIME = 60;
|
||||
|
||||
public static final int WRITE_IDLE_TIME = 40;
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(IdleCheckHandler.class);
|
||||
|
||||
public IdleCheckHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) {
|
||||
super(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
|
||||
|
||||
if (IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT == evt) {
|
||||
logger.debug("channel write timeout {}", ctx.channel());
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_HEARTBEAT);
|
||||
ctx.channel().writeAndFlush(proxyMessage);
|
||||
} else if (IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT == evt) {
|
||||
logger.warn("channel read timeout {}", ctx.channel());
|
||||
ctx.channel().close();
|
||||
}
|
||||
super.channelIdle(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package org.fengfei.lanproxy.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 代理客户端与代理服务器消息交换协议
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ProxyMessage {
|
||||
|
||||
/** 心跳消息 */
|
||||
public static final byte TYPE_HEARTBEAT = 0x07;
|
||||
|
||||
/** 认证消息,检测clientKey是否正确 */
|
||||
public static final byte C_TYPE_AUTH = 0x01;
|
||||
|
||||
// /** 保活确认消息 */
|
||||
// public static final byte TYPE_ACK = 0x02;
|
||||
|
||||
/** 代理后端服务器建立连接消息 */
|
||||
public static final byte TYPE_CONNECT = 0x03;
|
||||
|
||||
/** 代理后端服务器断开连接消息 */
|
||||
public static final byte TYPE_DISCONNECT = 0x04;
|
||||
|
||||
/** 代理数据传输 */
|
||||
public static final byte P_TYPE_TRANSFER = 0x05;
|
||||
|
||||
/** 用户与代理服务器以及代理客户端与真实服务器连接是否可写状态同步 */
|
||||
public static final byte C_TYPE_WRITE_CONTROL = 0x06;
|
||||
|
||||
/** 消息类型 */
|
||||
private byte type;
|
||||
|
||||
/** 消息流水号 */
|
||||
private long serialNumber;
|
||||
|
||||
/** 消息命令请求信息 */
|
||||
private String uri;
|
||||
|
||||
/** 消息传输数据 */
|
||||
private byte[] data;
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public byte getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(byte type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
public void setSerialNumber(long serialNumber) {
|
||||
this.serialNumber = serialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProxyMessage [type=" + type + ", serialNumber=" + serialNumber + ", uri=" + uri + ", data=" + Arrays.toString(data) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package org.fengfei.lanproxy.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
|
||||
public class ProxyMessageDecoder extends LengthFieldBasedFrameDecoder {
|
||||
|
||||
private static final byte HEADER_SIZE = 4;
|
||||
|
||||
private static final int TYPE_SIZE = 1;
|
||||
|
||||
private static final int SERIAL_NUMBER_SIZE = 8;
|
||||
|
||||
private static final int URI_LENGTH_SIZE = 1;
|
||||
|
||||
/**
|
||||
* @param maxFrameLength
|
||||
* @param lengthFieldOffset
|
||||
* @param lengthFieldLength
|
||||
* @param lengthAdjustment
|
||||
* @param initialBytesToStrip
|
||||
*/
|
||||
public ProxyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment,
|
||||
int initialBytesToStrip) {
|
||||
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxFrameLength
|
||||
* @param lengthFieldOffset
|
||||
* @param lengthFieldLength
|
||||
* @param lengthAdjustment
|
||||
* @param initialBytesToStrip
|
||||
* @param failFast
|
||||
*/
|
||||
public ProxyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment,
|
||||
int initialBytesToStrip, boolean failFast) {
|
||||
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProxyMessage decode(ChannelHandlerContext ctx, ByteBuf in2) throws Exception {
|
||||
ByteBuf in = (ByteBuf) super.decode(ctx, in2);
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in.readableBytes() < HEADER_SIZE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int frameLength = in.readInt();
|
||||
if (in.readableBytes() < frameLength) {
|
||||
return null;
|
||||
}
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
byte type = in.readByte();
|
||||
long sn = in.readLong();
|
||||
|
||||
proxyMessage.setSerialNumber(sn);
|
||||
|
||||
proxyMessage.setType(type);
|
||||
|
||||
byte uriLength = in.readByte();
|
||||
byte[] uriBytes = new byte[uriLength];
|
||||
in.readBytes(uriBytes);
|
||||
proxyMessage.setUri(new String(uriBytes));
|
||||
|
||||
byte[] data = new byte[frameLength - TYPE_SIZE - SERIAL_NUMBER_SIZE - URI_LENGTH_SIZE - uriLength];
|
||||
in.readBytes(data);
|
||||
proxyMessage.setData(data);
|
||||
|
||||
in.release();
|
||||
|
||||
return proxyMessage;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.fengfei.lanproxy.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
public class ProxyMessageEncoder extends MessageToByteEncoder<ProxyMessage> {
|
||||
|
||||
private static final int TYPE_SIZE = 1;
|
||||
|
||||
private static final int SERIAL_NUMBER_SIZE = 8;
|
||||
|
||||
private static final int URI_LENGTH_SIZE = 1;
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ProxyMessage msg, ByteBuf out) throws Exception {
|
||||
int bodyLength = TYPE_SIZE + SERIAL_NUMBER_SIZE + URI_LENGTH_SIZE;
|
||||
byte[] uriBytes = null;
|
||||
if (msg.getUri() != null) {
|
||||
uriBytes = msg.getUri().getBytes();
|
||||
bodyLength += uriBytes.length;
|
||||
}
|
||||
|
||||
if (msg.getData() != null) {
|
||||
bodyLength += msg.getData().length;
|
||||
}
|
||||
|
||||
// write the total packet length but without length field's length.
|
||||
out.writeInt(bodyLength);
|
||||
|
||||
out.writeByte(msg.getType());
|
||||
out.writeLong(msg.getSerialNumber());
|
||||
|
||||
if (uriBytes != null) {
|
||||
out.writeByte((byte) uriBytes.length);
|
||||
out.writeBytes(uriBytes);
|
||||
} else {
|
||||
out.writeByte((byte) 0x00);
|
||||
}
|
||||
|
||||
if (msg.getData() != null) {
|
||||
out.writeBytes(msg.getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#Generated by Maven
|
||||
#Wed Jul 05 11:03:47 CST 2023
|
||||
groupId=org.fengfei
|
||||
artifactId=proxy-protocol
|
||||
version=0.1
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
org\fengfei\lanproxy\protocol\Constants.class
|
||||
org\fengfei\lanproxy\protocol\ProxyMessage.class
|
||||
org\fengfei\lanproxy\protocol\IdleCheckHandler.class
|
||||
org\fengfei\lanproxy\protocol\ProxyMessageEncoder.class
|
||||
org\fengfei\lanproxy\protocol\ProxyMessageDecoder.class
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
E:\yx\lanproxy-0.1\proxy-protocol\src\main\java\org\fengfei\lanproxy\protocol\Constants.java
|
||||
E:\yx\lanproxy-0.1\proxy-protocol\src\main\java\org\fengfei\lanproxy\protocol\ProxyMessageEncoder.java
|
||||
E:\yx\lanproxy-0.1\proxy-protocol\src\main\java\org\fengfei\lanproxy\protocol\ProxyMessageDecoder.java
|
||||
E:\yx\lanproxy-0.1\proxy-protocol\src\main\java\org\fengfei\lanproxy\protocol\ProxyMessage.java
|
||||
E:\yx\lanproxy-0.1\proxy-protocol\src\main\java\org\fengfei\lanproxy\protocol\IdleCheckHandler.java
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>yxwlkjnwct</artifactId>
|
||||
<version>0.1</version>
|
||||
</parent>
|
||||
<artifactId>proxy-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>proxy-server</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>proxy-common</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fengfei</groupId>
|
||||
<artifactId>proxy-protocol</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>proxy-server-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>*.properties</exclude>
|
||||
<exclude>*.sh</exclude>
|
||||
<exclude>*.bat</exclude>
|
||||
<exclude>*.jks</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>../distribution/proxy-server-${project.version}/lib</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-config</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-server-${project.version}/conf</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>*.properties</include>
|
||||
<include>*.json</include>
|
||||
<include>*.jks</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-webpages</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-server-${project.version}/webpages</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>webpages</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-sh</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-server-${project.version}/bin</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>*.sh</include>
|
||||
<include>*.bat</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-dist-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<outputDirectory>../distribution/proxy-server-${project.version}/lib</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>target</directory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
package org.fengfei.lanproxy.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.fengfei.lanproxy.protocol.Constants;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig.ConfigChangedListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
/**
|
||||
* 代理服务连接管理(代理客户端连接+用户请求连接)
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ProxyChannelManager {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ProxyChannelManager.class);
|
||||
|
||||
private static final AttributeKey<Map<String, Channel>> USER_CHANNELS = AttributeKey.newInstance("user_channels");
|
||||
|
||||
private static final AttributeKey<String> REQUEST_LAN_INFO = AttributeKey.newInstance("request_lan_info");
|
||||
|
||||
private static final AttributeKey<List<Integer>> CHANNEL_PORT = AttributeKey.newInstance("channel_port");
|
||||
|
||||
private static final AttributeKey<String> CHANNEL_CLIENT_KEY = AttributeKey.newInstance("channel_client_key");
|
||||
|
||||
private static Map<Integer, Channel> portCmdChannelMapping = new ConcurrentHashMap<Integer, Channel>();
|
||||
|
||||
private static Map<String, Channel> cmdChannels = new ConcurrentHashMap<String, Channel>();
|
||||
|
||||
static {
|
||||
ProxyConfig.getInstance().addConfigChangedListener(new ConfigChangedListener() {
|
||||
|
||||
/**
|
||||
* 代理配置发生变化时回调
|
||||
*/
|
||||
@Override
|
||||
public synchronized void onChanged() {
|
||||
Iterator<Entry<String, Channel>> ite = cmdChannels.entrySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
Channel proxyChannel = ite.next().getValue();
|
||||
String clientKey = proxyChannel.attr(CHANNEL_CLIENT_KEY).get();
|
||||
|
||||
// 去除已经去掉的clientKey配置
|
||||
Set<String> clientKeySet = ProxyConfig.getInstance().getClientKeySet();
|
||||
if (!clientKeySet.contains(clientKey)) {
|
||||
removeCmdChannel(proxyChannel);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (proxyChannel.isActive()) {
|
||||
List<Integer> inetPorts = new ArrayList<Integer>(ProxyConfig.getInstance().getClientInetPorts(clientKey));
|
||||
Set<Integer> inetPortSet = new HashSet<Integer>(inetPorts);
|
||||
List<Integer> channelInetPorts = new ArrayList<Integer>(proxyChannel.attr(CHANNEL_PORT).get());
|
||||
|
||||
synchronized (portCmdChannelMapping) {
|
||||
|
||||
// 移除旧的连接映射关系
|
||||
for (int chanelInetPort : channelInetPorts) {
|
||||
Channel channel = portCmdChannelMapping.get(chanelInetPort);
|
||||
if (channel == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断是否是同一个连接对象,有可能之前已经更换成其他client的连接了
|
||||
if (proxyChannel == channel) {
|
||||
if (!inetPortSet.contains(chanelInetPort)) {
|
||||
|
||||
// 移除新配置中不包含的端口
|
||||
portCmdChannelMapping.remove(chanelInetPort);
|
||||
proxyChannel.attr(CHANNEL_PORT).get().remove(new Integer(chanelInetPort));
|
||||
} else {
|
||||
|
||||
// 端口已经在改proxyChannel中使用了
|
||||
inetPorts.remove(new Integer(chanelInetPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将新配置中增加的外网端口写入到映射配置中
|
||||
for (int inetPort : inetPorts) {
|
||||
portCmdChannelMapping.put(inetPort, proxyChannel);
|
||||
proxyChannel.attr(CHANNEL_PORT).get().add(inetPort);
|
||||
}
|
||||
|
||||
checkAndClearUserChannels(proxyChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ite = cmdChannels.entrySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
Entry<String, Channel> entry = ite.next();
|
||||
Channel proxyChannel = entry.getValue();
|
||||
logger.info("proxyChannel config, {}, {}, {} ,{}", entry.getKey(), proxyChannel, getUserChannels(proxyChannel).size(), proxyChannel.attr(CHANNEL_PORT).get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测连接配置是否与当前配置一致,不一致则关闭
|
||||
*
|
||||
* @param proxyChannel
|
||||
*/
|
||||
private void checkAndClearUserChannels(Channel proxyChannel) {
|
||||
Map<String, Channel> userChannels = getUserChannels(proxyChannel);
|
||||
Iterator<Entry<String, Channel>> userChannelIte = userChannels.entrySet().iterator();
|
||||
while (userChannelIte.hasNext()) {
|
||||
Entry<String, Channel> entry = userChannelIte.next();
|
||||
Channel userChannel = entry.getValue();
|
||||
String requestLanInfo = getUserChannelRequestLanInfo(userChannel);
|
||||
InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
|
||||
String lanInfo = ProxyConfig.getInstance().getLanInfo(sa.getPort());
|
||||
|
||||
// 判断当前配置中对应外网端口的lan信息是否与正在运行的连接中的lan信息是否一致
|
||||
if (lanInfo == null || !lanInfo.equals(requestLanInfo)) {
|
||||
userChannel.close();
|
||||
|
||||
// ConcurrentHashMap不会报ConcurrentModificationException异常
|
||||
userChannels.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加代理服务器端口与代理控制客户端连接的映射关系
|
||||
*
|
||||
* @param ports
|
||||
* @param channel
|
||||
*/
|
||||
public static void addCmdChannel(List<Integer> ports, String clientKey, Channel channel) {
|
||||
|
||||
if (ports == null) {
|
||||
throw new IllegalArgumentException("port can not be null");
|
||||
}
|
||||
|
||||
// 客户端(proxy-client)相对较少,这里同步的比较重
|
||||
// 保证服务器对外端口与客户端到服务器的连接关系在临界情况时调用removeChannel(Channel channel)时不出问题
|
||||
synchronized (portCmdChannelMapping) {
|
||||
for (int port : ports) {
|
||||
portCmdChannelMapping.put(port, channel);
|
||||
}
|
||||
}
|
||||
|
||||
channel.attr(CHANNEL_PORT).set(ports);
|
||||
channel.attr(CHANNEL_CLIENT_KEY).set(clientKey);
|
||||
channel.attr(USER_CHANNELS).set(new ConcurrentHashMap<String, Channel>());
|
||||
cmdChannels.put(clientKey, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理客户端连接断开后清除关系
|
||||
*
|
||||
* @param channel
|
||||
*/
|
||||
public static void removeCmdChannel(Channel channel) {
|
||||
logger.warn("channel closed, clear user channels, {}", channel);
|
||||
if (channel.attr(CHANNEL_PORT).get() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String clientKey = channel.attr(CHANNEL_CLIENT_KEY).get();
|
||||
Channel channel0 = cmdChannels.remove(clientKey);
|
||||
if (channel != channel0) {
|
||||
cmdChannels.put(clientKey, channel);
|
||||
}
|
||||
|
||||
List<Integer> ports = channel.attr(CHANNEL_PORT).get();
|
||||
for (int port : ports) {
|
||||
Channel proxyChannel = portCmdChannelMapping.remove(port);
|
||||
if (proxyChannel == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在执行断连之前新的连接已经连上来了
|
||||
if (proxyChannel != channel) {
|
||||
portCmdChannelMapping.put(port, proxyChannel);
|
||||
}
|
||||
}
|
||||
|
||||
if (channel.isActive()) {
|
||||
logger.info("disconnect proxy channel {}", channel);
|
||||
channel.close();
|
||||
}
|
||||
|
||||
Map<String, Channel> userChannels = getUserChannels(channel);
|
||||
Iterator<String> ite = userChannels.keySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
Channel userChannel = userChannels.get(ite.next());
|
||||
if (userChannel.isActive()) {
|
||||
userChannel.close();
|
||||
logger.info("disconnect user channel {}", userChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Channel getCmdChannel(Integer port) {
|
||||
return portCmdChannelMapping.get(port);
|
||||
}
|
||||
|
||||
public static Channel getCmdChannel(String clientKey) {
|
||||
return cmdChannels.get(clientKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加用户连接与代理客户端连接关系
|
||||
*
|
||||
* @param proxyChannel
|
||||
* @param userId
|
||||
* @param userChannel
|
||||
*/
|
||||
public static void addUserChannelToCmdChannel(Channel cmdChannel, String userId, Channel userChannel) {
|
||||
InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
|
||||
String lanInfo = ProxyConfig.getInstance().getLanInfo(sa.getPort());
|
||||
userChannel.attr(Constants.USER_ID).set(userId);
|
||||
userChannel.attr(REQUEST_LAN_INFO).set(lanInfo);
|
||||
cmdChannel.attr(USER_CHANNELS).get().put(userId, userChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户连接与代理客户端连接关系
|
||||
*
|
||||
* @param proxyChannel
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
public static Channel removeUserChannelFromCmdChannel(Channel cmdChannel, String userId) {
|
||||
if (cmdChannel.attr(USER_CHANNELS).get() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (cmdChannel) {
|
||||
return cmdChannel.attr(USER_CHANNELS).get().remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代理客户端连接与用户编号获取用户连接
|
||||
*
|
||||
* @param proxyChannel
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
public static Channel getUserChannel(Channel cmdChannel, String userId) {
|
||||
return cmdChannel.attr(USER_CHANNELS).get().get(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户编号
|
||||
*
|
||||
* @param userChannel
|
||||
* @return
|
||||
*/
|
||||
public static String getUserChannelUserId(Channel userChannel) {
|
||||
return userChannel.attr(Constants.USER_ID).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户请求的内网IP端口信息
|
||||
*
|
||||
* @param userChannel
|
||||
* @return
|
||||
*/
|
||||
public static String getUserChannelRequestLanInfo(Channel userChannel) {
|
||||
return userChannel.attr(REQUEST_LAN_INFO).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理控制客户端连接绑定的所有用户连接
|
||||
*
|
||||
* @param cmdChannel
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Channel> getUserChannels(Channel cmdChannel) {
|
||||
return cmdChannel.attr(USER_CHANNELS).get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
package org.fengfei.lanproxy.server;
|
||||
|
||||
import java.net.BindException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.fengfei.lanproxy.common.container.Container;
|
||||
import org.fengfei.lanproxy.common.container.ContainerHelper;
|
||||
import org.fengfei.lanproxy.protocol.IdleCheckHandler;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessageDecoder;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessageEncoder;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig.ConfigChangedListener;
|
||||
import org.fengfei.lanproxy.server.config.web.WebConfigContainer;
|
||||
import org.fengfei.lanproxy.server.handlers.ServerChannelHandler;
|
||||
import org.fengfei.lanproxy.server.handlers.UserChannelHandler;
|
||||
import org.fengfei.lanproxy.server.metrics.handler.BytesMetricsHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
|
||||
public class ProxyServerContainer implements Container, ConfigChangedListener {
|
||||
|
||||
/**
|
||||
* max packet is 6M.
|
||||
*/
|
||||
private static final int MAX_FRAME_LENGTH = 6 * 1024 * 1024;
|
||||
|
||||
private static final int LENGTH_FIELD_OFFSET = 0;
|
||||
|
||||
private static final int LENGTH_FIELD_LENGTH = 4;
|
||||
|
||||
private static final int INITIAL_BYTES_TO_STRIP = 0;
|
||||
|
||||
private static final int LENGTH_ADJUSTMENT = 0;
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ProxyServerContainer.class);
|
||||
|
||||
private NioEventLoopGroup serverWorkerGroup;
|
||||
|
||||
private NioEventLoopGroup serverBossGroup;
|
||||
|
||||
public ProxyServerContainer() {
|
||||
|
||||
serverBossGroup = new NioEventLoopGroup();
|
||||
serverWorkerGroup = new NioEventLoopGroup();
|
||||
|
||||
ProxyConfig.getInstance().addConfigChangedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(serverBossGroup, serverWorkerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast(new ProxyMessageDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
|
||||
ch.pipeline().addLast(new ProxyMessageEncoder());
|
||||
ch.pipeline().addLast(new IdleCheckHandler(IdleCheckHandler.READ_IDLE_TIME, IdleCheckHandler.WRITE_IDLE_TIME, 0));
|
||||
ch.pipeline().addLast(new ServerChannelHandler());
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
bootstrap.bind(ProxyConfig.getInstance().getServerBind(), ProxyConfig.getInstance().getServerPort()).get();
|
||||
logger.info("proxy server start on port " + ProxyConfig.getInstance().getServerPort());
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
if (Config.getInstance().getBooleanValue("server.ssl.enable", false)) {
|
||||
String host = Config.getInstance().getStringValue("server.ssl.bind", "0.0.0.0");
|
||||
int port = Config.getInstance().getIntValue("server.ssl.port");
|
||||
initializeSSLTCPTransport(host, port, new SslContextCreator().initSSLContext());
|
||||
}
|
||||
|
||||
startUserPort();
|
||||
|
||||
}
|
||||
|
||||
private void initializeSSLTCPTransport(String host, int port, final SSLContext sslContext) {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(serverBossGroup, serverWorkerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
try {
|
||||
pipeline.addLast("ssl", createSslHandler(sslContext, Config.getInstance().getBooleanValue("server.ssl.needsClientAuth", false)));
|
||||
ch.pipeline().addLast(new ProxyMessageDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
|
||||
ch.pipeline().addLast(new ProxyMessageEncoder());
|
||||
ch.pipeline().addLast(new IdleCheckHandler(IdleCheckHandler.READ_IDLE_TIME, IdleCheckHandler.WRITE_IDLE_TIME, 0));
|
||||
ch.pipeline().addLast(new ServerChannelHandler());
|
||||
} catch (Throwable th) {
|
||||
logger.error("Severe error during pipeline creation", th);
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
ChannelFuture f = b.bind(host, port);
|
||||
f.sync();
|
||||
logger.info("proxy ssl server start on port {}", port);
|
||||
} catch (InterruptedException ex) {
|
||||
logger.error("An interruptedException was caught while initializing server", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void startUserPort() {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(serverBossGroup, serverWorkerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addFirst(new BytesMetricsHandler());
|
||||
ch.pipeline().addLast(new UserChannelHandler());
|
||||
}
|
||||
});
|
||||
|
||||
List<Integer> ports = ProxyConfig.getInstance().getUserPorts();
|
||||
for (int port : ports) {
|
||||
try {
|
||||
bootstrap.bind(port).get();
|
||||
logger.info("bind user port " + port);
|
||||
} catch (Exception ex) {
|
||||
|
||||
// BindException表示该端口已经绑定过
|
||||
if (!(ex.getCause() instanceof BindException)) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged() {
|
||||
startUserPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
serverBossGroup.shutdownGracefully();
|
||||
serverWorkerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
private ChannelHandler createSslHandler(SSLContext sslContext, boolean needsClientAuth) {
|
||||
SSLEngine sslEngine = sslContext.createSSLEngine();
|
||||
sslEngine.setUseClientMode(false);
|
||||
if (needsClientAuth) {
|
||||
sslEngine.setNeedClientAuth(true);
|
||||
}
|
||||
|
||||
return new SslHandler(sslEngine);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
ContainerHelper.start(Arrays.asList(new Container[] { new ProxyServerContainer(), new WebConfigContainer() }));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package org.fengfei.lanproxy.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SslContextCreator {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SslContextCreator.class);
|
||||
|
||||
public SSLContext initSSLContext() {
|
||||
logger.info("Checking SSL configuration properties...");
|
||||
final String jksPath = Config.getInstance().getStringValue("server.ssl.jksPath");
|
||||
logger.info("Initializing SSL context. KeystorePath = {}.", jksPath);
|
||||
if (jksPath == null || jksPath.isEmpty()) {
|
||||
// key_store_password or key_manager_password are empty
|
||||
logger.warn("The keystore path is null or empty. The SSL context won't be initialized.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// if we have the port also the jks then keyStorePassword and
|
||||
// keyManagerPassword
|
||||
// has to be defined
|
||||
final String keyStorePassword = Config.getInstance().getStringValue("server.ssl.keyStorePassword");
|
||||
final String keyManagerPassword = Config.getInstance().getStringValue("server.ssl.keyManagerPassword");
|
||||
if (keyStorePassword == null || keyStorePassword.isEmpty()) {
|
||||
|
||||
// key_store_password or key_manager_password are empty
|
||||
logger.warn("The keystore password is null or empty. The SSL context won't be initialized.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (keyManagerPassword == null || keyManagerPassword.isEmpty()) {
|
||||
|
||||
// key_manager_password or key_manager_password are empty
|
||||
logger.warn("The key manager password is null or empty. The SSL context won't be initialized.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// if client authentification is enabled a trustmanager needs to be
|
||||
// added to the ServerContext
|
||||
boolean needsClientAuth = Config.getInstance().getBooleanValue("server.ssl.needsClientAuth", false);
|
||||
|
||||
try {
|
||||
logger.info("Loading keystore. KeystorePath = {}.", jksPath);
|
||||
InputStream jksInputStream = jksDatastore(jksPath);
|
||||
SSLContext serverContext = SSLContext.getInstance("TLS");
|
||||
final KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(jksInputStream, keyStorePassword.toCharArray());
|
||||
logger.info("Initializing key manager...");
|
||||
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(ks, keyManagerPassword.toCharArray());
|
||||
TrustManager[] trustManagers = null;
|
||||
if (needsClientAuth) {
|
||||
logger.warn(
|
||||
"Client authentication is enabled. The keystore will be used as a truststore. KeystorePath = {}.",
|
||||
jksPath);
|
||||
// use keystore as truststore, as server needs to trust
|
||||
// certificates signed by the
|
||||
// server certificates
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(ks);
|
||||
trustManagers = tmf.getTrustManagers();
|
||||
}
|
||||
|
||||
// init sslContext
|
||||
logger.info("Initializing SSL context...");
|
||||
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
|
||||
logger.info("The SSL context has been initialized successfully.");
|
||||
|
||||
return serverContext;
|
||||
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | KeyStoreException
|
||||
| KeyManagementException | IOException ex) {
|
||||
logger.error("Unable to initialize SSL context. Cause = {}, errorMessage = {}.", ex.getCause(),
|
||||
ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream jksDatastore(String jksPath) throws FileNotFoundException {
|
||||
URL jksUrl = getClass().getClassLoader().getResource(jksPath);
|
||||
if (jksUrl != null) {
|
||||
logger.info("Starting with jks at {}, jks normal {}", jksUrl.toExternalForm(), jksUrl);
|
||||
return getClass().getClassLoader().getResourceAsStream(jksPath);
|
||||
}
|
||||
|
||||
logger.warn("No keystore has been found in the bundled resources. Scanning filesystem...");
|
||||
File jksFile = new File(jksPath);
|
||||
if (jksFile.exists()) {
|
||||
logger.info("Loading external keystore. Url = {}.", jksFile.getAbsolutePath());
|
||||
return new FileInputStream(jksFile);
|
||||
}
|
||||
|
||||
logger.warn("The keystore file does not exist. Url = {}.", jksFile.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
package org.fengfei.lanproxy.server.config;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.fengfei.lanproxy.common.Config;
|
||||
import org.fengfei.lanproxy.common.JsonUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* server config
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ProxyConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 配置文件为config.json */
|
||||
public static final String CONFIG_FILE;
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ProxyConfig.class);
|
||||
|
||||
static {
|
||||
|
||||
// 代理配置信息存放在用户根目录下
|
||||
String dataPath = System.getProperty("user.home") + "/" + ".lanproxy/";
|
||||
File file = new File(dataPath);
|
||||
if (!file.isDirectory()) {
|
||||
file.mkdir();
|
||||
}
|
||||
|
||||
CONFIG_FILE = dataPath + "/config.json";
|
||||
}
|
||||
|
||||
/** 代理服务器绑定主机host */
|
||||
private String serverBind;
|
||||
|
||||
/** 代理服务器与代理客户端通信端口 */
|
||||
private Integer serverPort;
|
||||
|
||||
/** 配置服务绑定主机host */
|
||||
private String configServerBind;
|
||||
|
||||
/** 配置服务端口 */
|
||||
private Integer configServerPort;
|
||||
|
||||
/** 配置服务管理员用户名 */
|
||||
private String configAdminUsername;
|
||||
|
||||
/** 配置服务管理员密码 */
|
||||
private String configAdminPassword;
|
||||
|
||||
/** 代理客户端,支持多个客户端 */
|
||||
private List<Client> clients;
|
||||
|
||||
/** 更新配置后保证在其他线程即时生效 */
|
||||
private static ProxyConfig instance = new ProxyConfig();;
|
||||
|
||||
/** 代理服务器为各个代理客户端(key)开启对应的端口列表(value) */
|
||||
private volatile Map<String, List<Integer>> clientInetPortMapping = new HashMap<String, List<Integer>>();
|
||||
|
||||
/** 代理服务器上的每个对外端口(key)对应的代理客户端背后的真实服务器信息(value) */
|
||||
private volatile Map<Integer, String> inetPortLanInfoMapping = new HashMap<Integer, String>();
|
||||
|
||||
/** 配置变化监听器 */
|
||||
private List<ConfigChangedListener> configChangedListeners = new ArrayList<ConfigChangedListener>();
|
||||
|
||||
private ProxyConfig() {
|
||||
|
||||
// 代理服务器主机和端口配置初始化
|
||||
this.serverPort = Config.getInstance().getIntValue("server.port");
|
||||
this.serverBind = Config.getInstance().getStringValue("server.bind", "0.0.0.0");
|
||||
|
||||
// 配置服务器主机和端口配置初始化
|
||||
this.configServerPort = Config.getInstance().getIntValue("config.server.port");
|
||||
this.configServerBind = Config.getInstance().getStringValue("config.server.bind", "0.0.0.0");
|
||||
|
||||
// 配置服务器管理员登录认证信息
|
||||
this.configAdminUsername = Config.getInstance().getStringValue("config.admin.username");
|
||||
this.configAdminPassword = Config.getInstance().getStringValue("config.admin.password");
|
||||
|
||||
logger.info(
|
||||
"config init serverBind {}, serverPort {}, configServerBind {}, configServerPort {}, configAdminUsername {}, configAdminPassword {}",
|
||||
serverBind, serverPort, configServerBind, configServerPort, configAdminUsername, configAdminPassword);
|
||||
|
||||
update(null);
|
||||
}
|
||||
|
||||
public Integer getServerPort() {
|
||||
return this.serverPort;
|
||||
}
|
||||
|
||||
public String getServerBind() {
|
||||
return serverBind;
|
||||
}
|
||||
|
||||
public void setServerBind(String serverBind) {
|
||||
this.serverBind = serverBind;
|
||||
}
|
||||
|
||||
public String getConfigServerBind() {
|
||||
return configServerBind;
|
||||
}
|
||||
|
||||
public void setConfigServerBind(String configServerBind) {
|
||||
this.configServerBind = configServerBind;
|
||||
}
|
||||
|
||||
public Integer getConfigServerPort() {
|
||||
return configServerPort;
|
||||
}
|
||||
|
||||
public void setConfigServerPort(Integer configServerPort) {
|
||||
this.configServerPort = configServerPort;
|
||||
}
|
||||
|
||||
public String getConfigAdminUsername() {
|
||||
return configAdminUsername;
|
||||
}
|
||||
|
||||
public void setConfigAdminUsername(String configAdminUsername) {
|
||||
this.configAdminUsername = configAdminUsername;
|
||||
}
|
||||
|
||||
public String getConfigAdminPassword() {
|
||||
return configAdminPassword;
|
||||
}
|
||||
|
||||
public void setConfigAdminPassword(String configAdminPassword) {
|
||||
this.configAdminPassword = configAdminPassword;
|
||||
}
|
||||
|
||||
public void setServerPort(Integer serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public List<Client> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置文件
|
||||
*/
|
||||
public void update(String proxyMappingConfigJson) {
|
||||
|
||||
File file = new File(CONFIG_FILE);
|
||||
try {
|
||||
if (proxyMappingConfigJson == null && file.exists()) {
|
||||
InputStream in = new FileInputStream(file);
|
||||
byte[] buf = new byte[1024];
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int readIndex;
|
||||
while ((readIndex = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, readIndex);
|
||||
}
|
||||
|
||||
in.close();
|
||||
proxyMappingConfigJson = new String(out.toByteArray(), Charset.forName("UTF-8"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
List<Client> clients = JsonUtil.json2object(proxyMappingConfigJson, new TypeToken<List<Client>>() {
|
||||
});
|
||||
if (clients == null) {
|
||||
clients = new ArrayList<Client>();
|
||||
}
|
||||
|
||||
Map<String, List<Integer>> clientInetPortMapping = new HashMap<String, List<Integer>>();
|
||||
Map<Integer, String> inetPortLanInfoMapping = new HashMap<Integer, String>();
|
||||
|
||||
// 构造端口映射关系
|
||||
for (Client client : clients) {
|
||||
String clientKey = client.getClientKey();
|
||||
if (clientInetPortMapping.containsKey(clientKey)) {
|
||||
throw new IllegalArgumentException("密钥同时作为客户端标识,不能重复: " + clientKey);
|
||||
}
|
||||
List<ClientProxyMapping> mappings = client.getProxyMappings();
|
||||
List<Integer> ports = new ArrayList<Integer>();
|
||||
clientInetPortMapping.put(clientKey, ports);
|
||||
for (ClientProxyMapping mapping : mappings) {
|
||||
Integer port = mapping.getInetPort();
|
||||
ports.add(port);
|
||||
if (inetPortLanInfoMapping.containsKey(port)) {
|
||||
throw new IllegalArgumentException("一个公网端口只能映射一个后端信息,不能重复: " + port);
|
||||
}
|
||||
|
||||
inetPortLanInfoMapping.put(port, mapping.getLan());
|
||||
}
|
||||
}
|
||||
|
||||
// 替换之前的配置关系
|
||||
this.clientInetPortMapping = clientInetPortMapping;
|
||||
this.inetPortLanInfoMapping = inetPortLanInfoMapping;
|
||||
this.clients = clients;
|
||||
|
||||
if (proxyMappingConfigJson != null) {
|
||||
try {
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
out.write(proxyMappingConfigJson.getBytes(Charset.forName("UTF-8")));
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
notifyconfigChangedListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置更新通知
|
||||
*/
|
||||
private void notifyconfigChangedListeners() {
|
||||
List<ConfigChangedListener> changedListeners = new ArrayList<ConfigChangedListener>(configChangedListeners);
|
||||
for (ConfigChangedListener changedListener : changedListeners) {
|
||||
changedListener.onChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加配置变化监听器
|
||||
*
|
||||
* @param configChangedListener
|
||||
*/
|
||||
public void addConfigChangedListener(ConfigChangedListener configChangedListener) {
|
||||
configChangedListeners.add(configChangedListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除配置变化监听器
|
||||
*
|
||||
* @param configChangedListener
|
||||
*/
|
||||
public void removeConfigChangedListener(ConfigChangedListener configChangedListener) {
|
||||
configChangedListeners.remove(configChangedListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理客户端对应的代理服务器端口
|
||||
*
|
||||
* @param clientKey
|
||||
* @return
|
||||
*/
|
||||
public List<Integer> getClientInetPorts(String clientKey) {
|
||||
return clientInetPortMapping.get(clientKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的clientKey
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Set<String> getClientKeySet() {
|
||||
return clientInetPortMapping.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代理服务器端口获取后端服务器代理信息
|
||||
*
|
||||
* @param port
|
||||
* @return
|
||||
*/
|
||||
public String getLanInfo(Integer port) {
|
||||
return inetPortLanInfoMapping.get(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回需要绑定在代理服务器的端口(用于用户请求)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<Integer> getUserPorts() {
|
||||
List<Integer> ports = new ArrayList<Integer>();
|
||||
Iterator<Integer> ite = inetPortLanInfoMapping.keySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
ports.add(ite.next());
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
public static ProxyConfig getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理客户端
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public static class Client implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 客户端备注名称 */
|
||||
private String name;
|
||||
|
||||
/** 代理客户端唯一标识key */
|
||||
private String clientKey;
|
||||
|
||||
/** 代理客户端与其后面的真实服务器映射关系 */
|
||||
private List<ClientProxyMapping> proxyMappings;
|
||||
|
||||
private int status;
|
||||
|
||||
public String getClientKey() {
|
||||
return clientKey;
|
||||
}
|
||||
|
||||
public void setClientKey(String clientKey) {
|
||||
this.clientKey = clientKey;
|
||||
}
|
||||
|
||||
public List<ClientProxyMapping> getProxyMappings() {
|
||||
return proxyMappings;
|
||||
}
|
||||
|
||||
public void setProxyMappings(List<ClientProxyMapping> proxyMappings) {
|
||||
this.proxyMappings = proxyMappings;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理客户端与其后面真实服务器映射关系
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public static class ClientProxyMapping {
|
||||
|
||||
/** 代理服务器端口 */
|
||||
private Integer inetPort;
|
||||
|
||||
/** 需要代理的网络信息(代理客户端能够访问),格式 192.168.1.99:80 (必须带端口) */
|
||||
private String lan;
|
||||
|
||||
/** 备注名称 */
|
||||
private String name;
|
||||
|
||||
public Integer getInetPort() {
|
||||
return inetPort;
|
||||
}
|
||||
|
||||
public void setInetPort(Integer inetPort) {
|
||||
this.inetPort = inetPort;
|
||||
}
|
||||
|
||||
public String getLan() {
|
||||
return lan;
|
||||
}
|
||||
|
||||
public void setLan(String lan) {
|
||||
this.lan = lan;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置更新回调
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public static interface ConfigChangedListener {
|
||||
|
||||
void onChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.fengfei.lanproxy.server.config.web.exception.ContextException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
|
||||
/**
|
||||
* 接口路由管理
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ApiRoute {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ApiRoute.class);
|
||||
|
||||
/** 接口路由 */
|
||||
private static Map<String, RequestHandler> routes = new ConcurrentHashMap<String, RequestHandler>();
|
||||
|
||||
/** 拦截器,初始化后不会在变化 */
|
||||
private static List<RequestMiddleware> middlewares = new ArrayList<RequestMiddleware>();
|
||||
|
||||
/**
|
||||
* 增加接口请求路由
|
||||
*
|
||||
* @param uri
|
||||
* @param requestHandler
|
||||
*/
|
||||
public static void addRoute(String uri, RequestHandler requestHandler) {
|
||||
if (routes.containsKey(uri)) {
|
||||
throw new IllegalArgumentException("Duplicate uri:" + uri);
|
||||
}
|
||||
|
||||
logger.info("add route {}", uri);
|
||||
routes.put(uri, requestHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加拦截器
|
||||
*
|
||||
* @param requestMiddleware
|
||||
*/
|
||||
public static void addMiddleware(RequestMiddleware requestMiddleware) {
|
||||
if (middlewares.contains(requestMiddleware)) {
|
||||
throw new IllegalArgumentException("Duplicate RequestMiddleware:" + requestMiddleware);
|
||||
}
|
||||
|
||||
logger.info("add requestMiddleware {}", requestMiddleware);
|
||||
middlewares.add(requestMiddleware);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求执行
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static ResponseInfo run(FullHttpRequest request) {
|
||||
try {
|
||||
|
||||
// 拦截器中如果不能通过以异常的方式进行反馈
|
||||
for (RequestMiddleware middleware : middlewares) {
|
||||
middleware.preRequest(request);
|
||||
}
|
||||
|
||||
URI uri = new URI(request.getUri());
|
||||
RequestHandler handler = routes.get(uri.getPath());
|
||||
ResponseInfo responseInfo = null;
|
||||
if (handler != null) {
|
||||
responseInfo = handler.request(request);
|
||||
} else {
|
||||
responseInfo = ResponseInfo.build(ResponseInfo.CODE_API_NOT_FOUND, "api not found");
|
||||
}
|
||||
|
||||
return responseInfo;
|
||||
} catch (Exception ex) {
|
||||
if (ex instanceof ContextException) {
|
||||
return ResponseInfo.build(((ContextException) ex).getCode(), ex.getMessage());
|
||||
}
|
||||
|
||||
logger.error("request error", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.fengfei.lanproxy.common.JsonUtil;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.stream.ChunkedNioFile;
|
||||
|
||||
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
|
||||
private static final String PAGE_FOLDER = System.getProperty("app.home", System.getProperty("user.dir"))
|
||||
+ "/webpages";
|
||||
|
||||
private static final String SERVER_VS = "LPS-0.1";
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
|
||||
|
||||
// GET返回页面;POST请求接口
|
||||
if (request.getMethod() != HttpMethod.POST) {
|
||||
outputPages(ctx, request);
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseInfo responseInfo = ApiRoute.run(request);
|
||||
|
||||
// 错误码规则:除100取整为http状态码
|
||||
outputContent(ctx, request, responseInfo.getCode() / 100, JsonUtil.object2json(responseInfo),
|
||||
"Application/json;charset=utf-8");
|
||||
}
|
||||
|
||||
private void outputContent(ChannelHandlerContext ctx, FullHttpRequest request, int code, String content,
|
||||
String mimeType) {
|
||||
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(code),
|
||||
Unpooled.wrappedBuffer(content.getBytes(Charset.forName("UTF-8"))));
|
||||
response.headers().set(Names.CONTENT_TYPE, mimeType);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
response.headers().set(Names.SERVER, SERVER_VS);
|
||||
ChannelFuture future = ctx.writeAndFlush(response);
|
||||
if (!HttpHeaders.isKeepAlive(request)) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出静态资源数据
|
||||
*
|
||||
* @param ctx
|
||||
* @param request
|
||||
* @throws Exception
|
||||
*/
|
||||
private void outputPages(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
|
||||
HttpResponseStatus status = HttpResponseStatus.OK;
|
||||
URI uri = new URI(request.getUri());
|
||||
String uriPath = uri.getPath();
|
||||
uriPath = uriPath.equals("/") ? "/index.html" : uriPath;
|
||||
String path = PAGE_FOLDER + uriPath;
|
||||
File rfile = new File(path);
|
||||
if (rfile.isDirectory()) {
|
||||
path = path + "/index.html";
|
||||
rfile = new File(path);
|
||||
}
|
||||
|
||||
if (!rfile.exists()) {
|
||||
status = HttpResponseStatus.NOT_FOUND;
|
||||
outputContent(ctx, request, status.code(), status.toString(), "text/html");
|
||||
return;
|
||||
}
|
||||
|
||||
if (HttpHeaders.is100ContinueExpected(request)) {
|
||||
send100Continue(ctx);
|
||||
}
|
||||
|
||||
String mimeType = MimeType.getMimeType(MimeType.parseSuffix(path));
|
||||
long length = 0;
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
raf = new RandomAccessFile(rfile, "r");
|
||||
length = raf.length();
|
||||
} finally {
|
||||
if (length < 0 && raf != null) {
|
||||
raf.close();
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), status);
|
||||
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, mimeType);
|
||||
boolean keepAlive = HttpHeaders.isKeepAlive(request);
|
||||
if (keepAlive) {
|
||||
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, length);
|
||||
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
response.headers().set(Names.SERVER, SERVER_VS);
|
||||
ctx.write(response);
|
||||
|
||||
if (ctx.pipeline().get(SslHandler.class) == null) {
|
||||
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length));
|
||||
} else {
|
||||
ctx.write(new ChunkedNioFile(raf.getChannel()));
|
||||
}
|
||||
|
||||
ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
if (!keepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void send100Continue(ChannelHandlerContext ctx) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
|
||||
ctx.writeAndFlush(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MimeType {
|
||||
|
||||
final static Pattern pattern = Pattern.compile("\\S*[?]\\S*");
|
||||
|
||||
private static Map<String, String> h = new HashMap<String, String>();
|
||||
static {
|
||||
h.put("", "application/octet-stream");
|
||||
h.put("323", "text/h323");
|
||||
h.put("acx", "application/internet-property-stream");
|
||||
h.put("ai", "application/postscript");
|
||||
h.put("aif", "audio/x-aiff");
|
||||
h.put("aifc", "audio/x-aiff");
|
||||
h.put("aiff", "audio/x-aiff");
|
||||
h.put("asf", "video/x-ms-asf");
|
||||
h.put("asr", "video/x-ms-asf");
|
||||
h.put("asx", "video/x-ms-asf");
|
||||
h.put("au", "audio/basic");
|
||||
h.put("avi", "video/x-msvideo");
|
||||
h.put("axs", "application/olescript");
|
||||
h.put("bas", "text/plain");
|
||||
h.put("bcpio", "application/x-bcpio");
|
||||
h.put("bin", "application/octet-stream");
|
||||
h.put("bmp", "image/bmp");
|
||||
h.put("c", "text/plain");
|
||||
h.put("cat", "application/vnd.ms-pkiseccat");
|
||||
h.put("cdf", "application/x-cdf");
|
||||
h.put("cer", "application/x-x509-ca-cert");
|
||||
h.put("class", "application/octet-stream");
|
||||
h.put("clp", "application/x-msclip");
|
||||
h.put("cmx", "image/x-cmx");
|
||||
h.put("cod", "image/cis-cod");
|
||||
h.put("cpio", "application/x-cpio");
|
||||
h.put("crd", "application/x-mscardfile");
|
||||
h.put("crl", "application/pkix-crl");
|
||||
h.put("crt", "application/x-x509-ca-cert");
|
||||
h.put("csh", "application/x-csh");
|
||||
h.put("css", "text/css");
|
||||
h.put("dcr", "application/x-director");
|
||||
h.put("der", "application/x-x509-ca-cert");
|
||||
h.put("dir", "application/x-director");
|
||||
h.put("dll", "application/x-msdownload");
|
||||
h.put("dms", "application/octet-stream");
|
||||
h.put("doc", "application/msword");
|
||||
h.put("dot", "application/msword");
|
||||
h.put("dvi", "application/x-dvi");
|
||||
h.put("dxr", "application/x-director");
|
||||
h.put("eps", "application/postscript");
|
||||
h.put("etx", "text/x-setext");
|
||||
h.put("evy", "application/envoy");
|
||||
h.put("exe", "application/octet-stream");
|
||||
h.put("fif", "application/fractals");
|
||||
h.put("flr", "x-world/x-vrml");
|
||||
h.put("gif", "image/gif");
|
||||
h.put("gtar", "application/x-gtar");
|
||||
h.put("gz", "application/x-gzip");
|
||||
h.put("h", "text/plain");
|
||||
h.put("hdf", "applicatin/x-hdf");
|
||||
h.put("hlp", "application/winhlp");
|
||||
h.put("hqx", "application/mac-binhex40");
|
||||
h.put("hta", "application/hta");
|
||||
h.put("htc", "text/x-component");
|
||||
h.put("htm", "text/html");
|
||||
h.put("html", "text/html");
|
||||
h.put("htt", "text/webviewhtml");
|
||||
h.put("ico", "image/x-icon");
|
||||
h.put("ief", "image/ief");
|
||||
h.put("iii", "application/x-iphone");
|
||||
h.put("ins", "application/x-internet-signup");
|
||||
h.put("isp", "application/x-internet-signup");
|
||||
h.put("jfif", "image/pipeg");
|
||||
h.put("jpe", "image/jpeg");
|
||||
h.put("jpeg", "image/jpeg");
|
||||
h.put("jpg", "image/jpeg");
|
||||
h.put("js", "application/x-javascript");
|
||||
h.put("latex", "application/x-latex");
|
||||
h.put("lha", "application/octet-stream");
|
||||
h.put("lsf", "video/x-la-asf");
|
||||
h.put("lsx", "video/x-la-asf");
|
||||
h.put("lzh", "application/octet-stream");
|
||||
h.put("m13", "application/x-msmediaview");
|
||||
h.put("m14", "application/x-msmediaview");
|
||||
h.put("m3u", "audio/x-mpegurl");
|
||||
h.put("man", "application/x-troff-man");
|
||||
h.put("mdb", "application/x-msaccess");
|
||||
h.put("me", "application/x-troff-me");
|
||||
h.put("mht", "message/rfc822");
|
||||
h.put("mhtml", "message/rfc822");
|
||||
h.put("mid", "audio/mid");
|
||||
h.put("mny", "application/x-msmoney");
|
||||
h.put("mov", "video/quicktime");
|
||||
h.put("movie", "video/x-sgi-movie");
|
||||
h.put("mp2", "video/mpeg");
|
||||
h.put("mp3", "audio/mpeg");
|
||||
h.put("mpa", "video/mpeg");
|
||||
h.put("mpe", "video/mpeg");
|
||||
h.put("mpeg", "video/mpeg");
|
||||
h.put("mpg", "video/mpeg");
|
||||
h.put("mpp", "application/vnd.ms-project");
|
||||
h.put("mpv2", "video/mpeg");
|
||||
h.put("ms", "application/x-troff-ms");
|
||||
h.put("mvb", "application/x-msmediaview");
|
||||
h.put("nws", "message/rfc822");
|
||||
h.put("oda", "application/oda");
|
||||
h.put("p10", "application/pkcs10");
|
||||
h.put("p12", "application/x-pkcs12");
|
||||
h.put("p7b", "application/x-pkcs7-certificates");
|
||||
h.put("p7c", "application/x-pkcs7-mime");
|
||||
h.put("p7m", "application/x-pkcs7-mime");
|
||||
h.put("p7r", "application/x-pkcs7-certreqresp");
|
||||
h.put("p7s", "application/x-pkcs7-signature");
|
||||
h.put("pbm", "image/x-portable-bitmap");
|
||||
h.put("pdf", "application/pdf");
|
||||
h.put("pfx", "application/x-pkcs12");
|
||||
h.put("pgm", "image/x-portable-graymap");
|
||||
h.put("pko", "application/ynd.ms-pkipko");
|
||||
h.put("pma", "application/x-perfmon");
|
||||
h.put("pmc", "application/x-perfmon");
|
||||
h.put("pml", "application/x-perfmon");
|
||||
h.put("pmr", "application/x-perfmon");
|
||||
h.put("pmw", "application/x-perfmon");
|
||||
h.put("pnm", "image/x-portable-anymap");
|
||||
h.put("pot,", "application/vnd.ms-powerpoint");
|
||||
h.put("ppm", "image/x-portable-pixmap");
|
||||
h.put("pps", "application/vnd.ms-powerpoint");
|
||||
h.put("ppt", "application/vnd.ms-powerpoint");
|
||||
h.put("prf", "application/pics-rules");
|
||||
h.put("ps", "application/postscript");
|
||||
h.put("pub", "application/x-mspublisher");
|
||||
h.put("qt", "video/quicktime");
|
||||
h.put("ra", "audio/x-pn-realaudio");
|
||||
h.put("ram", "audio/x-pn-realaudio");
|
||||
h.put("ras", "image/x-cmu-raster");
|
||||
h.put("rgb", "image/x-rgb");
|
||||
h.put("rmi", "audio/mid");
|
||||
h.put("roff", "application/x-troff");
|
||||
h.put("rtf", "application/rtf");
|
||||
h.put("rtx", "text/richtext");
|
||||
h.put("scd", "application/x-msschedule");
|
||||
h.put("sct", "text/scriptlet");
|
||||
h.put("setpay", "application/set-payment-initiation");
|
||||
h.put("setreg", "application/set-registration-initiation");
|
||||
h.put("sh", "application/x-sh");
|
||||
h.put("shar", "application/x-shar");
|
||||
h.put("sit", "application/x-stuffit");
|
||||
h.put("snd", "audio/basic");
|
||||
h.put("spc", "application/x-pkcs7-certificates");
|
||||
h.put("spl", "application/futuresplash");
|
||||
h.put("src", "application/x-wais-source");
|
||||
h.put("sst", "application/vnd.ms-pkicertstore");
|
||||
h.put("stl", "application/vnd.ms-pkistl");
|
||||
h.put("stm", "text/html");
|
||||
h.put("svg", "image/svg+xml");
|
||||
h.put("sv4cpio", "application/x-sv4cpio");
|
||||
h.put("sv4crc", "application/x-sv4crc");
|
||||
h.put("swf", "application/x-shockwave-flash");
|
||||
h.put("t", "application/x-troff");
|
||||
h.put("tar", "application/x-tar");
|
||||
h.put("tcl", "application/x-tcl");
|
||||
h.put("tex", "application/x-tex");
|
||||
h.put("texi", "application/x-texinfo");
|
||||
h.put("texinfo", "application/x-texinfo");
|
||||
h.put("tgz", "application/x-compressed");
|
||||
h.put("tif", "image/tiff");
|
||||
h.put("tiff", "image/tiff");
|
||||
h.put("tr", "application/x-troff");
|
||||
h.put("trm", "application/x-msterminal");
|
||||
h.put("tsv", "text/tab-separated-values");
|
||||
h.put("txt", "text/plain");
|
||||
h.put("uls", "text/iuls");
|
||||
h.put("ustar", "application/x-ustar");
|
||||
h.put("vcf", "text/x-vcard");
|
||||
h.put("vrml", "x-world/x-vrml");
|
||||
h.put("wav", "audio/x-wav");
|
||||
h.put("wcm", "application/vnd.ms-works");
|
||||
h.put("wdb", "application/vnd.ms-works");
|
||||
h.put("wks", "application/vnd.ms-works");
|
||||
h.put("wmf", "application/x-msmetafile");
|
||||
h.put("wps", "application/vnd.ms-works");
|
||||
h.put("wri", "application/x-mswrite");
|
||||
h.put("wrl", "x-world/x-vrml");
|
||||
h.put("wrz", "x-world/x-vrml");
|
||||
h.put("xaf", "x-world/x-vrml");
|
||||
h.put("xbm", "image/x-xbitmap");
|
||||
h.put("xla", "application/vnd.ms-excel");
|
||||
h.put("xlc", "application/vnd.ms-excel");
|
||||
h.put("xlm", "application/vnd.ms-excel");
|
||||
h.put("xls", "application/vnd.ms-excel");
|
||||
h.put("xlt", "application/vnd.ms-excel");
|
||||
h.put("xlw", "application/vnd.ms-excel");
|
||||
h.put("xof", "x-world/x-vrml");
|
||||
h.put("xpm", "image/x-xpixmap");
|
||||
h.put("xwd", "image/x-xwindowdump");
|
||||
h.put("z", "application/x-compress");
|
||||
h.put("zip", "application/zip");
|
||||
}
|
||||
|
||||
public static String getMimeType(String docType) {
|
||||
String mime = h.get(docType);
|
||||
if (mime == null) {
|
||||
mime = "application/octet-stream";
|
||||
}
|
||||
return mime;
|
||||
}
|
||||
|
||||
public static String parseSuffix(String url) {
|
||||
try {
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
String[] spUrl = url.toString().split("/");
|
||||
int len = spUrl.length;
|
||||
String endUrl = spUrl[len - 1];
|
||||
if (matcher.find()) {
|
||||
String[] spEndUrl = endUrl.split("\\?");
|
||||
endUrl = spEndUrl[0];
|
||||
}
|
||||
String[] endUrlArr = endUrl.split("\\.");
|
||||
return endUrlArr[endUrlArr.length - 1];
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
|
||||
/**
|
||||
* 接口请求处理
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public interface RequestHandler {
|
||||
|
||||
/**
|
||||
* 请求处理
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
ResponseInfo request(FullHttpRequest request);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public interface RequestMiddleware {
|
||||
|
||||
/**
|
||||
* 请求预处理
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
void preRequest(FullHttpRequest request);
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ResponseInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int CODE_OK = 20000;
|
||||
|
||||
public static final int CODE_INVILID_PARAMS = 40000;
|
||||
|
||||
public static final int CODE_UNAUTHORIZED = 40100;
|
||||
|
||||
public static final int CODE_API_NOT_FOUND = 40400;
|
||||
|
||||
public static final int CODE_SYSTEM_ERROR = 50000;
|
||||
|
||||
private int code;
|
||||
|
||||
private String message;
|
||||
|
||||
private Object data;
|
||||
|
||||
public static ResponseInfo build(int code, String message, Object data) {
|
||||
return new ResponseInfo(code, message, data);
|
||||
}
|
||||
|
||||
public static ResponseInfo build(int code, String message) {
|
||||
return new ResponseInfo(code, message);
|
||||
}
|
||||
|
||||
public static ResponseInfo build(Object data) {
|
||||
return new ResponseInfo(data);
|
||||
}
|
||||
|
||||
private ResponseInfo(int code, String message, Object data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
private ResponseInfo(int code, String message) {
|
||||
this(code, message, null);
|
||||
}
|
||||
|
||||
private ResponseInfo(Object data) {
|
||||
this(ResponseInfo.CODE_OK, "success", data);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package org.fengfei.lanproxy.server.config.web;
|
||||
|
||||
import org.fengfei.lanproxy.common.container.Container;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig;
|
||||
import org.fengfei.lanproxy.server.config.web.routes.RouteConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
|
||||
public class WebConfigContainer implements Container {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(WebConfigContainer.class);
|
||||
|
||||
private NioEventLoopGroup serverWorkerGroup;
|
||||
|
||||
private NioEventLoopGroup serverBossGroup;
|
||||
|
||||
public WebConfigContainer() {
|
||||
|
||||
// 配置管理,并发处理很小,使用单线程处理网络事件
|
||||
serverBossGroup = new NioEventLoopGroup(1);
|
||||
serverWorkerGroup = new NioEventLoopGroup(1);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
ServerBootstrap httpServerBootstrap = new ServerBootstrap();
|
||||
httpServerBootstrap.group(serverBossGroup, serverWorkerGroup).channel(NioServerSocketChannel.class)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(64 * 1024));
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
pipeline.addLast(new HttpRequestHandler());
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
httpServerBootstrap.bind(ProxyConfig.getInstance().getConfigServerBind(),
|
||||
ProxyConfig.getInstance().getConfigServerPort()).get();
|
||||
logger.info("http server start on port " + ProxyConfig.getInstance().getConfigServerPort());
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
RouteConfig.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
serverBossGroup.shutdownGracefully();
|
||||
serverWorkerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.fengfei.lanproxy.server.config.web.exception;
|
||||
|
||||
public class ContextException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code;
|
||||
|
||||
public ContextException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public ContextException(int code) {
|
||||
super();
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package org.fengfei.lanproxy.server.config.web.routes;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.fengfei.lanproxy.common.JsonUtil;
|
||||
import org.fengfei.lanproxy.server.ProxyChannelManager;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig.Client;
|
||||
import org.fengfei.lanproxy.server.config.web.ApiRoute;
|
||||
import org.fengfei.lanproxy.server.config.web.RequestHandler;
|
||||
import org.fengfei.lanproxy.server.config.web.RequestMiddleware;
|
||||
import org.fengfei.lanproxy.server.config.web.ResponseInfo;
|
||||
import org.fengfei.lanproxy.server.config.web.exception.ContextException;
|
||||
import org.fengfei.lanproxy.server.metrics.MetricsCollector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
|
||||
/**
|
||||
* 接口实现
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class RouteConfig {
|
||||
|
||||
protected static final String AUTH_COOKIE_KEY = "token";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(RouteConfig.class);
|
||||
|
||||
/** 管理员不能同时在多个地方登录 */
|
||||
private static String token;
|
||||
|
||||
public static void init() {
|
||||
|
||||
ApiRoute.addMiddleware(new RequestMiddleware() {
|
||||
|
||||
@Override
|
||||
public void preRequest(FullHttpRequest request) {
|
||||
String cookieHeader = request.headers().get(HttpHeaders.Names.COOKIE);
|
||||
boolean authenticated = false;
|
||||
if (cookieHeader != null) {
|
||||
String[] cookies = cookieHeader.split(";");
|
||||
for (String cookie : cookies) {
|
||||
String[] cookieArr = cookie.split("=");
|
||||
if (AUTH_COOKIE_KEY.equals(cookieArr[0].trim())) {
|
||||
if (cookieArr.length == 2 && cookieArr[1].equals(token)) {
|
||||
authenticated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String auth = request.headers().get(HttpHeaders.Names.AUTHORIZATION);
|
||||
if (!authenticated && auth != null) {
|
||||
String[] authArr = auth.split(" ");
|
||||
if (authArr.length == 2 && authArr[0].equals(ProxyConfig.getInstance().getConfigAdminUsername()) && authArr[1].equals(ProxyConfig.getInstance().getConfigAdminPassword())) {
|
||||
authenticated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!request.getUri().equals("/login") && !authenticated) {
|
||||
throw new ContextException(ResponseInfo.CODE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
logger.info("handle request for api {}", request.getUri());
|
||||
}
|
||||
});
|
||||
|
||||
// 获取配置详细信息
|
||||
ApiRoute.addRoute("/config/detail", new RequestHandler() {
|
||||
|
||||
@Override
|
||||
public ResponseInfo request(FullHttpRequest request) {
|
||||
List<Client> clients = ProxyConfig.getInstance().getClients();
|
||||
for (Client client : clients) {
|
||||
Channel channel = ProxyChannelManager.getCmdChannel(client.getClientKey());
|
||||
if (channel != null) {
|
||||
client.setStatus(1);// online
|
||||
} else {
|
||||
client.setStatus(0);// offline
|
||||
}
|
||||
}
|
||||
return ResponseInfo.build(ProxyConfig.getInstance().getClients());
|
||||
}
|
||||
});
|
||||
|
||||
// 更新配置
|
||||
ApiRoute.addRoute("/config/update", new RequestHandler() {
|
||||
|
||||
@Override
|
||||
public ResponseInfo request(FullHttpRequest request) {
|
||||
byte[] buf = new byte[request.content().readableBytes()];
|
||||
request.content().readBytes(buf);
|
||||
String config = new String(buf, Charset.forName("UTF-8"));
|
||||
List<Client> clients = JsonUtil.json2object(config, new TypeToken<List<Client>>() {
|
||||
});
|
||||
if (clients == null) {
|
||||
return ResponseInfo.build(ResponseInfo.CODE_INVILID_PARAMS, "Error json config");
|
||||
}
|
||||
|
||||
try {
|
||||
ProxyConfig.getInstance().update(config);
|
||||
} catch (Exception ex) {
|
||||
logger.error("config update error", ex);
|
||||
return ResponseInfo.build(ResponseInfo.CODE_INVILID_PARAMS, ex.getMessage());
|
||||
}
|
||||
|
||||
return ResponseInfo.build(ResponseInfo.CODE_OK, "success");
|
||||
}
|
||||
});
|
||||
|
||||
ApiRoute.addRoute("/login", new RequestHandler() {
|
||||
|
||||
@Override
|
||||
public ResponseInfo request(FullHttpRequest request) {
|
||||
byte[] buf = new byte[request.content().readableBytes()];
|
||||
request.content().readBytes(buf);
|
||||
String config = new String(buf);
|
||||
Map<String, String> loginParams = JsonUtil.json2object(config, new TypeToken<Map<String, String>>() {
|
||||
});
|
||||
if (loginParams == null) {
|
||||
return ResponseInfo.build(ResponseInfo.CODE_INVILID_PARAMS, "Error login info");
|
||||
}
|
||||
|
||||
String username = loginParams.get("username");
|
||||
String password = loginParams.get("password");
|
||||
if (username == null || password == null) {
|
||||
return ResponseInfo.build(ResponseInfo.CODE_INVILID_PARAMS, "Error username or password");
|
||||
}
|
||||
|
||||
if (username.equals(ProxyConfig.getInstance().getConfigAdminUsername()) && password.equals(ProxyConfig.getInstance().getConfigAdminPassword())) {
|
||||
token = UUID.randomUUID().toString().replace("-", "");
|
||||
return ResponseInfo.build(token);
|
||||
}
|
||||
|
||||
return ResponseInfo.build(ResponseInfo.CODE_INVILID_PARAMS, "Error username or password");
|
||||
}
|
||||
});
|
||||
|
||||
ApiRoute.addRoute("/logout", new RequestHandler() {
|
||||
|
||||
@Override
|
||||
public ResponseInfo request(FullHttpRequest request) {
|
||||
token = null;
|
||||
return ResponseInfo.build(ResponseInfo.CODE_OK, "success");
|
||||
}
|
||||
});
|
||||
|
||||
ApiRoute.addRoute("/metrics/get", new RequestHandler() {
|
||||
|
||||
@Override
|
||||
public ResponseInfo request(FullHttpRequest request) {
|
||||
return ResponseInfo.build(MetricsCollector.getAllMetrics());
|
||||
}
|
||||
});
|
||||
|
||||
ApiRoute.addRoute("/metrics/getandreset", new RequestHandler() {
|
||||
|
||||
@Override
|
||||
public ResponseInfo request(FullHttpRequest request) {
|
||||
return ResponseInfo.build(MetricsCollector.getAndResetAllMetrics());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
package org.fengfei.lanproxy.server.handlers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.fengfei.lanproxy.protocol.Constants;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessage;
|
||||
import org.fengfei.lanproxy.server.ProxyChannelManager;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fengfei
|
||||
*
|
||||
*/
|
||||
public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessage> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ServerChannelHandler.class);
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception {
|
||||
logger.debug("ProxyMessage received {}", proxyMessage.getType());
|
||||
switch (proxyMessage.getType()) {
|
||||
case ProxyMessage.TYPE_HEARTBEAT:
|
||||
handleHeartbeatMessage(ctx, proxyMessage);
|
||||
break;
|
||||
case ProxyMessage.C_TYPE_AUTH:
|
||||
handleAuthMessage(ctx, proxyMessage);
|
||||
break;
|
||||
case ProxyMessage.TYPE_CONNECT:
|
||||
handleConnectMessage(ctx, proxyMessage);
|
||||
break;
|
||||
case ProxyMessage.TYPE_DISCONNECT:
|
||||
handleDisconnectMessage(ctx, proxyMessage);
|
||||
break;
|
||||
case ProxyMessage.P_TYPE_TRANSFER:
|
||||
handleTransferMessage(ctx, proxyMessage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTransferMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
Channel userChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (userChannel != null) {
|
||||
ByteBuf buf = ctx.alloc().buffer(proxyMessage.getData().length);
|
||||
buf.writeBytes(proxyMessage.getData());
|
||||
userChannel.writeAndFlush(buf);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDisconnectMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
String clientKey = ctx.channel().attr(Constants.CLIENT_KEY).get();
|
||||
|
||||
// 代理连接没有连上服务器由控制连接发送用户端断开连接消息
|
||||
if (clientKey == null) {
|
||||
String userId = proxyMessage.getUri();
|
||||
Channel userChannel = ProxyChannelManager.removeUserChannelFromCmdChannel(ctx.channel(), userId);
|
||||
if (userChannel != null) {
|
||||
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
||||
userChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Channel cmdChannel = ProxyChannelManager.getCmdChannel(clientKey);
|
||||
if (cmdChannel == null) {
|
||||
logger.warn("ConnectMessage:error cmd channel key {}", ctx.channel().attr(Constants.CLIENT_KEY).get());
|
||||
return;
|
||||
}
|
||||
|
||||
Channel userChannel = ProxyChannelManager.removeUserChannelFromCmdChannel(cmdChannel, ctx.channel().attr(Constants.USER_ID).get());
|
||||
if (userChannel != null) {
|
||||
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
||||
userChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
ctx.channel().attr(Constants.NEXT_CHANNEL).remove();
|
||||
ctx.channel().attr(Constants.CLIENT_KEY).remove();
|
||||
ctx.channel().attr(Constants.USER_ID).remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnectMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
String uri = proxyMessage.getUri();
|
||||
if (uri == null) {
|
||||
ctx.channel().close();
|
||||
logger.warn("ConnectMessage:null uri");
|
||||
return;
|
||||
}
|
||||
|
||||
String[] tokens = uri.split("@");
|
||||
if (tokens.length != 2) {
|
||||
ctx.channel().close();
|
||||
logger.warn("ConnectMessage:error uri");
|
||||
return;
|
||||
}
|
||||
|
||||
Channel cmdChannel = ProxyChannelManager.getCmdChannel(tokens[1]);
|
||||
if (cmdChannel == null) {
|
||||
ctx.channel().close();
|
||||
logger.warn("ConnectMessage:error cmd channel key {}", tokens[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
Channel userChannel = ProxyChannelManager.getUserChannel(cmdChannel, tokens[0]);
|
||||
if (userChannel != null) {
|
||||
ctx.channel().attr(Constants.USER_ID).set(tokens[0]);
|
||||
ctx.channel().attr(Constants.CLIENT_KEY).set(tokens[1]);
|
||||
ctx.channel().attr(Constants.NEXT_CHANNEL).set(userChannel);
|
||||
userChannel.attr(Constants.NEXT_CHANNEL).set(ctx.channel());
|
||||
// 代理客户端与后端服务器连接成功,修改用户连接为可读状态
|
||||
userChannel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleHeartbeatMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
ProxyMessage heartbeatMessage = new ProxyMessage();
|
||||
heartbeatMessage.setSerialNumber(heartbeatMessage.getSerialNumber());
|
||||
heartbeatMessage.setType(ProxyMessage.TYPE_HEARTBEAT);
|
||||
logger.debug("response heartbeat message {}", ctx.channel());
|
||||
ctx.channel().writeAndFlush(heartbeatMessage);
|
||||
}
|
||||
|
||||
private void handleAuthMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
String clientKey = proxyMessage.getUri();
|
||||
List<Integer> ports = ProxyConfig.getInstance().getClientInetPorts(clientKey);
|
||||
if (ports == null) {
|
||||
logger.info("error clientKey {}, {}", clientKey, ctx.channel());
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
Channel channel = ProxyChannelManager.getCmdChannel(clientKey);
|
||||
if (channel != null) {
|
||||
logger.warn("exist channel for key {}, {}", clientKey, channel);
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("set port => channel, {}, {}, {}", clientKey, ports, ctx.channel());
|
||||
ProxyChannelManager.addCmdChannel(ports, clientKey, ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
Channel userChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (userChannel != null) {
|
||||
userChannel.config().setOption(ChannelOption.AUTO_READ, ctx.channel().isWritable());
|
||||
}
|
||||
|
||||
super.channelWritabilityChanged(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
Channel userChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (userChannel != null && userChannel.isActive()) {
|
||||
String clientKey = ctx.channel().attr(Constants.CLIENT_KEY).get();
|
||||
String userId = ctx.channel().attr(Constants.USER_ID).get();
|
||||
Channel cmdChannel = ProxyChannelManager.getCmdChannel(clientKey);
|
||||
if (cmdChannel != null) {
|
||||
ProxyChannelManager.removeUserChannelFromCmdChannel(cmdChannel, userId);
|
||||
} else {
|
||||
logger.warn("null cmdChannel, clientKey is {}", clientKey);
|
||||
}
|
||||
|
||||
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
||||
userChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
userChannel.close();
|
||||
} else {
|
||||
ProxyChannelManager.removeCmdChannel(ctx.channel());
|
||||
}
|
||||
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
logger.error("exception caught", cause);
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package org.fengfei.lanproxy.server.handlers;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.fengfei.lanproxy.protocol.Constants;
|
||||
import org.fengfei.lanproxy.protocol.ProxyMessage;
|
||||
import org.fengfei.lanproxy.server.ProxyChannelManager;
|
||||
import org.fengfei.lanproxy.server.config.ProxyConfig;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
* 处理服务端 channel.
|
||||
*/
|
||||
public class UserChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
|
||||
private static AtomicLong userIdProducer = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
|
||||
// 当出现异常就关闭连接
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
|
||||
|
||||
// 通知代理客户端
|
||||
Channel userChannel = ctx.channel();
|
||||
Channel proxyChannel = userChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (proxyChannel == null) {
|
||||
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
byte[] bytes = new byte[buf.readableBytes()];
|
||||
buf.readBytes(bytes);
|
||||
String userId = ProxyChannelManager.getUserChannelUserId(userChannel);
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.P_TYPE_TRANSFER);
|
||||
proxyMessage.setUri(userId);
|
||||
proxyMessage.setData(bytes);
|
||||
proxyChannel.writeAndFlush(proxyMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
Channel userChannel = ctx.channel();
|
||||
InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
|
||||
Channel cmdChannel = ProxyChannelManager.getCmdChannel(sa.getPort());
|
||||
|
||||
if (cmdChannel == null) {
|
||||
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
String userId = newUserId();
|
||||
String lanInfo = ProxyConfig.getInstance().getLanInfo(sa.getPort());
|
||||
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
|
||||
userChannel.config().setOption(ChannelOption.AUTO_READ, false);
|
||||
ProxyChannelManager.addUserChannelToCmdChannel(cmdChannel, userId, userChannel);
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_CONNECT);
|
||||
proxyMessage.setUri(userId);
|
||||
proxyMessage.setData(lanInfo.getBytes());
|
||||
cmdChannel.writeAndFlush(proxyMessage);
|
||||
}
|
||||
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
// 通知代理客户端
|
||||
Channel userChannel = ctx.channel();
|
||||
InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
|
||||
Channel cmdChannel = ProxyChannelManager.getCmdChannel(sa.getPort());
|
||||
if (cmdChannel == null) {
|
||||
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
|
||||
// 用户连接断开,从控制连接中移除
|
||||
String userId = ProxyChannelManager.getUserChannelUserId(userChannel);
|
||||
ProxyChannelManager.removeUserChannelFromCmdChannel(cmdChannel, userId);
|
||||
Channel proxyChannel = userChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (proxyChannel != null && proxyChannel.isActive()) {
|
||||
proxyChannel.attr(Constants.NEXT_CHANNEL).remove();
|
||||
proxyChannel.attr(Constants.CLIENT_KEY).remove();
|
||||
proxyChannel.attr(Constants.USER_ID).remove();
|
||||
|
||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
// 通知客户端,用户连接已经断开
|
||||
ProxyMessage proxyMessage = new ProxyMessage();
|
||||
proxyMessage.setType(ProxyMessage.TYPE_DISCONNECT);
|
||||
proxyMessage.setUri(userId);
|
||||
proxyChannel.writeAndFlush(proxyMessage);
|
||||
}
|
||||
}
|
||||
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
// 通知代理客户端
|
||||
Channel userChannel = ctx.channel();
|
||||
InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
|
||||
Channel cmdChannel = ProxyChannelManager.getCmdChannel(sa.getPort());
|
||||
if (cmdChannel == null) {
|
||||
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
Channel proxyChannel = userChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (proxyChannel != null) {
|
||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, userChannel.isWritable());
|
||||
}
|
||||
}
|
||||
|
||||
super.channelWritabilityChanged(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为用户连接产生ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static String newUserId() {
|
||||
return String.valueOf(userIdProducer.incrementAndGet());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package org.fengfei.lanproxy.server.metrics;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Metrics implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int port;
|
||||
|
||||
private long readBytes;
|
||||
|
||||
private long wroteBytes;
|
||||
|
||||
private long readMsgs;
|
||||
|
||||
private long wroteMsgs;
|
||||
|
||||
private int channels;
|
||||
|
||||
private long timestamp;
|
||||
|
||||
public long getReadBytes() {
|
||||
return readBytes;
|
||||
}
|
||||
|
||||
public void setReadBytes(long readBytes) {
|
||||
this.readBytes = readBytes;
|
||||
}
|
||||
|
||||
public long getWroteBytes() {
|
||||
return wroteBytes;
|
||||
}
|
||||
|
||||
public void setWroteBytes(long wroteBytes) {
|
||||
this.wroteBytes = wroteBytes;
|
||||
}
|
||||
|
||||
public long getReadMsgs() {
|
||||
return readMsgs;
|
||||
}
|
||||
|
||||
public void setReadMsgs(long readMsgs) {
|
||||
this.readMsgs = readMsgs;
|
||||
}
|
||||
|
||||
public long getWroteMsgs() {
|
||||
return wroteMsgs;
|
||||
}
|
||||
|
||||
public void setWroteMsgs(long wroteMsgs) {
|
||||
this.wroteMsgs = wroteMsgs;
|
||||
}
|
||||
|
||||
public int getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
public void setChannels(int channels) {
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package org.fengfei.lanproxy.server.metrics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class MetricsCollector {
|
||||
|
||||
private static Map<Integer, MetricsCollector> metricsCollectors = new ConcurrentHashMap<Integer, MetricsCollector>();
|
||||
|
||||
private Integer port;
|
||||
|
||||
private AtomicLong readBytes = new AtomicLong();
|
||||
|
||||
private AtomicLong wroteBytes = new AtomicLong();
|
||||
|
||||
private AtomicLong readMsgs = new AtomicLong();
|
||||
|
||||
private AtomicLong wroteMsgs = new AtomicLong();
|
||||
|
||||
private AtomicInteger channels = new AtomicInteger();
|
||||
|
||||
private MetricsCollector() {
|
||||
}
|
||||
|
||||
public static MetricsCollector getCollector(Integer port) {
|
||||
MetricsCollector collector = metricsCollectors.get(port);
|
||||
if (collector == null) {
|
||||
synchronized (metricsCollectors) {
|
||||
collector = metricsCollectors.get(port);
|
||||
if (collector == null) {
|
||||
collector = new MetricsCollector();
|
||||
collector.setPort(port);
|
||||
metricsCollectors.put(port, collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collector;
|
||||
}
|
||||
|
||||
public static List<Metrics> getAndResetAllMetrics() {
|
||||
List<Metrics> allMetrics = new ArrayList<Metrics>();
|
||||
Iterator<Entry<Integer, MetricsCollector>> ite = metricsCollectors.entrySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
allMetrics.add(ite.next().getValue().getAndResetMetrics());
|
||||
}
|
||||
|
||||
return allMetrics;
|
||||
}
|
||||
|
||||
public static List<Metrics> getAllMetrics() {
|
||||
List<Metrics> allMetrics = new ArrayList<Metrics>();
|
||||
Iterator<Entry<Integer, MetricsCollector>> ite = metricsCollectors.entrySet().iterator();
|
||||
while (ite.hasNext()) {
|
||||
allMetrics.add(ite.next().getValue().getMetrics());
|
||||
}
|
||||
|
||||
return allMetrics;
|
||||
}
|
||||
|
||||
public Metrics getAndResetMetrics() {
|
||||
Metrics metrics = new Metrics();
|
||||
metrics.setChannels(channels.get());
|
||||
metrics.setPort(port);
|
||||
metrics.setReadBytes(readBytes.getAndSet(0));
|
||||
metrics.setWroteBytes(wroteBytes.getAndSet(0));
|
||||
metrics.setTimestamp(System.currentTimeMillis());
|
||||
metrics.setReadMsgs(readMsgs.getAndSet(0));
|
||||
metrics.setWroteMsgs(wroteMsgs.getAndSet(0));
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public Metrics getMetrics() {
|
||||
Metrics metrics = new Metrics();
|
||||
metrics.setChannels(channels.get());
|
||||
metrics.setPort(port);
|
||||
metrics.setReadBytes(readBytes.get());
|
||||
metrics.setWroteBytes(wroteBytes.get());
|
||||
metrics.setTimestamp(System.currentTimeMillis());
|
||||
metrics.setReadMsgs(readMsgs.get());
|
||||
metrics.setWroteMsgs(wroteMsgs.get());
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public void incrementReadBytes(long bytes) {
|
||||
readBytes.addAndGet(bytes);
|
||||
}
|
||||
|
||||
public void incrementWroteBytes(long bytes) {
|
||||
wroteBytes.addAndGet(bytes);
|
||||
}
|
||||
|
||||
public void incrementReadMsgs(long msgs) {
|
||||
readMsgs.addAndGet(msgs);
|
||||
}
|
||||
|
||||
public void incrementWroteMsgs(long msgs) {
|
||||
wroteMsgs.addAndGet(msgs);
|
||||
}
|
||||
|
||||
public AtomicInteger getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package org.fengfei.lanproxy.server.metrics.handler;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.fengfei.lanproxy.server.metrics.MetricsCollector;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
|
||||
public class BytesMetricsHandler extends ChannelDuplexHandler {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
InetSocketAddress sa = (InetSocketAddress) ctx.channel().localAddress();
|
||||
MetricsCollector metricsCollector = MetricsCollector.getCollector(sa.getPort());
|
||||
metricsCollector.incrementReadBytes(((ByteBuf) msg).readableBytes());
|
||||
metricsCollector.incrementReadMsgs(1);
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
InetSocketAddress sa = (InetSocketAddress) ctx.channel().localAddress();
|
||||
MetricsCollector metricsCollector = MetricsCollector.getCollector(sa.getPort());
|
||||
metricsCollector.incrementWroteBytes(((ByteBuf) msg).readableBytes());
|
||||
metricsCollector.incrementWroteMsgs(1);
|
||||
super.write(ctx, msg, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
InetSocketAddress sa = (InetSocketAddress) ctx.channel().localAddress();
|
||||
MetricsCollector.getCollector(sa.getPort()).getChannels().incrementAndGet();
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
InetSocketAddress sa = (InetSocketAddress) ctx.channel().localAddress();
|
||||
MetricsCollector.getCollector(sa.getPort()).getChannels().decrementAndGet();
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
server.bind=0.0.0.0
|
||||
server.port=4900
|
||||
# 服务启动接收请求端口4900
|
||||
|
||||
# ssl是否开启,默认不开启
|
||||
server.ssl.enable=false
|
||||
server.ssl.bind=0.0.0.0
|
||||
server.ssl.port=4993
|
||||
server.ssl.jksPath=test.jks
|
||||
server.ssl.keyStorePassword=123456
|
||||
server.ssl.keyManagerPassword=123456
|
||||
server.ssl.needsClientAuth=false
|
||||
|
||||
# 8090服务端启动访问端口,账号密码
|
||||
config.server.bind=0.0.0.0
|
||||
config.server.port=8090
|
||||
config.admin.username=admin
|
||||
config.admin.password=qq755141
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
log4j.rootLogger=info,R
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.R.DatePattern='_'yyyy-MM-dd'.log'
|
||||
log4j.appender.R.File=${app.home}/logs/server.log
|
||||
log4j.appender.R.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.R.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.io.netty=warn
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
@echo off & setlocal enabledelayedexpansion
|
||||
title lanproxy-server
|
||||
cd %~dp0
|
||||
|
||||
set LIB_JARS=""
|
||||
cd ..\lib
|
||||
for %%i in (*) do set LIB_JARS=!LIB_JARS!;..\lib\%%i
|
||||
cd ..\bin
|
||||
|
||||
java -Dapp.home=../ -Xms64m -Xmx1024m -classpath ..\conf;%LIB_JARS% org.fengfei.lanproxy.server.ProxyServerContainer
|
||||
goto end
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
CONF_DIR=$DEPLOY_DIR/conf
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
|
||||
APP_MAINCLASS=org.fengfei.lanproxy.server.ProxyServerContainer
|
||||
|
||||
PIDS=`ps -ef | grep -v grep | grep "$CONF_DIR" |awk '{print $2}'`
|
||||
if [ -n "$PIDS" ]; then
|
||||
echo "ERROR: already started!"
|
||||
echo "PID: $PIDS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
CLOG_FILE=$LOGS_DIR/gc.log
|
||||
|
||||
LIB_DIR=$DEPLOY_DIR/lib
|
||||
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'| xargs | sed "s/ /:/g"`
|
||||
|
||||
JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
|
||||
JAVA_DEBUG_OPTS=""
|
||||
if [ "$1" = "debug" ]; then
|
||||
JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
|
||||
fi
|
||||
JAVA_JMX_OPTS=""
|
||||
if [ "$1" = "jmx" ]; then
|
||||
JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
|
||||
fi
|
||||
|
||||
JAVA_MEM_OPTS=""
|
||||
#JAVA_MEM_OPTS="-server -Xms5120M -Xmx5120M -Xmn1024M -Xnoclassgc -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:$CLOG_FILE"
|
||||
echo -e "Starting the proxy server ...\c"
|
||||
nohup java -Dapp.home=$DEPLOY_DIR $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS $APP_MAINCLASS >$STDOUT_FILE 2>&1 &
|
||||
sleep 1
|
||||
echo "started"
|
||||
PIDS=`ps -ef | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'`
|
||||
echo "PID: $PIDS"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
BIN_DIR=`pwd`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
|
||||
PID=`ps -ef | grep -v grep | grep "$DEPLOY_DIR/conf" | awk '{print $2}'`
|
||||
echo "PID: $PID"
|
||||
if [ -z "$PID" ]; then
|
||||
echo "ERROR: The proxy server does not started!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Stopping the proxy server ...\c"
|
||||
kill $PID > $STDOUT_FILE 2>&1
|
||||
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 1 ]; do
|
||||
echo -e ".\c"
|
||||
sleep 1
|
||||
COUNT=1
|
||||
PID_EXIST=`ps -f -p $PID | grep java`
|
||||
if [ -n "$PID_EXIST" ]; then
|
||||
COUNT=0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "stopped"
|
||||
echo "PID: $PID"
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
|||
package org.fengfei.lanproxy.server.test;
|
||||
|
||||
import org.fengfei.lanproxy.server.ProxyServerContainer;
|
||||
|
||||
public class ServerMainTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ProxyServerContainer.main(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
server.bind=0.0.0.0
|
||||
server.port=4900
|
||||
|
||||
server.ssl.enable=true
|
||||
server.ssl.bind=0.0.0.0
|
||||
server.ssl.port=4993
|
||||
server.ssl.jksPath=test.jks
|
||||
server.ssl.keyStorePassword=123456
|
||||
server.ssl.keyManagerPassword=123456
|
||||
server.ssl.needsClientAuth=false
|
||||
|
||||
config.server.bind=0.0.0.0
|
||||
config.server.port=8099
|
||||
config.admin.username=admin
|
||||
config.admin.password=admin
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
log4j.rootLogger=info,stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
#log4j.logger.io.netty=warn
|
||||
Binary file not shown.
|
|
@ -0,0 +1,18 @@
|
|||
server.bind=0.0.0.0
|
||||
server.port=4900
|
||||
# 服务启动接收请求端口4900
|
||||
|
||||
# ssl是否开启,默认不开启
|
||||
server.ssl.enable=false
|
||||
server.ssl.bind=0.0.0.0
|
||||
server.ssl.port=4993
|
||||
server.ssl.jksPath=test.jks
|
||||
server.ssl.keyStorePassword=123456
|
||||
server.ssl.keyManagerPassword=123456
|
||||
server.ssl.needsClientAuth=false
|
||||
|
||||
# 8090服务端启动访问端口,账号密码
|
||||
config.server.bind=0.0.0.0
|
||||
config.server.port=8090
|
||||
config.admin.username=admin
|
||||
config.admin.password=qq755141
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
log4j.rootLogger=info,R
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.R.DatePattern='_'yyyy-MM-dd'.log'
|
||||
log4j.appender.R.File=${app.home}/logs/server.log
|
||||
log4j.appender.R.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.R.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.io.netty=warn
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
@echo off & setlocal enabledelayedexpansion
|
||||
title lanproxy-server
|
||||
cd %~dp0
|
||||
|
||||
set LIB_JARS=""
|
||||
cd ..\lib
|
||||
for %%i in (*) do set LIB_JARS=!LIB_JARS!;..\lib\%%i
|
||||
cd ..\bin
|
||||
|
||||
java -Dapp.home=../ -Xms64m -Xmx1024m -classpath ..\conf;%LIB_JARS% org.fengfei.lanproxy.server.ProxyServerContainer
|
||||
goto end
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
CONF_DIR=$DEPLOY_DIR/conf
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
|
||||
APP_MAINCLASS=org.fengfei.lanproxy.server.ProxyServerContainer
|
||||
|
||||
PIDS=`ps -ef | grep -v grep | grep "$CONF_DIR" |awk '{print $2}'`
|
||||
if [ -n "$PIDS" ]; then
|
||||
echo "ERROR: already started!"
|
||||
echo "PID: $PIDS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
CLOG_FILE=$LOGS_DIR/gc.log
|
||||
|
||||
LIB_DIR=$DEPLOY_DIR/lib
|
||||
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'| xargs | sed "s/ /:/g"`
|
||||
|
||||
JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
|
||||
JAVA_DEBUG_OPTS=""
|
||||
if [ "$1" = "debug" ]; then
|
||||
JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
|
||||
fi
|
||||
JAVA_JMX_OPTS=""
|
||||
if [ "$1" = "jmx" ]; then
|
||||
JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
|
||||
fi
|
||||
|
||||
JAVA_MEM_OPTS=""
|
||||
#JAVA_MEM_OPTS="-server -Xms5120M -Xmx5120M -Xmn1024M -Xnoclassgc -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:$CLOG_FILE"
|
||||
echo -e "Starting the proxy server ...\c"
|
||||
nohup java -Dapp.home=$DEPLOY_DIR $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS $APP_MAINCLASS >$STDOUT_FILE 2>&1 &
|
||||
sleep 1
|
||||
echo "started"
|
||||
PIDS=`ps -ef | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'`
|
||||
echo "PID: $PIDS"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
BIN_DIR=`pwd`
|
||||
cd ..
|
||||
DEPLOY_DIR=`pwd`
|
||||
LOGS_DIR=$DEPLOY_DIR/logs
|
||||
if [ ! -d $LOGS_DIR ]; then
|
||||
mkdir $LOGS_DIR
|
||||
fi
|
||||
STDOUT_FILE=$LOGS_DIR/stdout.log
|
||||
|
||||
PID=`ps -ef | grep -v grep | grep "$DEPLOY_DIR/conf" | awk '{print $2}'`
|
||||
echo "PID: $PID"
|
||||
if [ -z "$PID" ]; then
|
||||
echo "ERROR: The proxy server does not started!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Stopping the proxy server ...\c"
|
||||
kill $PID > $STDOUT_FILE 2>&1
|
||||
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 1 ]; do
|
||||
echo -e ".\c"
|
||||
sleep 1
|
||||
COUNT=1
|
||||
PID_EXIST=`ps -f -p $PID | grep java`
|
||||
if [ -n "$PID_EXIST" ]; then
|
||||
COUNT=0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "stopped"
|
||||
echo "PID: $PID"
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
|||
#Generated by Maven
|
||||
#Wed Jul 05 11:03:48 CST 2023
|
||||
groupId=org.fengfei
|
||||
artifactId=proxy-server
|
||||
version=0.1
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
org\fengfei\lanproxy\server\config\web\HttpRequestHandler.class
|
||||
org\fengfei\lanproxy\server\config\web\RequestMiddleware.class
|
||||
org\fengfei\lanproxy\server\config\web\ResponseInfo.class
|
||||
org\fengfei\lanproxy\server\config\web\WebConfigContainer$1.class
|
||||
org\fengfei\lanproxy\server\config\ProxyConfig$1.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$1.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$3$1.class
|
||||
org\fengfei\lanproxy\server\config\ProxyConfig$Client.class
|
||||
org\fengfei\lanproxy\server\ProxyServerContainer.class
|
||||
org\fengfei\lanproxy\server\SslContextCreator.class
|
||||
org\fengfei\lanproxy\server\metrics\handler\BytesMetricsHandler.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$3.class
|
||||
org\fengfei\lanproxy\server\ProxyServerContainer$2.class
|
||||
org\fengfei\lanproxy\server\config\web\exception\ContextException.class
|
||||
org\fengfei\lanproxy\server\config\web\ApiRoute.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$5.class
|
||||
org\fengfei\lanproxy\server\metrics\Metrics.class
|
||||
org\fengfei\lanproxy\server\config\web\RequestHandler.class
|
||||
org\fengfei\lanproxy\server\handlers\ServerChannelHandler.class
|
||||
org\fengfei\lanproxy\server\config\ProxyConfig.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$2.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$4$1.class
|
||||
org\fengfei\lanproxy\server\config\web\WebConfigContainer.class
|
||||
org\fengfei\lanproxy\server\ProxyChannelManager.class
|
||||
org\fengfei\lanproxy\server\ProxyServerContainer$3.class
|
||||
org\fengfei\lanproxy\server\config\ProxyConfig$ConfigChangedListener.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$7.class
|
||||
org\fengfei\lanproxy\server\handlers\UserChannelHandler.class
|
||||
org\fengfei\lanproxy\server\ProxyChannelManager$1.class
|
||||
org\fengfei\lanproxy\server\ProxyServerContainer$1.class
|
||||
org\fengfei\lanproxy\server\config\ProxyConfig$ClientProxyMapping.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$4.class
|
||||
org\fengfei\lanproxy\server\config\web\MimeType.class
|
||||
org\fengfei\lanproxy\server\metrics\MetricsCollector.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig.class
|
||||
org\fengfei\lanproxy\server\config\web\routes\RouteConfig$6.class
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\metrics\MetricsCollector.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\RequestMiddleware.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\handlers\ServerChannelHandler.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\metrics\handler\BytesMetricsHandler.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\ProxyConfig.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\RequestHandler.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\metrics\Metrics.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\routes\RouteConfig.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\handlers\UserChannelHandler.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\ApiRoute.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\MimeType.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\ProxyChannelManager.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\exception\ContextException.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\HttpRequestHandler.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\ResponseInfo.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\WebConfigContainer.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\SslContextCreator.java
|
||||
E:\yx\lanproxy-0.1\proxy-server\src\main\java\org\fengfei\lanproxy\server\ProxyServerContainer.java
|
||||
|
|
@ -0,0 +1 @@
|
|||
org\fengfei\lanproxy\server\test\ServerMainTest.class
|
||||
|
|
@ -0,0 +1 @@
|
|||
E:\yx\lanproxy-0.1\proxy-server\src\test\java\org\fengfei\lanproxy\server\test\ServerMainTest.java
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
server.bind=0.0.0.0
|
||||
server.port=4900
|
||||
|
||||
server.ssl.enable=true
|
||||
server.ssl.bind=0.0.0.0
|
||||
server.ssl.port=4993
|
||||
server.ssl.jksPath=test.jks
|
||||
server.ssl.keyStorePassword=123456
|
||||
server.ssl.keyManagerPassword=123456
|
||||
server.ssl.needsClientAuth=false
|
||||
|
||||
config.server.bind=0.0.0.0
|
||||
config.server.port=8099
|
||||
config.admin.username=admin
|
||||
config.admin.password=admin
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
log4j.rootLogger=info,stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
#log4j.logger.io.netty=warn
|
||||
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>404 - LanProxy</title>
|
||||
</head>
|
||||
<body>404
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue