diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java index 928db99f..60f89e23 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java @@ -42,14 +42,8 @@ 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; - private Boolean transferLogEnable; + private Tcp tcp; + private Udp udp; } @Data @@ -65,4 +59,22 @@ public class ProxyConfig { private Boolean heartbeatLogEnable; } + @Data + public static class Tcp { + private Integer bossThreadCount; + private Integer workThreadCount; + private String domainName; + private Integer httpProxyPort; + private Integer httpsProxyPort; + private String keyStorePassword; + private String jksPath; + private Boolean transferLogEnable; + } + + @Data + public static class Udp { + private Integer bossThreadCount; + private Integer workThreadCount; + private Boolean transferLogEnable; + } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java index 14dd698b..3188e182 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java @@ -1,8 +1,12 @@ package org.dromara.neutrinoproxy.server.base.proxy; +import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LoggingHandler; import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; @@ -13,8 +17,8 @@ import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.nio.NioEventLoopGroup; import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler; -import org.dromara.neutrinoproxy.server.proxy.core.ProxyTunnelServer; import org.dromara.neutrinoproxy.server.proxy.core.TcpVisitorChannelHandler; +import org.dromara.neutrinoproxy.server.proxy.core.UdpVisitorChannelHandler; import org.noear.solon.Solon; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; @@ -35,43 +39,80 @@ public class ProxyConfiguration implements LifecycleBean { public void start() throws Throwable { List list = Solon.context().getBeansOfType(ProxyMessageHandler.class); Dispatcher dispatcher = new DefaultDispatcher<>("消息调度器", list, - proxyMessage -> ProxyDataTypeEnum.of((int)proxyMessage.getType()) == null ? - null : ProxyDataTypeEnum.of((int)proxyMessage.getType()).getName()); + proxyMessage -> ProxyDataTypeEnum.of((int)proxyMessage.getType()) == null ? + null : ProxyDataTypeEnum.of((int)proxyMessage.getType()).getName()); Solon.context().wrapAndPut(Dispatcher.class, dispatcher); } - @Bean("serverBossGroup") - public NioEventLoopGroup serverBossGroup(@Inject ProxyConfig proxyConfig) { - return new NioEventLoopGroup(proxyConfig.getServer().getBossThreadCount()); + @Bean("tcpServerBossGroup") + public NioEventLoopGroup tcpServerBossGroup(@Inject ProxyConfig proxyConfig) { + return new NioEventLoopGroup(proxyConfig.getServer().getTcp().getBossThreadCount()); } - @Bean("serverWorkerGroup") - public NioEventLoopGroup serverWorkerGroup(@Inject ProxyConfig proxyConfig) { - return new NioEventLoopGroup(proxyConfig.getServer().getWorkThreadCount()); + @Bean("tcpServerWorkerGroup") + public NioEventLoopGroup tcpServerWorkerGroup(@Inject ProxyConfig proxyConfig) { + return new NioEventLoopGroup(proxyConfig.getServer().getTcp().getWorkThreadCount()); } @Bean("tcpServerBootstrap") - public ServerBootstrap tcpServerBootstrap(@Inject("serverBossGroup") NioEventLoopGroup serverBossGroup, - @Inject("serverWorkerGroup") NioEventLoopGroup serverWorkerGroup, + public ServerBootstrap tcpServerBootstrap(@Inject("tcpServerBossGroup") NioEventLoopGroup tcpServerBossGroup, + @Inject("tcpServerWorkerGroup") NioEventLoopGroup tcpServerWorkerGroup, @Inject ProxyConfig proxyConfig ) { ServerBootstrap bootstrap = new ServerBootstrap(); - bootstrap.group(serverBossGroup, serverWorkerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - if (null != proxyConfig.getServer().getTransferLogEnable() && proxyConfig.getServer().getTransferLogEnable()) { - ch.pipeline().addFirst(new LoggingHandler(TcpVisitorChannelHandler.class)); - } - ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new TcpVisitorChannelHandler()); + bootstrap.group(tcpServerBossGroup, tcpServerWorkerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(TcpVisitorChannelHandler.class)); + } + ch.pipeline().addFirst(new BytesMetricsHandler()); + ch.pipeline().addLast(new TcpVisitorChannelHandler()); } }); return bootstrap; } + @Bean("udpServerBossGroup") + private NioEventLoopGroup udpBossGroup(@Inject ProxyConfig proxyConfig) { + return new NioEventLoopGroup(proxyConfig.getServer().getUdp().getBossThreadCount()); + } + + @Bean("udpServerWorkerGroup") + private NioEventLoopGroup udpWorkerGroup(@Inject ProxyConfig proxyConfig) { + return new NioEventLoopGroup(proxyConfig.getServer().getUdp().getWorkThreadCount()); + } + + @Bean("udpServerBootstrap") + public Bootstrap udpBootstrap(@Inject("udpServerBossGroup") NioEventLoopGroup udpServerBossGroup, + @Inject("udpServerWorkerGroup") NioEventLoopGroup udpServerWorkerGroup, + @Inject ProxyConfig proxyConfig) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(udpServerBossGroup) + // 主线程处理 + .channel(NioDatagramChannel.class) + // 广播 + .option(ChannelOption.SO_BROADCAST, true) + // 设置读缓冲区为2M + .option(ChannelOption.SO_RCVBUF, 2048 * 1024) + // 设置写缓冲区为1M + .option(ChannelOption.SO_SNDBUF, 1024 * 1024) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(NioDatagramChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + if (null != proxyConfig.getServer().getUdp().getTransferLogEnable() && proxyConfig.getServer().getUdp().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(UdpVisitorChannelHandler.class)); + } + pipeline.addLast(udpServerWorkerGroup, new UdpVisitorChannelHandler()); + } + }); + return bootstrap; + } + @Bean("tunnelBossGroup") public NioEventLoopGroup tunnelBossGroup(@Inject ProxyConfig proxyConfig) { return new NioEventLoopGroup(proxyConfig.getTunnel().getBossThreadCount()); diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java index 3ff09d03..3f64d661 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java @@ -21,7 +21,7 @@ public class DomainNameController { @Get @Mapping("/bind-info") public String bindInfo () { - return proxyConfig.getServer().getDomainName(); + return proxyConfig.getServer().getTcp().getDomainName(); } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/UdpVisitorChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/UdpVisitorChannelHandler.java new file mode 100644 index 00000000..26492d10 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/UdpVisitorChannelHandler.java @@ -0,0 +1,18 @@ +package org.dromara.neutrinoproxy.server.proxy.core; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.socket.DatagramPacket; + +/** + * @author: aoshiguchen + * @date: 2023/9/16 + */ +public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception { + + } + +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java index 742be02f..9c8716c9 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java @@ -28,7 +28,7 @@ public class HttpProxy implements EventListener { private ProxyConfig proxyConfig; @Override public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable { - if (StrUtil.isBlank(proxyConfig.getServer().getDomainName()) || null == proxyConfig.getServer().getHttpProxyPort()) { + if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName()) || null == proxyConfig.getServer().getTcp().getHttpProxyPort()) { log.info("no config domain name,nonsupport http proxy."); return; } @@ -42,15 +42,15 @@ public class HttpProxy implements EventListener { .channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) throws Exception { - if (null != proxyConfig.getServer().getTransferLogEnable() && proxyConfig.getServer().getTransferLogEnable()) { + if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) { ch.pipeline().addFirst(new LoggingHandler(HttpProxy.class)); } ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName())); + ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); } }); - bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync(); - log.info("Http代理服务启动成功!port:{}", proxyConfig.getServer().getHttpProxyPort()); + bootstrap.bind("0.0.0.0", proxyConfig.getServer().getTcp().getHttpProxyPort()).sync(); + log.info("Http代理服务启动成功!port:{}", proxyConfig.getServer().getTcp().getHttpProxyPort()); } catch (Exception e) { log.error("http proxy start err!", e); } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java index 85699f3d..281cfda0 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java @@ -35,8 +35,8 @@ public class HttpsProxy implements EventListener { 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())) { + if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName()) || null == proxyConfig.getServer().getTcp().getHttpsProxyPort() || + StringUtils.isEmpty(proxyConfig.getServer().getTcp().getJksPath()) || StringUtils.isEmpty(proxyConfig.getServer().getTcp().getKeyStorePassword())) { log.info("no config domain name,nonsupport https proxy."); return; } @@ -50,16 +50,16 @@ public class HttpsProxy implements EventListener { .channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) throws Exception { - if (null != proxyConfig.getServer().getTransferLogEnable() && proxyConfig.getServer().getTransferLogEnable()) { + if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) { ch.pipeline().addFirst(new LoggingHandler(HttpsProxy.class)); } ch.pipeline().addLast(createSslHandler()); ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName())); + ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); } }); - bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpsProxyPort()).sync(); - log.info("Https代理服务启动成功!port:{}", proxyConfig.getServer().getHttpsProxyPort()); + bootstrap.bind("0.0.0.0", proxyConfig.getServer().getTcp().getHttpsProxyPort()).sync(); + log.info("Https代理服务启动成功!port:{}", proxyConfig.getServer().getTcp().getHttpsProxyPort()); } catch (Exception e) { log.error("https proxy start err!", e); } @@ -67,13 +67,13 @@ public class HttpsProxy implements EventListener { private ChannelHandler createSslHandler() { try { - InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getServer().getJksPath()); + InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getServer().getTcp().getJksPath()); SSLContext serverContext = SSLContext.getInstance("TLS"); final KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(jksInputStream, proxyConfig.getServer().getKeyStorePassword().toCharArray()); + ks.load(jksInputStream, proxyConfig.getServer().getTcp().getKeyStorePassword().toCharArray()); final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(ks, proxyConfig.getServer().getKeyStorePassword().toCharArray()); + kmf.init(ks, proxyConfig.getServer().getTcp().getKeyStorePassword().toCharArray()); TrustManager[] trustManagers = null; serverContext.init(kmf.getKeyManagers(), trustManagers, null); diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java index 76a5e881..5febb14e 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java @@ -110,8 +110,8 @@ public class PortMappingService implements LifecycleBean { return; } item.setUserName(user.getName()); - if (StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(item.getSubdomain())) { - item.setDomain(item.getSubdomain() + "." + proxyConfig.getServer().getDomainName()); + if (StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(item.getSubdomain())) { + item.setDomain(item.getSubdomain() + "." + proxyConfig.getServer().getTcp().getDomainName()); } if (NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal())) { item.setProtocal("HTTP(S)"); @@ -151,7 +151,7 @@ public class PortMappingService implements LifecycleBean { // 更新VisitorChannel visitorChannelService.addVisitorChannelByPortMapping(portMappingDO); // 更新域名映射 - if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { + if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); } return new PortMappingCreateRes(); @@ -192,7 +192,7 @@ public class PortMappingService implements LifecycleBean { ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain()); } // 更新域名映射 - if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { + if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); } return new PortMappingUpdateRes(); @@ -279,7 +279,7 @@ public class PortMappingService implements LifecycleBean { portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date()); // 未配置域名,则不需要处理域名映射逻辑 - if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) { + if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName())) { return; } List portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper().eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc()).isNotNull(PortMappingDO::getSubdomain)); diff --git a/neutrino-proxy-server/src/main/resources/app.yml b/neutrino-proxy-server/src/main/resources/app.yml index 99082f24..d268da3b 100644 --- a/neutrino-proxy-server/src/main/resources/app.yml +++ b/neutrino-proxy-server/src/main/resources/app.yml @@ -35,20 +35,27 @@ neutrino: # 是否开启心跳日志 heartbeat-log-enable: ${HEARTBEAT_LOG:false} server: - # 线程池相关配置,用于技术调优,可忽略 - boss-thread-count: 5 - work-thread-count: 20 - # http代理端口,默认80 - http-proxy-port: ${HTTP_PROXY_PORT:80} - # https代理端口,默认443 (需要配置域名、证书) - https-proxy-port: ${HTTPS_PROXY_PORT:443} - # 如果不配置,则不支持域名映射 - domain-name: ${DOMAIN_NAME:} - # https证书配置 - key-store-password: ${HTTPS_STORE_PASS:} - jks-path: ${HTTPS_JKS_PATH:} - # 是否开启代理服务报文日志(日志级别为debug时开启才有效) - transfer-log-enable: ${SERVER_LOG:false} + tcp: + # 线程池相关配置,用于技术调优,可忽略 + boss-thread-count: 5 + work-thread-count: 20 + # http代理端口,默认80 + http-proxy-port: ${HTTP_PROXY_PORT:80} + # https代理端口,默认443 (需要配置域名、证书) + https-proxy-port: ${HTTPS_PROXY_PORT:443} + # 如果不配置,则不支持域名映射 + domain-name: ${DOMAIN_NAME:} + # https证书配置 + key-store-password: ${HTTPS_STORE_PASS:} + jks-path: ${HTTPS_JKS_PATH:} + # 是否开启代理服务报文日志(日志级别为debug时开启才有效) + transfer-log-enable: ${SERVER_LOG:false} + udp: + # 线程池相关配置,用于技术调优,可忽略 + boss-thread-count: 5 + work-thread-count: 20 + # 是否开启代理服务报文日志(日志级别为debug时开启才有效) + transfer-log-enable: ${SERVER_LOG:false} data: db: # 数据库类型,目前支持sqlite、mysql、mariadb diff --git a/todolist.MD b/todolist.MD index 0f245b65..264c8d08 100644 --- a/todolist.MD +++ b/todolist.MD @@ -20,11 +20,12 @@ - [x] 客户端断开连接时,记录日志空指针异常问题修复 -# 1.8.7 +# 1.9.0 - [ ] 增加服务端/客户端jar式一键部署脚本 - [ ] 排查解决问题:https://gitee.com/dromara/neutrino-proxy/issues/I7LGLB - [ ] 访问白名单 - [ ] 端口映射分组 +- [ ] 支持UDP # Bug - 指令通达被close的问题,org.dromara.neutrinoproxy.server.proxy.core.ProxyTunnelChannelHandler.channelInactive