Compare commits

..
21 changed files with 346 additions and 235 deletions
@@ -50,12 +50,11 @@ export default {
this.chartDom = document.getElementById(this.chartId)
this.myChart = echarts.init(this.chartDom)
const seriesList = []
const legendList = []
this.data.list && this.data.list.forEach((item, index) => {
seriesList.push({
name: item.name,
type: 'line',
stack: 'Total',
// stack: 'Total',
data: item.value,
areaStyle: {
normal: {
@@ -74,7 +73,6 @@ export default {
},
smooth: true
})
legendList.push(item.name)
})
const option = {
@@ -87,13 +85,13 @@ export default {
formatter: (value) => {
let title = this.data.text + '<br/>'
value.forEach(item => {
title = title + item.marker + item.seriesName + ' : ' + this.data.list[item.seriesIndex].label[item.dataIndex] + '<br/>'
title = title + item.marker + item.seriesName + ' : ' + getSizeDescByByteCount(item.data) + '<br/>'
})
return title
}
},
legend: {
data: legendList,
data: this.data.legendList,
left: 'right'
},
grid: {
@@ -70,37 +70,19 @@ export default {
})
},
getChartData(last7dFlow) {
const title = []
const downFlowDesc = []
const totalFlowDesc = []
const upFlowDesc = []
last7dFlow.dataList.forEach(item => {
title.push(item.dateStr)
totalFlowDesc.push(item.totalFlowDesc)
downFlowDesc.push(item.downFlowDesc)
upFlowDesc.push(item.upFlowDesc)
})
const list = []
last7dFlow.seriesList.forEach(item => {
let label = []
if (item.seriesName.indexOf('上') > -1) {
label = upFlowDesc
} else if (item.seriesName.indexOf('下') > -1) {
label = downFlowDesc
} else if (item.seriesName.indexOf('总') > -1) {
label = totalFlowDesc
}
list.push({
name: item.seriesName,
value: item.seriesData,
label: label
value: item.seriesData
})
})
return {
text: '流量监控',
subtext: `最近${last7dFlow.dataList.length || 0}天流量监控`,
title: title,
list: list
title: last7dFlow.xDate,
list: list,
legendList: last7dFlow.legendData
}
}
}
@@ -73,13 +73,15 @@ public class ClientChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
switch (event.state()) {
case READER_IDLE:
// 读超时,断开连接
log.info("读超时");
ctx.channel().close();
// log.info("读超时");
// ctx.channel().close();
break;
case WRITER_IDLE:
ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage());
break;
case ALL_IDLE:
log.info("读写超时");
ctx.channel().close();
break;
}
}
@@ -10,8 +10,8 @@ neutrino:
initial-bytes-to-strip: 0
length-adjustment: 0
read-idle-time: 40
write-idle-time: 8
all-idle-time-seconds: 0
write-idle-time: 5
all-idle-time-seconds: 45
client:
thread-count: 50
key-store-password: ${STORE_PASS:123456}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_FILE" value="/work/projects/neutrino-proxy-client/app.log"/>
<property name="LOG_FILE" value="./neutrino-proxy-client.log"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
<!-- <property name="ENCODE" value="utf8" />-->
@@ -64,8 +64,7 @@ public class DBInitialize implements EventListener<AppLoadEndEvent> {
@Override
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
// TODO 该事件有50%的概率不触发
System.out.println("11");
}
/**
@@ -18,10 +18,15 @@ public class ProxyConfig {
@Inject("${neutrino.proxy.protocol}")
private Protocol protocol;
/**
* 服务配置
* 代理服务配置
*/
@Inject("${neutrino.proxy.server}")
private Server server;
/**
* 代理隧道配置
*/
@Inject("${neutrino.proxy.tunnel}")
private Tunnel tunnel;
@Data
public static class Protocol {
@@ -37,15 +42,24 @@ public class ProxyConfig {
@Data
public static class Server {
private Integer bossThreadCount;
private Integer workThreadCount;
private String domainName;
private Integer httpProxyPort;
private Integer httpsProxyPort;
private String keyStorePassword;
private String jksPath;
}
@Data
public static class Tunnel {
private Integer bossThreadCount;
private Integer workThreadCount;
private Integer port;
private Integer sslPort;
private String keyStorePassword;
private String keyManagerPassword;
private String jksPath;
private Integer bossThreadCount;
private Integer workThreadCount;
private String domainName;
private Integer httpProxyPort;
}
}
@@ -43,4 +43,14 @@ public class ProxyConfiguration implements LifecycleBean {
return new NioEventLoopGroup(proxyConfig.getServer().getWorkThreadCount());
}
@Bean("tunnelBossGroup")
public NioEventLoopGroup tunnelBossGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getTunnel().getBossThreadCount());
}
@Bean("tunnelWorkerGroup")
public NioEventLoopGroup tunnelWorkerGroup(@Inject ProxyConfig proxyConfig) {
return new NioEventLoopGroup(proxyConfig.getTunnel().getWorkThreadCount());
}
}
@@ -2,28 +2,17 @@ package org.dromara.neutrinoproxy.server.proxy.core;
import cn.hutool.core.util.StrUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.neutrinoproxy.core.Constants;
import org.dromara.neutrinoproxy.core.ProxyMessage;
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
import org.dromara.neutrinoproxy.server.service.FlowReportService;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.event.AppLoadEndEvent;
import org.noear.solon.core.event.EventListener;
import java.net.InetSocketAddress;
/**
* @author: aoshiguchen
* @date: 2023/4/2
@@ -54,7 +43,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new VisitorChannelHandler());
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName()));
}
});
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync();
@@ -63,142 +52,4 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
log.error("http proxy start err!", e);
}
}
private class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
ctx.channel().close();
return;
}
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
byteBuf.resetReaderIndex();
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
if (null == proxyChannel) {
// 该端口还没有代理客户端
ctx.channel().close();
return;
}
proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(ProxyUtil.getVisitorIdByChannel(channel), bytes));
// 增加流量计数
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(channel);
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
});
String visitorId = ProxyUtil.getVisitorIdByChannel(ctx.channel());
if (StringUtils.isNotBlank(visitorId)) {
proxyAttachment.execute();
return;
}
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
ctx.channel().config().setOption(ChannelOption.AUTO_READ, false);
String host = getHost(bytes);
if (StringUtils.isBlank(host)) {
ctx.channel().close();
return;
}
log.debug("HttpProxy host: {}", host);
if (!host.endsWith(proxyConfig.getServer().getDomainName())) {
ctx.channel().close();
return;
}
int index = host.lastIndexOf("." + proxyConfig.getServer().getDomainName());
String subdomain = host.substring(0, index);
// 根据域名拿到绑定的映射对应的cmdChannel
Integer serverPort = ProxyUtil.getServerPortBySubdomain(subdomain);
if (null == serverPort) {
ctx.channel().close();
return;
}
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(serverPort);
if (null == cmdChannel) {
ctx.channel().close();
return;
}
String lanInfo = ProxyUtil.getClientLanInfoByServerPort(serverPort);
if (StringUtils.isBlank(lanInfo)) {
ctx.channel().close();
return;
}
visitorId = ProxyUtil.newVisitorId();
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, ctx.channel(), serverPort);
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 通知代理客户端
Channel visitorChannel = ctx.channel();
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
if (cmdChannel == null) {
// 该端口还没有代理客户端
ctx.channel().close();
} else {
// 用户连接断开,从控制连接中移除
String visitorId = ProxyUtil.getVisitorIdByChannel(visitorChannel);
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
// 删除代理附加对象
ProxyUtil.remoteProxyConnectAttachment(visitorId);
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
if (proxyChannel != null && proxyChannel.isActive()) {
proxyChannel.attr(Constants.NEXT_CHANNEL).remove();
proxyChannel.attr(Constants.LICENSE_ID).remove();
proxyChannel.attr(Constants.VISITOR_ID).remove();
proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);
// 通知客户端,用户连接已经断开
proxyChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(visitorId));
}
}
super.channelInactive(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 当出现异常就关闭连接
ctx.close();
}
private String getHost(byte[] buf) {
String req = new String(buf);
String[] lines = req.split("\r\n");
String firstLine = lines[0];
if (!(firstLine.endsWith("HTTP/1.1") || firstLine.endsWith("HTTP/1.0"))) {
return null;
}
for (int i = 1; i < lines.length; i++) {
String line = lines[i];
if (!line.startsWith("Host: ")) {
continue;
}
// 域名
String domain = line.substring(6);
return domain;
}
return null;
}
}
}
@@ -0,0 +1,170 @@
package org.dromara.neutrinoproxy.server.proxy.core;
import cn.hutool.core.util.StrUtil;
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;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.neutrinoproxy.core.Constants;
import org.dromara.neutrinoproxy.core.ProxyMessage;
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
import org.dromara.neutrinoproxy.server.service.FlowReportService;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
import org.noear.solon.Solon;
import java.net.InetSocketAddress;
/**
* @author: aoshiguchen
* @date: 2023/5/27
*/
@Slf4j
public class HttpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 域名
*/
private String domainName;
public HttpVisitorChannelHandler(String domainName) {
this.domainName = domainName;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
if (StrUtil.isBlank(domainName)) {
ctx.channel().close();
return;
}
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
byteBuf.resetReaderIndex();
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
if (null == proxyChannel) {
// 该端口还没有代理客户端
ctx.channel().close();
return;
}
proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(ProxyUtil.getVisitorIdByChannel(channel), bytes));
// 增加流量计数
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(channel);
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
});
String visitorId = ProxyUtil.getVisitorIdByChannel(ctx.channel());
if (StringUtils.isNotBlank(visitorId)) {
proxyAttachment.execute();
return;
}
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
ctx.channel().config().setOption(ChannelOption.AUTO_READ, false);
String host = getHost(bytes);
if (StringUtils.isBlank(host)) {
ctx.channel().close();
return;
}
log.debug("HttpProxy host: {}", host);
if (!host.endsWith(domainName)) {
ctx.channel().close();
return;
}
int index = host.lastIndexOf("." + domainName);
String subdomain = host.substring(0, index);
// 根据域名拿到绑定的映射对应的cmdChannel
Integer serverPort = ProxyUtil.getServerPortBySubdomain(subdomain);
if (null == serverPort) {
ctx.channel().close();
return;
}
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(serverPort);
if (null == cmdChannel) {
ctx.channel().close();
return;
}
String lanInfo = ProxyUtil.getClientLanInfoByServerPort(serverPort);
if (StringUtils.isBlank(lanInfo)) {
ctx.channel().close();
return;
}
visitorId = ProxyUtil.newVisitorId();
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, ctx.channel(), serverPort);
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 通知代理客户端
Channel visitorChannel = ctx.channel();
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
if (cmdChannel == null) {
// 该端口还没有代理客户端
ctx.channel().close();
} else {
// 用户连接断开,从控制连接中移除
String visitorId = ProxyUtil.getVisitorIdByChannel(visitorChannel);
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
// 删除代理附加对象
ProxyUtil.remoteProxyConnectAttachment(visitorId);
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
if (proxyChannel != null && proxyChannel.isActive()) {
proxyChannel.attr(Constants.NEXT_CHANNEL).remove();
proxyChannel.attr(Constants.LICENSE_ID).remove();
proxyChannel.attr(Constants.VISITOR_ID).remove();
proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);
// 通知客户端,用户连接已经断开
proxyChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(visitorId));
}
}
super.channelInactive(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 当出现异常就关闭连接
ctx.close();
}
private String getHost(byte[] buf) {
String req = new String(buf);
String[] lines = req.split("\r\n");
String firstLine = lines[0];
if (!(firstLine.endsWith("HTTP/1.1") || firstLine.endsWith("HTTP/1.0"))) {
return null;
}
for (int i = 1; i < lines.length; i++) {
String line = lines[i];
if (!line.startsWith("Host: ")) {
continue;
}
// 域名
String domain = line.substring(6);
return domain;
}
return null;
}
}
@@ -0,0 +1,89 @@
package org.dromara.neutrinoproxy.server.proxy.core;
import cn.hutool.core.util.StrUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.neutrinoproxy.core.util.FileUtil;
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.event.AppLoadEndEvent;
import org.noear.solon.core.event.EventListener;
import javax.net.ssl.*;
import java.io.InputStream;
import java.security.KeyStore;
/**
* @author: aoshiguchen
* @date: 2023/4/2
*/
@Slf4j
@Component
public class HttpsProxy implements EventListener<AppLoadEndEvent> {
@Inject("serverBossGroup")
private NioEventLoopGroup serverBossGroup;
@Inject("serverWorkerGroup")
private NioEventLoopGroup serverWorkerGroup;
@Inject
private ProxyConfig proxyConfig;
@Override
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName()) || null == proxyConfig.getServer().getHttpsProxyPort() ||
StringUtils.isEmpty(proxyConfig.getServer().getJksPath()) || StringUtils.isEmpty(proxyConfig.getServer().getKeyStorePassword())) {
log.info("no config domain name,nonsupport https proxy.");
return;
}
this.start();
}
private void start() {
try {
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(createSslHandler());
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName()));
}
});
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpsProxyPort()).sync();
log.info("Https代理服务启动成功!");
} catch (Exception e) {
log.error("https proxy start err!", e);
}
}
private ChannelHandler createSslHandler() {
try {
InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getServer().getJksPath());
SSLContext serverContext = SSLContext.getInstance("TLS");
final KeyStore ks = KeyStore.getInstance("JKS");
ks.load(jksInputStream, proxyConfig.getServer().getKeyStorePassword().toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, proxyConfig.getServer().getKeyStorePassword().toCharArray());
TrustManager[] trustManagers = null;
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
SSLEngine sslEngine = serverContext.createSSLEngine();
sslEngine.setUseClientMode(false);
sslEngine.setNeedClientAuth(false);
return new SslHandler(sslEngine);
} catch (Exception e) {
log.error("创建SSL处理器失败", e);
e.printStackTrace();
}
return null;
}
}
@@ -36,20 +36,10 @@ import java.security.KeyStore;
public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
@Inject
private ProxyConfig proxyConfig;
@Inject("serverBossGroup")
@Inject("tunnelBossGroup")
private NioEventLoopGroup serverBossGroup;
@Inject("serverWorkerGroup")
@Inject("tunnelWorkerGroup")
private NioEventLoopGroup serverWorkerGroup;
@Inject("${neutrino.proxy.server.port}")
private Integer port;
@Inject("${neutrino.proxy.server.ssl-port}")
private Integer sslPort;
@Inject("${neutrino.proxy.server.jks-path}")
private String jksPath;
@Inject("${neutrino.proxy.server.key-store-password}")
private String keyStorePassword;
@Inject("${neutrino.proxy.server.key-manager-password}")
private String keyManagerPassword;
@Override
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
startProxyServer();
@@ -68,15 +58,15 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
}
});
try {
bootstrap.bind(port).sync();
log.info("代理服务启动,端口:{}", port);
bootstrap.bind(proxyConfig.getTunnel().getPort()).sync();
log.info("代理服务启动,端口:{}", proxyConfig.getTunnel().getPort());
} catch (Exception e) {
log.error("代理服务异常", e);
}
}
private void startProxyServerForSSL() {
if (null == sslPort) {
if (null == proxyConfig.getTunnel().getSslPort()) {
return;
}
ServerBootstrap bootstrap = new ServerBootstrap();
@@ -89,8 +79,8 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
}
});
try {
bootstrap.bind(sslPort).sync();
log.info("代理服务启动,SSL端口: {}", sslPort);
bootstrap.bind(proxyConfig.getTunnel().getSslPort()).sync();
log.info("代理服务启动,SSL端口: {}", proxyConfig.getTunnel().getSslPort());
} catch (Exception e) {
log.error("代理服务异常", e);
}
@@ -98,13 +88,13 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
private ChannelHandler createSslHandler() {
try {
InputStream jksInputStream = FileUtil.getInputStream(jksPath);
InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getTunnel().getJksPath());
SSLContext serverContext = SSLContext.getInstance("TLS");
final KeyStore ks = KeyStore.getInstance("JKS");
ks.load(jksInputStream, keyStorePassword.toCharArray());
ks.load(jksInputStream, proxyConfig.getTunnel().getKeyStorePassword().toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyManagerPassword.toCharArray());
kmf.init(ks, proxyConfig.getTunnel().getKeyManagerPassword().toCharArray());
TrustManager[] trustManagers = null;
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
@@ -71,19 +71,21 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel userChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
if (userChannel != null && userChannel.isActive()) {
Channel visitorChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
if (null != visitorChannel) {
Integer licenseId = ctx.channel().attr(Constants.LICENSE_ID).get();
String visitorId = ctx.channel().attr(Constants.VISITOR_ID).get();
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseId);
if (cmdChannel != null) {
if (null != cmdChannel) {
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
}
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
userChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
userChannel.close();
if (visitorChannel.isActive()) {
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
visitorChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
visitorChannel.close();
}
} else {
CmdChannelAttachInfo cmdChannelAttachInfo = ProxyUtil.getAttachInfo(ctx.channel());
if (null != cmdChannelAttachInfo) {
@@ -96,8 +98,8 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
.setCode(SuccessCodeEnum.SUCCESS.getCode())
.setCreateTime(new Date())
);
ProxyUtil.removeCmdChannel(ctx.channel());
}
ProxyUtil.removeCmdChannel(ctx.channel());
}
super.channelInactive(ctx);
@@ -122,7 +124,6 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
ctx.channel().close();
break;
case WRITER_IDLE:
log.info("写超时");
break;
case ALL_IDLE:
break;
@@ -4,7 +4,6 @@ import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.neutrinoproxy.core.Constants;
import org.dromara.neutrinoproxy.core.ProxyMessage;
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
import org.dromara.neutrinoproxy.server.service.FlowReportService;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
@@ -23,7 +22,7 @@ import java.net.InetSocketAddress;
* @date: 2022/6/16
*/
@Slf4j
public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
@@ -91,8 +90,7 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf>
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
if (cmdChannel == null) {
if (null == cmdChannel) {
// 该端口还没有代理客户端
ctx.channel().close();
} else {
@@ -127,12 +125,12 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf>
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
if (cmdChannel == null) {
if (null == cmdChannel) {
// 该端口还没有代理客户端
ctx.channel().close();
} else {
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
if (proxyChannel != null) {
if (null != proxyChannel) {
proxyChannel.config().setOption(ChannelOption.AUTO_READ, visitorChannel.isWritable());
}
}
@@ -2,6 +2,7 @@ package org.dromara.neutrinoproxy.server.service;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
import org.dromara.neutrinoproxy.server.controller.res.system.ProtocalListRes;
import org.noear.solon.annotation.Component;
@@ -22,7 +23,7 @@ public class ProtocalService {
public List<ProtocalListRes> list() {
return Lists.newArrayList(
new ProtocalListRes().setName("TCP").setEnable(Boolean.TRUE).setRemark("支持一切TCP之上的协议"),
new ProtocalListRes().setName("HTTP").setEnable(Boolean.TRUE).setRemark("支持绑定子域名,未绑定时等价于时使用TCP"),
new ProtocalListRes().setName("HTTP(S)").setEnable(Boolean.TRUE).setRemark("支持绑定子域名,未绑定时等价于时使用TCP。 若配置了证书,则同时支持HTTPS。"),
new ProtocalListRes().setName("UDP").setEnable(Boolean.FALSE).setRemark("暂不支持")
);
}
@@ -14,7 +14,7 @@ import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO;
import org.dromara.neutrinoproxy.server.dal.entity.PortPoolDO;
import org.dromara.neutrinoproxy.server.dal.entity.UserDO;
import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler;
import org.dromara.neutrinoproxy.server.proxy.core.VisitorChannelHandler;
import org.dromara.neutrinoproxy.server.proxy.core.TcpVisitorChannelHandler;
import org.dromara.neutrinoproxy.server.proxy.domain.CmdChannelAttachInfo;
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
@@ -224,7 +224,7 @@ public class VisitorChannelService {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addFirst(new BytesMetricsHandler());
ch.pipeline().addLast(new VisitorChannelHandler());
ch.pipeline().addLast(new TcpVisitorChannelHandler());
}
});
@@ -10,19 +10,25 @@ neutrino:
initial-bytes-to-strip: 0
length-adjustment: 0
read-idle-time: 40
write-idle-time: 10
write-idle-time: 5
all-idle-time-seconds: 0
server:
boss-thread-count: 10
work-thread-count: 60
tunnel:
boss-thread-count: 2
work-thread-count: 10
port: ${OPEN_PORT:9000}
ssl-port: ${SSL_PORT:9002}
key-store-password: ${STORE_PASS:123456}
key-manager-password: ${MGR_PASS:123456}
jks-path: ${JKS_PATH:classpath:/test.jks}
server:
boss-thread-count: 5
work-thread-count: 20
http-proxy-port: ${HTTP_PROXY_PORT:80}
https-proxy-port: ${HTTPS_PROXY_PORT:443}
# 如果不配置,则不支持域名映射
domain-name: ${DOMAIN_NAME:}
key-store-password: ${HTTPS_STORE_PASS:}
jks-path: ${HTTPS_JKS_PATH:}
data:
db:
type: ${DB_TYPE:sqlite}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_FILE" value="/work/projects/neutrino-proxy-server/app.log"/>
<property name="LOG_FILE" value="./neutrino-proxy-server.log"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
<!-- <property name="ENCODE" value="utf8" />-->
@@ -46,8 +46,8 @@ registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest
- 在服务器上新建部署目录:`/work/projects/neutrino-proxy-server`
-` neutrino-proxy-server.jar``neutrino-proxy-admin.zip`上传至服务器部署目录。
- 解压`neutrino-proxy-admin.zip`文件
- 执行命令`java -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用sqlite数据库。
- 若需要指定自己的mysql数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署
- 执行命令`java -Dfile.encoding=utf-8 -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用sqlite数据库。
- 若需要指定自己的mysql数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -Dfile.encoding=utf-8 -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署
- 可参照 https://gitee.com/dromara/neutrino-proxy/blob/master/bin/server_start.sh 使用shell脚本启动服务端。
## 2、管理后台配置
+1 -1
View File
@@ -37,7 +37,7 @@ cp $OUT $JAR_PATH/logs/back_$time.out
fi
rm -f $OUT
cd $JAR_PATH
nohup java $JAVA_OPS -jar $NAME.jar $startupParams > $OUT 2>&1 &
nohup java -Dfile.encoding=utf-8 $JAVA_OPS -jar $NAME.jar $startupParams > $OUT 2>&1 &
echo "sleep 15s wating service start"
sleep 15
tail -200 $OUT
+1 -1
View File
@@ -29,7 +29,7 @@ cp $OUT $JAR_PATH/logs/back_$time.out
fi
rm -f $OUT
cd $JAR_PATH
nohup java $JAVA_OPS -jar $NAME.jar > $OUT 2>&1 &
nohup java -Dfile.encoding=utf-8 $JAVA_OPS -jar $NAME.jar > $OUT 2>&1 &
echo "sleep 15s wating service start"
sleep 15
tail -200 $OUT