From 3596d147b8a25cd950233887c94a0dd9c3afe9f1 Mon Sep 17 00:00:00 2001 From: xgc Date: Sat, 20 Jan 2024 22:32:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=8B=86=E5=88=86SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/icon.png | Bin 11870 -> 0 bytes neutrino-proxy-client-sdk/Dockerfile | 16 ++ neutrino-proxy-client-sdk/pom.xml | 62 +++++ .../client/sdk/ProxyClientSdk.java | 48 ++++ .../client/sdk/config/IBeanHandler.java | 8 + .../sdk/config/IProxyConfiguration.java | 187 +++++++++++++++ .../NeutrinoClientRuntimeNativeRegistrar.java | 26 ++ .../client/sdk/config/ProxyConfig.java | 74 ++++++ .../client/sdk/constant/Constants.java | 13 + .../client/sdk/core/CmdChannelHandler.java | 87 +++++++ .../client/sdk/core/CustomThreadFactory.java | 55 +++++ .../sdk/core/IAbProxyClientService.java | 127 ++++++++++ .../sdk/core/ProxyChannelBorrowListener.java | 38 +++ .../client/sdk/core/ProxyClientService.java | 31 +++ .../sdk/core/RealServerChannelHandler.java | 103 ++++++++ .../sdk/core/TcpProxyChannelHandler.java | 87 +++++++ .../sdk/core/UdpProxyChannelHandler.java | 84 +++++++ .../client/sdk/core/UdpRealServerHandler.java | 41 ++++ .../sdk/handler/ProxyMessageAuthHandler.java | 46 ++++ .../handler/ProxyMessageConnectHandler.java | 91 +++++++ .../ProxyMessageDisconnectHandler.java | 40 ++++ .../sdk/handler/ProxyMessageErrorHandler.java | 33 +++ .../handler/ProxyMessageTransferHandler.java | 41 ++++ .../UdpProxyMessageConnectHandler.java | 66 +++++ .../UdpProxyMessageTransferHandler.java | 47 ++++ .../client/sdk/solon/BeanHandler.java | 19 ++ .../client/sdk/solon/ProxyConfiguration.java | 119 +++++++++ .../client/sdk/util/LockChannel.java | 28 +++ .../client/sdk/util/ProxyUtil.java | 226 ++++++++++++++++++ .../client/sdk/util/UdpChannelBindInfo.java | 22 ++ .../client/sdk/util/UdpServerUtil.java | 201 ++++++++++++++++ .../src/main/resources/app.yml | 68 ++++++ .../src/main/resources/test.jks | Bin 0 -> 1388 bytes pom.xml | 1 + 34 files changed, 2135 insertions(+) delete mode 100644 .idea/icon.png create mode 100644 neutrino-proxy-client-sdk/Dockerfile create mode 100644 neutrino-proxy-client-sdk/pom.xml create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/ProxyClientSdk.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IBeanHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IProxyConfiguration.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/NeutrinoClientRuntimeNativeRegistrar.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/ProxyConfig.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/constant/Constants.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CmdChannelHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CustomThreadFactory.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/IAbProxyClientService.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyChannelBorrowListener.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyClientService.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/RealServerChannelHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/TcpProxyChannelHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpProxyChannelHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpRealServerHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageAuthHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageConnectHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageDisconnectHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageErrorHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageTransferHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageConnectHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageTransferHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/BeanHandler.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/ProxyConfiguration.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/LockChannel.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/ProxyUtil.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpChannelBindInfo.java create mode 100644 neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpServerUtil.java create mode 100644 neutrino-proxy-client-sdk/src/main/resources/app.yml create mode 100644 neutrino-proxy-client-sdk/src/main/resources/test.jks diff --git a/.idea/icon.png b/.idea/icon.png deleted file mode 100644 index 4b526ba9be57865ba11d9e8ab59a3c006d924d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11870 zcmeHtS5#BcwrCQf25A8Vq=(+YP^E|75s<0~NC_=;q*p}2eH&(z8)LubH0R*Z}COW-;3HTpjqi&!B0tIk_Kw*&}&~M;T*cu4*;06e^ z4!}!ggFrOyc^~v;fEST=rW$A+9S|=thJh}DDL@3k2n_szz;qxyHedwO1k?X>Yyjr| z2Mh!RigpA+{{eFc_{D$JfiHf}-(P|v@V_t?LH-LGj4LAe=hz89EvuFd*fWUGUERzJ z1R|ouf5D)fyz3wk#KF(kgu<=ps$Fao2R`HQc_Y<=!US6u&@9CA>ifj>V4l&z}1WGFOvVlqipA8 zF=e&jsQX2Jk4M$b>NG z?T_z6KZT6O$}s1aOIR3jt;bHxb~i6i)Do}nCWiNi><`!;4>tIT=G=QEdodGE0xqLe zs$Nx4$xTw>B3)l^^RzxVB{O^Ui`3T2NM*x@el}K^tgq~I2lMv4>;*N-sOI%Hb^e#U z*39(5{8H5)%XNRsx!SFq%zT|s@QQpCq_uw(G`v zOdPI!o4djx?do!;L74B$P+iJ*lyWea8d~GmM>{b61(%CtA?-%el3CI;!eSb4N}sCs zSz;%g3j-X7+`4O+`|>eXQS{q?e3XA&P(B~oyt5g+Z!eeT(01-MJlmkU*S#9wsFd3z zjB8=q|KN_hbw5u>H!+Uu(uMR|Ig3`EWbGwbY&D-gr0%!CPE_@5({bhGy7kWmC>c8@ zrk5(=iQOk$bJ}R4B;31U*zEZRNf)QI4UyeY%H-(!s-58&PC`pXSyYF^l_agH1p1LiVNOz)9Ktgi5=+ zU=n_o{@N<44TWb|o353~KwZE|Myi)Pw_TEWuz^$uKMgXMk=`I6VJ`8|R^}WT=muLj z?8T0;fWp5Jiqd*E@&8cF?IGi}#AbeV2`wX8kY|l-3O2a`fuRtJ5b@=+7u6|&(Xe_B z^qBjev{W8Q^b(-|MTmLMN^9pEy+R_j;&~iW1N~nqHo3og2SykbM&Ohy4IcrVfYX3a z%r(207o;k!aoHc<3)kzaD;;%4?pUg|I^&6=A{VY=G;Rsl0?CzB~Gv<86IXoh!aFtW7qCUQ{-Ks6Ie37VXc* z_ISq_4=Fm^dC=6&5v*C+QMm?qem>VkeGV6|T#U#jZ-aw`=j2sj=Nfa%&IJ2TOWq-N z2a13jh=~kaG3px-Tb9f7He?#=Xb`TC+GK5dCig560&E}Q0|}@g5~zG?zp@K#A2gf3 zx)8+0a(Wlwg2Dn7z$Ef;n9^2R09R_}gc!rJZ^Pw?EO;176e^WxGW3XSm{LTmf5n%- z)$pFxa}qq$1Skf*%*IZPRCkKn_1{cB(9EKBix`8S&3p~slb%)mL=8xX09xWt#ip+I zmUq+81pT!xb#7OR#6k%KnL)tyEmUZlM=Y@S$ln|@iuL!-ycH0c_6vO*Z-TcX2y2l7 zll^M6@Y7Oo;;Uaz5NHW=1j}g-4s&R0Wg$1*tr(8tfE8-eDlaz4mmA9ENmt|y457i_bZ-`^VDh(As<9+KJn;0|UVk)e`?mMxSyWKy7$u*jE zL1C()dw)V>+_B-OU56FFVTsdQ+2?o9atp>zzNpi88U*vpKf|orli$1w)r4Iut3E}o z4B*l}VkjVHJdXea*+?V1xA!d{8jacA`MkW^LIPq%5lmFMT-xZU7@b{KeYA;Ec6oD0 ziGh&{Ouy{GEiN0=Q*ozbxvAR-7c%h#8kV77H>n!Z$w%GY)N^=V$D!!M%X*OS#d7<@ zhY#tsZ^<_K{O3V8I8z(q1kL+<%B9+E(tCLmPm@MtRSZPt&cA*@rY#9N9}yi+pQ8IF z5mU#zq7)g~`kAOy6OX55$hsh%I@ac%jX0G*CVQ6q%1nG-!Y}5aYCef54~=TyisS)@ zvzqz?o?uJ!u?;$@j=NdSb`~m;7}`j4w0YEHxxoQITbCCKd&7TNyO5LLOAO}PrQ1|V za-2IDUV0HvOuJ73745Rw?!WJm6uYy4)k=9`M1?Gi05nEMEQctOf+L)WC6GG71M6>` zz>^I|!b|ZAfBV@gwWa_h#!LXyuCT|9TUH#&bNJ0n#X$#YA)LtIe<;pD{@GI2*Zi9i zX8S4%s!WbE%71ap_?gq@*#a(OfD&fhRbos2h9`FGeAFwdK>8odae`i%mrbs^f*ns9Y^kstP`sN0lhhQn%~_e}7z%vJ^Y{X_<&*N^02jYneQ zbjDiWuZ0Jl$2WHGRZ5FE&^jYm28gr6si8lo_1KGVoWMP_KuE9N+jr9Yc<5RyCBJaz zBLGzBXS&1(9SB@1<4Iy-17T)Z0p-tWln;jxlVV?k)u`SUYtvH9JX}6(rZzzB_H9}B zDKx=rDx1gEw*mLBz*O(1+AbNM66n?lfIk_H;^0}so^q+enoN4iL@jI&*M`5fCnpLB zN;pMPXr@3hT_4Lk)ZrUb@-bMq)*J>yr6aLUIt~ z3PL&)KNw2w-&Ti(;=b4)h|~5>kJd3FCx#`DObEt4`;adybp4^qKwGe&``sI7
& zrWGqzY+hXt1O*xft_XOqzwFJ%>_&Fk<9?fseSQ;COSreA5rCeGna~z3@YUXzrfA)! z_4eOS`)-3vCr|jcqF$8euQx90EI;*!eDL$mL}WwU8O+3q7FibAeV2p2^;U?QTbR8> z-l6^WkM)Am(v`N{0dXO8)wBs+w_#^{jaF--nx zl30t86#okJBXY{PyRMPfG3AOsRq)V*VL?6-uInE~ts|5UW>2lQYzY8Iw_Qv_2LvZH zj3A%}oJEB^e=y{pbKPdmJ>}`HBhjInq&E|*KGfH$xT{O# zcfBBBx$wj}CaEbWD02h*>*amxdPFPN2^YTRx$`<@^gwI!Eju##e9Nu-YW+PPh4}mw zv0cU~e-{O}m5*%kU3ODC>fU~z$0f3Vzm7mkPg*rM=ss8FQ=yvIZ{xGyM$LM3iZ?B# z4hdE9#1m@Q>@I3DmXSZxkuwLZiit{6Klsh9+}Cha-<_7N*CSU{25#<28-vV)hAf|0 zWW8#0NVhpN@uy55&s!-)c^_mb`m%N{fGStYvLp{?J%e8^X3$Wm!drI}yxsR!6k zuEbINX;L6pg#TNEnXJvzAofFwkMSKL)x}jjbP)`6PYc0&yi!m)^z9h4><|yJSAm?L z8~K=4D=htP%dw^;%1sU~yhBbbc9BS}9?mjQ7lN4zzQdf}_ao2?zdL5SFd?0{pVh5! ze}(;2{W|M?YNuU$)BRC@nV;rEt1JA{!%*unay7~8E3z69zsiQ-A({$+x7OG7SveV4 zg418UE9Dl$M&O{!#Ah3)Dydy>9 z0CV{oPa*DP|f|J?Xjdz(q z`d}+@x;qB26Q~V;A_86Q=P4$=75m~(=SCxK@dr&lik~aT15VfJW$tw3U8F;RGapo)cx;ubyY?1U>E zH}IjNI$X;=CQ{ZpslH9|NJXaBPHd-mI&x|@LguX-trGHahdC2UP3o)nz(h)5e#A9c zA4gbrfBUBaElBK&?=^B-Z1-ddtj8^jKPTWxu;yI*^_+E)$dInG>knz9BrX@&k4a{| zm6~@9>AXnhMW$0uaEn4TwBo;y&*a}NhlqBc*faUP{qUYvYHGkIWH?hpO2x$HJfB!Y zEiC{V`#nP&l62h{FWz_YI(~frGdtI2wZu91AZ;(?&R9re|`FeMx4)TvV+p)_LR@o%RzefO6xVI^|qs$ zVSVbl0rr0KOOQ<1auYVl#md4Z&-fM7{odo)oh8I3C^2#b57M{TiT{hp}({C#0v^G|K+F8b#b44!s!mAvGDKNKN+%ED;GVQ%{4NULvNc0Lt^{*)H}C3^`%0yrVMFm0L@i``%i>)5#T zgZkf-A!@2?!NZhKB$vo>E2gEhBeGsTA=y%qwj1vnRlCwW{7#}+(VHiTC*tq@9_R>Hxv89s< z&F!)ZLdM>iTO{)N=XzDBklH%2&+ET11vLyz!k+W^j?^$20q8GHi0W#BJGDuCMwJ&)>TC zS?;CZJIw}Zh4A`5>HTb}rXWky|63SkLCix1Bx$?_q$302#AQav-ox{19-BcAOm_q; zb4*INmO$Mf%4IjkrBc<7id*lQ=y5(No2P!fl~eo?;?kvVFn4UKmCI_8T579mjmmLJcwjRIG}=r2y_;=-_X@?+z%)2cc3L>SJz3AblqD^}|mu&Tyt zyK70>yLXedTIwb;+LZ!A8SOUq6QM8eB#l~^VVnT%i6 zdvcu1nfu3x!ezm2`~fsq+^h&7UiKMnaT_)!y}l1gf}_X6$mGS74&m=LK*;*;2wxZY zj^F;9ygrU)3!!?BJ8|2CaZ_Zcpq({od4iHrQ-JpBfLbk|KZioeS#$o#q zz9?xe0EX7Odx`CD!6pa5ku!gj_;0NQSoYBpkt+U33xbFNToP;XxPEa6k5mF7i8^T# zpcjBn6sSI@hf6o`U~pJ8z^^2s>x6$x9qeJ*qtMR#f6G*)fWR0mZ%|wSz5s+%Mws;J zV!sFmU{g06t=KOB|36{>k4kJlJ*N{;fmakqR@y!eeanN(&T9CI-zdS$uV6@}9DOf0 z?k-#Kna_;vYHr8R+Jx?JozwM3(Ijwpb>JiiOD_S^I;>k?{%km{Ys0w%nVSul)w!c2 zl?YiszK*wQJGH#>W4ynk@c>ZS&FbX3RWi;Wzxv}7pJgYu)y6?LD3JF zysufMUKw#U`=Uq9^l^0H$a1>UpHAQ9C4S#ZLVAA3Lcs5!aFKU1j)_#XURF%udyZRWVkI3Z{M}s~=BwQ@9U0M?OUa8%i5dxo;&f%IXE-Szzi z0S6$+Do6+xl4g-sBE%tX+fgD}_nohy~UM zxPato_0wz*bQ#Ha);V!{s02O3pRFWgi!0l;bk!<@(qw~7Wt*1uOGae-n71w!tFGC5iBt%4#mz@6b5zLQ*_ zkb%#aI06WT^&xfsToPB4msJ-YuplHTbFajvHDh3+xS|**E;PO<7zB9P`D7r8N)`}MDncO2 z-YSXk2uia$^bx}RSi`dYAzC>2dtH>VgW98_dvjet1-6+MAc0ns?qPEfn)kloD+adg&-~gn_c`7$yvG&EW9>1x|@G@do0g}<4nd?OOLhS!v;(x@b&BsQeC(AGH z5}@B?L3d_2u3EeSQhVnt`Hy_z3mt(d?+j4mj~7de^y1dkg}3U_L(vr^<3oP87~kTw zMH-EpU;cKdJ^1nqNO1OvvsY-yyMptrXdX--oO$dMtod8#my_lCvd9hqV(eG`l%xt< z#+FiI*q&KFNq@V0^@>oagKH8lpZQjnd{rxX_wOnQ{j0y#F)_wWimpA6?+0BH)U9c` zb2%a+%>F<4^qB*eG(NorBE{P(j}#GzFTKT+oAmGFqxPP}8&lV&(jM#ns2pkyc)-WL z`RT*p%>gk$QN{Ep??yFy^rR*LrerhHwvI_7!{zUu*fHP?@6iwWHGDxDMupixVz4$v zqVKMS4;j3DR({$faJxKDhL+2k=c)hv%U?hGlPa1P#GpBxfOF8vJF9?AZzfjEK0khC ziqz4bi~mLO^L|2%9pxoUaRsd03Xo&1G~ds<1(`VHv9xZ`6`wjj?pmH`%C8tKPxnLToRe!GcT@1w`a{Bnju+pX=*YyAn=ZBJ$}yymq%ej4Xors5klw`p{PE`GMn( zja}Ffd|uc_w1Rjxg=}RjCvTox176j*;%P%MK>A6&4Jg>P$;IJw0>bv>nf|XEBYMHV z>V^#OT!S3}0q@veo6M}y#R9_sW<^5Qz5aES%=4x(J1h3zb;uBn$VfDq`@v9zY8C+3cxQVqfC6+j8Dyb$G)*j_*65MpL*g+MNW@v$nQ zkK`&;29T-@N~d0<8|3lsUAM2T0Gip2i?3JKe5+AHe*oO5BhdaOJntW<+ z+?5CP^EYAFpx&h4&4sDbcq51*B7g)$nydUt!>5DzESC=n_p(35-0LN;YGDcPK0Dv^ zykbBPc?^U^EMYG^rSqM3Exuda%b1a6V-e)e$OcP2H6I!z>%? z#%|z|UaI)@Q>Q*Dp#!XGKqWsNC=Ve3KLSeMLHCy(wKe$N)XoN%M607#pX2MfHZf<8 z{@O8Sy`-?Rz&+lmZ)B=o@y()5&r<6&!<)q*&@l6e)E%uoUe1lv0jz6;(&e-{{nkTV zY67tMfaK7hFf(kO{bV&YheSb?Lb^${O^D6=_Po(Ltq?WN2)LEq;{#EkBs_Vx-T2EV z6KE%G1$x>MnPOijRHc)om{g~`+6;;irp$S8<{0`^32U{F+?EzQoGFW*8hLm62x#?* z-fYX(**|^DE+}rPVnY*U2=t7~DN49`9WB~Flh<$u#ggyC*5sX~z9tTBh-D}P0_3@w z1JvgHXz42{5lu@E=rkHugl(o>&#Y>K?z)CCwYzYRDHKHlL@wd43t(5MIU<{&xA}8T zvF(f9cYV~mKCZk8+;#AFrlCiqP=NmsUpr>}CZZ4vBtO*8oZ}nfl7BB7B@ES#8^)tj z=k(<$mwn40RT5ocLEqak_=QX}sg~k%Dp3zRO&SVdM8>zjrSl9m5#pm>=&m+>#0Yo4 zWtB6hFE5he0E2^ugH0wI;wqUm3Z#I(tzK*x3F*Y+(s{vyi3lf)_QvY-`?D1-`1U3pu;1_Y8uGWN9quKX zg}sI2K>0OR>NzIWLbql|Pte@(urNM|c$1A~j6_Iovz)dnjXU zuR#RoW-BUuowY~H7TJ`bJ2(Z$70q}_zV}tBkR;*5m+{XC*X)5tTF`2fNPxQnaAZ*! zvZn{S!6ifHUO=i-BnhnWtbDkJIp;@4!8!#(v`J9%yi+4ZO)jM&E^_+BUWW|M&2^)H z`+Z=I3M^Fesa|c3Fi|_DM2APJiTkZaQYrgUC z*C0fAg<*Lr&WAv|3^z@@i<-up^QYWg|0`e^RArtQ-%$ZeUPcnN(27@D7p9!R^IEPW z^uojXx5p&_X@SbHywEVwPyWV2;$0C9ak!%@M#usMqRB?D@xI;IV`)s+pAXN6Sh}7G zFb@hr&EeTAzU+~6m-;7Ns)WlDD-ObI=tPwE7q_pxy*rNr5-5JW$yUCzvr;;)qZtD& zF_ZA&(7{3Gk%xdbn63Z399{xjv#*EYE$((#=^q68HbvvP?mn-RpAe7*#$=($fnHK) z31dR`F_*4|%6YOh z))*W=UY>$K7XMwY7X2)T3l*YP*$3Eidp+A>BIA3Pw6S$^Q+j$Yi|xTlwLd%nsvFT^ zB%mL;Gpv!Jp}IOg0vchN6KTz8!*~8vgms1CrN{jf1IeXnPA}@F{rwM=YDGfk8j638 zYXD9DLG$vG7RgBsz0oU)*+%w87Pq}{-)+XkOz1g8AvzUlYENiC`$4#hR*7YOQ|5+Z zpTTjzughOcHVXaaULQiq@I4&}@cZhz*Up#&3v=gc@(W3WCl#m2fjbWBa>wTUUp7z~ z_?nE=JkZ-0+jvZI6YPKI-Fd6L=oxM2Oa#e-C0^>A_H7tRZ)WYsp*;Rn?-EGf-P>|- zlF$Wu9d?+#pw7FUb;kzAVQRCJr3>D$HA$7v0s%?)Gi%?>k>l38fYvZgvg#<=6EqB} zJjiZYa6Ef#`n-S9pkzPF@rENLAc>`EY4|pf%|A?Nh%@58bWcs$-fC}y-ag_DUrjjf zzySvN3h3>wRH9}jIqo=nmTCc)W0N@S521LD%+N|Ith{#ZDVU?qzcsELNM`6Suhn%% z?KJ)92%Dnkuoh4MbI~QRr&wPk{X?n~>2MGFy|-AoKQ22&zE)5^NNr4I)%QVP0O>G8 z<+5+ykCt-p?X7OpX0iOQ72ezen2a6M*svwSL4hK-#@?WF=76;Ew1RJLA?GVFQI8#4 z{u3imvg_6oBlHfUDzJuQ--BJ2>-Wwr4VojMh@K$xz1&rgT+jDwjA26y`<4U^>m$AP z%@x1$krSX#vZ&K2?L-N+|qUrOu%vs1PfcJ*j0Zi#)R z^{2`@vC^Xl5O9X~o+f#2X+3WfLIdn4tb3QtcdPAnHX(3+x>eM*&AloodxcZanpy-1 zkT=4S8DpoW9{vL-pN-Dc#xQ{KmMA}JT-7}!0+8Vrn|6EVm^_69r29L z#Pp=~v2r^C{IQM#GJ@~KP1X2v%ja7bKM1x{wP;WC@8QIs7&BqY8FSV3*2a<3^6XvD zh7*eMm#e~Qfxx<4oTnwt)H< zsOWv^$5+CZCsO$(3m;ER4vW)!o-eYn912K=xdNA_lwszVqw_G`Bjsw=4{o-wywVZT zJnkva`YdCmD$OKexoq^$>q(+YsLypdBV*8vIgcdq%*sAaXPb1L9I$v(7Gs^;hE)jF z0sf2|7DfXRFLjv%E-wWO1@n)*a2iQ4XcWd>X83%wB(|T<^IVw~1Fzw97`cL{${&h> zom0LXt`O|gxNa$kb~1eYj*T530vh7>(bOmSgRiJIUf*(S_oM{}pJN1*X1=P`obOIc zZTW3Dh5yUX(EZv>W5wRIXc>sErH#Aze1Q_iMBvZf{4s zkv>-{tO>7cPg$)VShI`EzHrQ%%@H#de;O=_%uT{<+gj~uM<~6Do0KN}m1UnKOQ(}N zF9dKD43y!43y^yJ<-AU;)}C6XI{7cS(6`S5-6ZUaIaqScEYD^OT?Nn39}o9rr&5s= zjV}+m^e$>Z2wBK0N>$f-VeESQA9|1>*Ip1mUo}$7BZAoN0szl9e0>OD%>QhtLwOWR Xy;8YVgVVrY*9K{*=qgtzT0Q(</etc/timezone +# 设置时区为北京时间 +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN mkdir -p /root/neutrino-proxy/config +WORKDIR /root/neutrino-proxy +COPY ./target/neutrino-proxy-client.jar /root/neutrino-proxy/neutrino-proxy-client.jar +COPY ./src/main/resources/app-copy.yml /root/neutrino-proxy/config/app.yml +#VOLUME ["/root/neutrino-proxy"] +ENTRYPOINT ["java","-jar","neutrino-proxy-client.jar","config=./config/app.yml"] + +#docker run -it -d --restart=always --name np_client -e SERVER_IP=127.0.0.1 diff --git a/neutrino-proxy-client-sdk/pom.xml b/neutrino-proxy-client-sdk/pom.xml new file mode 100644 index 00000000..9db95010 --- /dev/null +++ b/neutrino-proxy-client-sdk/pom.xml @@ -0,0 +1,62 @@ + + + + neutrino-proxy + org.dromara.neutrino-proxy + ${revision} + ../pom.xml + + 4.0.0 + jar + + neutrino-proxy-client-sdk + + + + org.dromara.neutrino-proxy + neutrino-proxy-core + ${revision} + + + org.noear + solon-lib + + + com.github.jsqlparser + jsqlparser + 4.5 + compile + + + + + ${project.artifactId} + + + src/main/resources + true + + *.yml + + + + ${project.basedir}/src/main/resources + false + + + app-dev.yml + + + + + + + org.noear + solon-maven-plugin + + + + + diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/ProxyClientSdk.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/ProxyClientSdk.java new file mode 100644 index 00000000..c0570f13 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/ProxyClientSdk.java @@ -0,0 +1,48 @@ +package org.dromara.neutrinoproxy.client.sdk; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.noear.solon.Solon; +import org.noear.solon.Utils; +import org.noear.solon.annotation.SolonMain; + +/** + * + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +@SolonMain +public class ProxyClientSdk { + + public static void main(String[] args) { + Solon.start(ProxyClientSdk.class, args, app -> { + String loglevel = System.getenv("LOG_LEVEL"); + if (Utils.isNotEmpty(loglevel)) { + app.cfg().put("solon.logging.logger.root.level", loglevel); + } + + setAlias("neutrino.proxy.tunnel.server-ip", "serverIp"); + setAlias("neutrino.proxy.tunnel.server-port", "serverPort"); + setAlias("neutrino.proxy.tunnel.ssl-enable", "sslEnable"); + setAlias("neutrino.proxy.tunnel.jks-path", "jksPath"); + setAlias("neutrino.proxy.tunnel.key-store-password", "keyStorePassword"); + setAlias("neutrino.proxy.tunnel.license-key", "licenseKey"); + + log.info("NeutrinoProxy Client :{}", app.cfg().get("solon.app.version")); + }); + } + + /** + * 别名处理,支持较短的启动参数名 + * @param key + * @param alias + */ + private static void setAlias(String key, String alias) { + String val = Solon.cfg().argx().get(alias); + if (StrUtil.isNotBlank(val)) { + Solon.cfg().put(key, val); + } + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IBeanHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IBeanHandler.java new file mode 100644 index 00000000..266d5b3d --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IBeanHandler.java @@ -0,0 +1,8 @@ +package org.dromara.neutrinoproxy.client.sdk.config; + +import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher; + +public interface IBeanHandler { + Dispatcher getDispatcher(); + ProxyConfig getProxyConfig(); +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IProxyConfiguration.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IProxyConfiguration.java new file mode 100644 index 00000000..abe2a641 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/IProxyConfiguration.java @@ -0,0 +1,187 @@ +package org.dromara.neutrinoproxy.client.sdk.config; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioDatagramChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.timeout.IdleStateHandler; +import org.dromara.neutrinoproxy.client.sdk.core.*; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.ProxyMessageDecoder; +import org.dromara.neutrinoproxy.core.ProxyMessageEncoder; +import org.dromara.neutrinoproxy.core.aot.NeutrinoCoreRuntimeNativeRegistrar; + +import java.net.InetSocketAddress; + +/** + * 代理配置 + * @author: aoshiguchen + * @date: 2022/10/8 + */ + +public abstract class IProxyConfiguration { + + public abstract IBeanHandler getBeanHandler(); + + public NioEventLoopGroup tunnelWorkGroup(ProxyConfig proxyConfig) { + return new NioEventLoopGroup(proxyConfig.getTunnel().getThreadCount()); + } + + public NioEventLoopGroup tcpRealServerWorkGroup(ProxyConfig proxyConfig) { + // 暂时先公用此配置 + return new NioEventLoopGroup(proxyConfig.getTunnel().getThreadCount()); + } + + public NioEventLoopGroup udpServerGroup(ProxyConfig proxyConfig) { + // 暂时先公用此配置 + return new NioEventLoopGroup(proxyConfig.getClient().getUdp().getBossThreadCount()); + } + + public NioEventLoopGroup udpWorkGroup(ProxyConfig proxyConfig) { + // 暂时先公用此配置 + return new NioEventLoopGroup(proxyConfig.getClient().getUdp().getWorkThreadCount()); + } + + public Bootstrap cmdTunnelBootstrap(ProxyConfig proxyConfig, + NioEventLoopGroup tunnelWorkGroup) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(tunnelWorkGroup); + bootstrap.channel(NioSocketChannel.class); +// bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000); +// bootstrap.option(ChannelOption.SO_KEEPALIVE, true); +// /** +// * TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。(一个连接会设置MSS参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。 +// * Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。 +// */ +// bootstrap.option(ChannelOption.TCP_NODELAY, true); + bootstrap.remoteAddress(InetSocketAddress.createUnresolved(proxyConfig.getTunnel().getServerIp(), proxyConfig.getTunnel().getServerPort())); + + bootstrap.handler(new ChannelInitializer() { + + @Override + public void initChannel(SocketChannel ch) throws Exception { + if (proxyConfig.getTunnel().getSslEnable()) { + ch.pipeline().addLast(ProxyUtil.createSslHandler(proxyConfig)); + } + if (null != proxyConfig.getTunnel().getTransferLogEnable() && proxyConfig.getTunnel().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(CmdChannelHandler.class)); + } + ch.pipeline().addLast(new ProxyMessageDecoder(proxyConfig.getProtocol().getMaxFrameLength(), + proxyConfig.getProtocol().getLengthFieldOffset(), proxyConfig.getProtocol().getLengthFieldLength(), + proxyConfig.getProtocol().getLengthAdjustment(), proxyConfig.getProtocol().getInitialBytesToStrip())); + ch.pipeline().addLast(new ProxyMessageEncoder()); + ch.pipeline().addLast(new IdleStateHandler(proxyConfig.getProtocol().getReadIdleTime(), proxyConfig.getProtocol().getWriteIdleTime(), proxyConfig.getProtocol().getAllIdleTimeSeconds())); + ch.pipeline().addLast(new CmdChannelHandler(getBeanHandler())); + } + }); + return bootstrap; + } + + public Bootstrap tcpProxyTunnelBootstrap(ProxyConfig proxyConfig, + NioEventLoopGroup tunnelWorkGroup) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(tunnelWorkGroup); + bootstrap.channel(NioSocketChannel.class); + bootstrap.remoteAddress(InetSocketAddress.createUnresolved(proxyConfig.getTunnel().getServerIp(), proxyConfig.getTunnel().getServerPort())); + bootstrap.handler(new ChannelInitializer() { + + @Override + public void initChannel(SocketChannel ch) throws Exception { + if (proxyConfig.getTunnel().getSslEnable()) { + ch.pipeline().addLast(ProxyUtil.createSslHandler(proxyConfig)); + } + if (null != proxyConfig.getTunnel().getTransferLogEnable() && proxyConfig.getTunnel().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(TcpProxyChannelHandler.class)); + } + ch.pipeline().addLast(new ProxyMessageDecoder(proxyConfig.getProtocol().getMaxFrameLength(), + proxyConfig.getProtocol().getLengthFieldOffset(), proxyConfig.getProtocol().getLengthFieldLength(), + proxyConfig.getProtocol().getLengthAdjustment(), proxyConfig.getProtocol().getInitialBytesToStrip())); + ch.pipeline().addLast(new ProxyMessageEncoder()); + ch.pipeline().addLast(new IdleStateHandler(proxyConfig.getProtocol().getReadIdleTime(), proxyConfig.getProtocol().getWriteIdleTime(), proxyConfig.getProtocol().getAllIdleTimeSeconds())); + ch.pipeline().addLast(new TcpProxyChannelHandler(getBeanHandler())); + } + }); + return bootstrap; + } + public Bootstrap udpProxyTunnelBootstrap(ProxyConfig proxyConfig, + NioEventLoopGroup tunnelWorkGroup) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(tunnelWorkGroup); + bootstrap.channel(NioSocketChannel.class); + bootstrap.remoteAddress(InetSocketAddress.createUnresolved(proxyConfig.getTunnel().getServerIp(), proxyConfig.getTunnel().getServerPort())); + bootstrap.handler(new ChannelInitializer() { + + @Override + public void initChannel(SocketChannel ch) throws Exception { + if (proxyConfig.getTunnel().getSslEnable()) { + ch.pipeline().addLast(ProxyUtil.createSslHandler(proxyConfig)); + } + if (null != proxyConfig.getTunnel().getTransferLogEnable() && proxyConfig.getTunnel().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(TcpProxyChannelHandler.class)); + } + ch.pipeline().addLast(new ProxyMessageDecoder(proxyConfig.getProtocol().getMaxFrameLength(), + proxyConfig.getProtocol().getLengthFieldOffset(), proxyConfig.getProtocol().getLengthFieldLength(), + proxyConfig.getProtocol().getLengthAdjustment(), proxyConfig.getProtocol().getInitialBytesToStrip())); + ch.pipeline().addLast(new ProxyMessageEncoder()); + ch.pipeline().addLast(new IdleStateHandler(proxyConfig.getProtocol().getReadIdleTime(), proxyConfig.getProtocol().getWriteIdleTime(), proxyConfig.getProtocol().getAllIdleTimeSeconds())); + ch.pipeline().addLast(new UdpProxyChannelHandler(getBeanHandler())); + } + }); + return bootstrap; + } + + public Bootstrap realServerBootstrap(ProxyConfig proxyConfig, + NioEventLoopGroup tcpRealServerWorkGroup + ) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(tcpRealServerWorkGroup); + bootstrap.channel(NioSocketChannel.class); + bootstrap.handler(new ChannelInitializer() { + + @Override + public void initChannel(SocketChannel ch) throws Exception { + if (null != proxyConfig.getTunnel().getTransferLogEnable() && proxyConfig.getTunnel().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(RealServerChannelHandler.class)); + } + ch.pipeline().addLast(new RealServerChannelHandler()); + } + }); + return bootstrap; + } + + public Bootstrap udpServerBootstrap(ProxyConfig proxyConfig, + NioEventLoopGroup udpServerGroup, + NioEventLoopGroup udpWorkGroup) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(udpServerGroup) + // 主线程处理 + .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.getClient().getUdp().getTransferLogEnable() && proxyConfig.getClient().getUdp().getTransferLogEnable()) { + ch.pipeline().addFirst(new LoggingHandler(UdpRealServerHandler.class)); + } + pipeline.addLast(udpWorkGroup, new UdpRealServerHandler()); + } + }); + return bootstrap; + } + + public NeutrinoCoreRuntimeNativeRegistrar neutrinoCoreRuntimeNativeRegistrar() { + return new NeutrinoCoreRuntimeNativeRegistrar(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/NeutrinoClientRuntimeNativeRegistrar.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/NeutrinoClientRuntimeNativeRegistrar.java new file mode 100644 index 00000000..5b060964 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/NeutrinoClientRuntimeNativeRegistrar.java @@ -0,0 +1,26 @@ +package org.dromara.neutrinoproxy.client.sdk.config; + +import org.noear.solon.annotation.Component; +import org.noear.solon.aot.RuntimeNativeMetadata; +import org.noear.solon.aot.RuntimeNativeRegistrar; +import org.noear.solon.aot.hint.MemberCategory; +import org.noear.solon.core.AppContext; + +/** + * @author songyinyin + * @since 2023/10/21 22:50 + */ +@Component +public class NeutrinoClientRuntimeNativeRegistrar implements RuntimeNativeRegistrar { + @Override + public void register(AppContext context, RuntimeNativeMetadata metadata) { + metadata.registerResourceInclude("test.jks"); + + metadata.registerReflection(ProxyConfig.Protocol.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS); + metadata.registerReflection(ProxyConfig.Client.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS); + metadata.registerReflection(ProxyConfig.Tunnel.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS); + metadata.registerReflection(ProxyConfig.Tcp.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS); + metadata.registerReflection(ProxyConfig.Udp.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS); + metadata.registerReflection(ProxyConfig.Reconnection.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS); + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/ProxyConfig.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/ProxyConfig.java new file mode 100644 index 00000000..ba740bef --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/config/ProxyConfig.java @@ -0,0 +1,74 @@ +package org.dromara.neutrinoproxy.client.sdk.config; + +import lombok.Data; +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Inject; + +/** + * + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Data +@Component +public class ProxyConfig { + @Inject("${neutrino.proxy.protocol}") + private Protocol protocol; + @Inject("${neutrino.proxy.tunnel}") + private Tunnel tunnel; + @Inject("${neutrino.proxy.client}") + private Client client; + + @Data + public static class Protocol { + private Integer maxFrameLength; + private Integer lengthFieldOffset; + private Integer lengthFieldLength; + private Integer initialBytesToStrip; + private Integer lengthAdjustment; + private Integer readIdleTime; + private Integer writeIdleTime; + private Integer allIdleTimeSeconds; + } + + @Data + public static class Tunnel { + private String keyStorePassword; + private String jksPath; + private String serverIp; + private Integer serverPort; + private Boolean sslEnable; + private Integer obtainLicenseInterval; + private String licenseKey; + private Integer threadCount; + private String clientId; + private Boolean transferLogEnable; + private Boolean heartbeatLogEnable; + private Reconnection reconnection; + } + + @Data + public static class Client { +// private Tcp tcp; + private Udp udp; + } + + @Data + public static class Reconnection { + private Integer intervalSeconds; + private Boolean unlimited; + } + + @Data + public static class Tcp { + + } + + @Data + public static class Udp { + private Integer bossThreadCount; + private Integer workThreadCount; + private String puppetPortRange; + private Boolean transferLogEnable; + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/constant/Constants.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/constant/Constants.java new file mode 100644 index 00000000..02dd5fe8 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/constant/Constants.java @@ -0,0 +1,13 @@ +package org.dromara.neutrinoproxy.client.sdk.constant; + +import io.netty.util.AttributeKey; +import org.dromara.neutrinoproxy.client.sdk.util.UdpChannelBindInfo; + +/** + * @author: aoshiguchen + * @date: 2023/9/21 + */ +public interface Constants { + AttributeKey UDP_CHANNEL_BIND_KEY = AttributeKey.newInstance("udpChannelBindKey"); + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CmdChannelHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CmdChannelHandler.java new file mode 100644 index 00000000..f4bb1c67 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CmdChannelHandler.java @@ -0,0 +1,87 @@ +package org.dromara.neutrinoproxy.client.sdk.core; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOption; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.timeout.IdleStateEvent; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.IBeanHandler; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyMessage; + +/** + * 处理与服务端之间的数据传输 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +public class CmdChannelHandler extends SimpleChannelInboundHandler{ + private static volatile Boolean transferLogEnable = Boolean.FALSE; + private IBeanHandler beanHandler; + + public CmdChannelHandler(IBeanHandler beanHandler) { + this.beanHandler = beanHandler; + ProxyConfig proxyConfig = beanHandler.getProxyConfig(); + if (null != proxyConfig.getClient() && null != proxyConfig.getTunnel().getHeartbeatLogEnable()) { + transferLogEnable = proxyConfig.getTunnel().getHeartbeatLogEnable(); + } + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception { + if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType() || transferLogEnable) { + log.debug("[CMD Channel]Client CmdChannel recieved proxy message, type is {}", proxyMessage.getType()); + } + beanHandler.getDispatcher().dispatch(ctx, 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 { + log.info("[CMD Channel]Client CmdChannel disconnect"); + ProxyUtil.setCmdChannel(null); + ProxyUtil.clearRealServerChannels(); + + super.channelInactive(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("[CMD Channel]Client CmdChannel Error channelId:{}", ctx.channel().id().asLongText(), cause); + ctx.close(); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if(evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent)evt; + switch (event.state()) { + case READER_IDLE: + // 读超时,断开连接 + log.error("[CMD Channel] Read timeout disconnect"); + ctx.channel().close(); + break; + case WRITER_IDLE: + ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage()); + break; + case ALL_IDLE: + log.error("[CMD Channel] ReadWrite timeout disconnect"); + ctx.close(); + break; + } + } + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CustomThreadFactory.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CustomThreadFactory.java new file mode 100644 index 00000000..1bddce35 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/CustomThreadFactory.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.dromara.neutrinoproxy.client.sdk.core; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * @author: aoshiguchen + * @date: 2022/9/4 + */ +public class CustomThreadFactory implements ThreadFactory { + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public CustomThreadFactory(String prefix) { + SecurityManager s = System.getSecurityManager(); + group = (s != null) ? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + namePrefix = prefix + "-thread-"; + } + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); + if (t.isDaemon()) { + t.setDaemon(false); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/IAbProxyClientService.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/IAbProxyClientService.java new file mode 100644 index 00000000..0af1ef55 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/IAbProxyClientService.java @@ -0,0 +1,127 @@ +package org.dromara.neutrinoproxy.client.sdk.core; + +import cn.hutool.core.util.StrUtil; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.client.sdk.util.UdpServerUtil; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.noear.solon.Solon; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 代理客户端服务 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +public class IAbProxyClientService { + + public ProxyConfig proxyConfig; + + public Bootstrap cmdTunnelBootstrap; + + public Bootstrap udpServerBootstrap; + + private volatile Channel channel; + /** + * 重连次数 + */ + private volatile int reconnectCount = 0; + + /** + * 重连服务执行器 + */ + public static final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor(new CustomThreadFactory("ClientReconnect")); + + public void init_i(){ + reconnectExecutor.scheduleWithFixedDelay(this::reconnect, 10, proxyConfig.getTunnel().getReconnection().getIntervalSeconds(), TimeUnit.SECONDS); + try { + this.start(); + UdpServerUtil.initCache(proxyConfig, udpServerBootstrap); + } catch (Exception e) { + // 启动连不上也做一下重连,因此先catch异常 + log.error("[CmdChannel] start error", e); + } + } + + public void start() { + if (StrUtil.isEmpty(proxyConfig.getTunnel().getServerIp())) { + log.error("not found server-ip config."); + Solon.stop(); + return; + } + if (null == proxyConfig.getTunnel().getServerPort()) { + log.error("not found server-port config."); + Solon.stop(); + return; + } + if (null != proxyConfig.getTunnel().getSslEnable() && proxyConfig.getTunnel().getSslEnable() + && StrUtil.isEmpty(proxyConfig.getTunnel().getJksPath())) { + log.error("not found jks-path config."); + Solon.stop(); + return; + } + if (StrUtil.isEmpty(proxyConfig.getTunnel().getLicenseKey())) { + log.error("not found license-key config."); + Solon.stop(); + return; + } + if (null == channel || !channel.isActive()) { + try { + connectProxyServer(); + } catch (Exception e) { + log.error("client start error", e); + } + } else { + channel.writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getTunnel().getLicenseKey(), ProxyUtil.getClientId())); + } + } + + /** + * 连接代理服务器 + */ + private void connectProxyServer() throws InterruptedException { + cmdTunnelBootstrap.connect() + .addListener(new ChannelFutureListener() { + + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + channel = future.channel(); + // 连接成功,向服务器发送客户端认证信息(licenseKey) + ProxyUtil.setCmdChannel(future.channel()); + future.channel().writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getTunnel().getLicenseKey(), ProxyUtil.getClientId())); + log.info("[CmdChannel] connect proxy server success. channelId:{}", future.channel().id().asLongText()); + +// reconnectServiceEnable = true; + reconnectCount = 0; + } else { + log.info("[CmdChannel] connect proxy server failed!"); + } + } + }).sync(); + } + + protected synchronized void reconnect() { + if (null != channel) { + if (channel.isActive()) { + return; + } + channel.close(); + } + + log.info("[CmdChannel] client reconnect seq:{}", ++reconnectCount); + try { + connectProxyServer(); + } catch (Exception e) { + log.error("[CmdChannel] reconnect error", e); + } + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyChannelBorrowListener.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyChannelBorrowListener.java new file mode 100644 index 00000000..1dce20d2 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyChannelBorrowListener.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.dromara.neutrinoproxy.client.sdk.core; + +import io.netty.channel.Channel; + +/** + * + * @author: aoshiguchen + * @date: 2022/6/16 + */ +public interface ProxyChannelBorrowListener { + + void success(Channel channel); + + void error(Throwable cause); + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyClientService.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyClientService.java new file mode 100644 index 00000000..8922a8bc --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/ProxyClientService.java @@ -0,0 +1,31 @@ +package org.dromara.neutrinoproxy.client.sdk.core; + +import io.netty.bootstrap.Bootstrap; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Init; +import org.noear.solon.annotation.Inject; + +/** + * 代理客户端服务 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +@Component +public class ProxyClientService extends IAbProxyClientService{ + @Inject + private ProxyConfig proxyConfig; + @Inject("cmdTunnelBootstrap") + private Bootstrap cmdTunnelBootstrap; + @Inject("udpServerBootstrap") + private Bootstrap udpServerBootstrap; + @Init + public void init() { + super.proxyConfig=proxyConfig; + super.cmdTunnelBootstrap=cmdTunnelBootstrap; + super.udpServerBootstrap=udpServerBootstrap; + init_i(); + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/RealServerChannelHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/RealServerChannelHandler.java new file mode 100644 index 00000000..ea0cb3d5 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/RealServerChannelHandler.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.dromara.neutrinoproxy.client.sdk.core; + +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyMessage; +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; + +/** + * 处理与被代理客户端的数据传输 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +public class RealServerChannelHandler extends SimpleChannelInboundHandler { + + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { + Channel realServerChannel = ctx.channel(); + Channel proxyChannel = realServerChannel.attr(Constants.NEXT_CHANNEL).get(); + if (null == proxyChannel) { + // 代理客户端连接断开 + ctx.channel().close(); + } else { + + if (proxyChannel.isWritable()) { + if (!realServerChannel.config().isAutoRead()) { + realServerChannel.config().setAutoRead(true); + } + } else { + if (realServerChannel.config().isAutoRead()) { + realServerChannel.config().setAutoRead(false); + } + } + + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + String visitorId = ProxyUtil.getVisitorIdByRealServerChannel(realServerChannel); + proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(visitorId, bytes)); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + Channel realServerChannel = ctx.channel(); + String visitorId = ProxyUtil.getVisitorIdByRealServerChannel(realServerChannel); + ProxyUtil.removeRealServerChannel(visitorId); + Channel channel = realServerChannel.attr(Constants.NEXT_CHANNEL).get(); + if (channel != null) { + channel.writeAndFlush(ProxyMessage.buildDisconnectMessage(visitorId)); + } + + 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 { + log.error("Client ProxyChannel Error", cause); + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/TcpProxyChannelHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/TcpProxyChannelHandler.java new file mode 100644 index 00000000..59161321 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/TcpProxyChannelHandler.java @@ -0,0 +1,87 @@ +package org.dromara.neutrinoproxy.client.sdk.core; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOption; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.timeout.IdleStateEvent; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.IBeanHandler; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher; +import org.noear.solon.Solon; + +/** + * 处理与服务端之间的数据传输 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +public class TcpProxyChannelHandler extends SimpleChannelInboundHandler { + + private IBeanHandler beanHandler; + public TcpProxyChannelHandler(IBeanHandler beanHandler) { + this.beanHandler = beanHandler; + } + @Override + protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception { + if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) { + log.debug("[TCP Proxy Channel]Client ProxyChannel recieved proxy message, type is {}", proxyMessage.getType()); + } + beanHandler.getDispatcher().dispatch(ctx, 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 { + // 数据传输连接 + Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get(); + if (realServerChannel != null && realServerChannel.isActive()) { + realServerChannel.close(); + } + + ProxyUtil.removeTcpProxyChanel(ctx.channel()); + super.channelInactive(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("[TCP Proxy Channel]Client ProxyChannel Error channelId:{}", ctx.channel().id().asLongText(), cause); + ctx.close(); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if(evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent)evt; + switch (event.state()) { + case READER_IDLE: + if (ctx.channel().isWritable()) { + // 读超时,断开连接 + log.info("[TCP Proxy Channel]Read timeout"); + ctx.channel().close(); + } + break; + case WRITER_IDLE: + ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage()); + break; + case ALL_IDLE: +// log.debug("[TCP Proxy Channel]ReadWrite timeout"); +// ctx.close(); + break; + } + } + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpProxyChannelHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpProxyChannelHandler.java new file mode 100644 index 00000000..40dec1b6 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpProxyChannelHandler.java @@ -0,0 +1,84 @@ +package org.dromara.neutrinoproxy.client.sdk.core; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOption; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.timeout.IdleStateEvent; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.IBeanHandler; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher; +import org.noear.solon.Solon; + +/** + * 处理与服务端之间的数据传输 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +public class UdpProxyChannelHandler extends SimpleChannelInboundHandler { + + private IBeanHandler beanHandler; + public UdpProxyChannelHandler(IBeanHandler beanHandler) { + this.beanHandler = beanHandler; + } + @Override + protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception { + if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) { + log.debug("[UDP Proxy Channel]Client ProxyChannel recieved proxy message, type is {}", proxyMessage.getType()); + } + beanHandler.getDispatcher().dispatch(ctx, 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 { + // 数据传输连接 + Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get(); + if (realServerChannel != null && realServerChannel.isActive()) { + realServerChannel.close(); + } + + ProxyUtil.removeTcpProxyChanel(ctx.channel()); + super.channelInactive(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("[UDP Proxy Channel]Client ProxyChannel Error channelId:{}", ctx.channel().id().asLongText(), cause); + ctx.close(); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if(evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent)evt; + switch (event.state()) { + case READER_IDLE: + // 读超时,断开连接 + log.info("[UDP Proxy Channel]Read timeout"); + ctx.channel().close(); + break; + case WRITER_IDLE: + ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage()); + break; + case ALL_IDLE: + log.debug("[UDP Proxy Channel]ReadWrite timeout"); + ctx.close(); + break; + } + } + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpRealServerHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpRealServerHandler.java new file mode 100644 index 00000000..758a5618 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/core/UdpRealServerHandler.java @@ -0,0 +1,41 @@ +package org.dromara.neutrinoproxy.client.sdk.core; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.socket.DatagramPacket; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.constant.Constants; +import org.dromara.neutrinoproxy.client.sdk.util.UdpChannelBindInfo; +import org.dromara.neutrinoproxy.core.ProxyMessage; + +import java.net.InetSocketAddress; + +/** + * @author: aoshiguchen + * @date: 2023/9/21 + */ +@Slf4j +public class UdpRealServerHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception { + log.debug("chid---<:{} port:{}", ctx.channel().id().asLongText(), ((InetSocketAddress)ctx.channel().localAddress()).getPort()); + UdpChannelBindInfo udpChannelBindInfo = ctx.channel().attr(Constants.UDP_CHANNEL_BIND_KEY).get(); + if (null != udpChannelBindInfo) { + byte[] bytes = new byte[datagramPacket.content().readableBytes()]; + datagramPacket.content().readBytes(bytes); + + udpChannelBindInfo.getTunnelChannel().writeAndFlush(ProxyMessage.buildUdpTransferMessage(new ProxyMessage.UdpBaseInfo() + .setVisitorId(udpChannelBindInfo.getVisitorId()) + .setVisitorIp(udpChannelBindInfo.getVisitorIp()) + .setVisitorPort(udpChannelBindInfo.getVisitorPort()) + .setServerPort(udpChannelBindInfo.getServerPort()) + .setTargetIp(udpChannelBindInfo.getTargetIp()) + .setTargetPort(udpChannelBindInfo.getTargetPort())) + .setData(bytes) + ); + + udpChannelBindInfo.getLockChannel().setResponseCount(udpChannelBindInfo.getLockChannel().getResponseCount() + 1); + } + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageAuthHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageAuthHandler.java new file mode 100644 index 00000000..5ae9219e --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageAuthHandler.java @@ -0,0 +1,46 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ExceptionEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.dispatcher.Match; +import org.noear.snack.ONode; +import org.noear.solon.Solon; + +/** + * 认证信息处理器 + * @author: aoshiguchen + * @date: 2022/9/4 + */ +@Slf4j +@Match(type = Constants.ProxyDataTypeName.AUTH) +public class ProxyMessageAuthHandler implements ProxyMessageHandler { + private ProxyConfig proxyConfig; + + public ProxyMessageAuthHandler(ProxyConfig proxyConfig){ + this.proxyConfig=proxyConfig; + } + @Override + public void handle(ChannelHandlerContext context, ProxyMessage proxyMessage) { + String info = proxyMessage.getInfo(); + ONode load = ONode.load(info); + Integer code = load.get("code").getInt(); + log.info("Auth result:{}", info); + if (ExceptionEnum.AUTH_FAILED.getCode().equals(code)) { + // 客户端认证失败,直接停止服务 + log.info("client auth failed , client stop."); + context.channel().close(); + if (!proxyConfig.getTunnel().getReconnection().getUnlimited()) { + Solon.stop(); + } + } else if (ExceptionEnum.CONNECT_FAILED.getCode().equals(code) || + ExceptionEnum.LICENSE_CANNOT_REPEAT_CONNECT.getCode().equals(code) + ){ + context.channel().close(); + } + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageConnectHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageConnectHandler.java new file mode 100644 index 00000000..804c2cc2 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageConnectHandler.java @@ -0,0 +1,91 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.core.ProxyChannelBorrowListener; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.dispatcher.Match; + +/** + * 连接信息处理器 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +@Match(type = Constants.ProxyDataTypeName.CONNECT) +public class ProxyMessageConnectHandler implements ProxyMessageHandler { + + private Bootstrap tcpProxyTunnelBootstrap; + private Bootstrap realServerBootstrap; + private ProxyConfig proxyConfig; + + public ProxyMessageConnectHandler(Bootstrap tcpProxyTunnelBootstrap,Bootstrap realServerBootstrap,ProxyConfig proxyConfig){ + this.proxyConfig=proxyConfig; + this.realServerBootstrap=realServerBootstrap; + this.tcpProxyTunnelBootstrap=tcpProxyTunnelBootstrap; + } + + @Override + public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { + final Channel cmdChannel = ctx.channel(); + final String visitorId = proxyMessage.getInfo(); + String[] serverInfo = new String(proxyMessage.getData()).split(":"); + String ip = serverInfo[0]; + int port = Integer.parseInt(serverInfo[1]); + // 连接真实的、被代理的服务 + realServerBootstrap.connect(ip, port).addListener(new ChannelFutureListener() { + + @Override + public void operationComplete(ChannelFuture future) throws Exception { + + // 连接后端服务器成功 + if (future.isSuccess()) { + final Channel realServerChannel = future.channel(); + + realServerChannel.config().setOption(ChannelOption.AUTO_READ, false); + + // 获取连接 + ProxyUtil.borrowTcpProxyChanel(tcpProxyTunnelBootstrap, new ProxyChannelBorrowListener() { + + @Override + public void success(Channel channel) { + // 连接绑定 + channel.attr(Constants.NEXT_CHANNEL).set(realServerChannel); + realServerChannel.attr(Constants.NEXT_CHANNEL).set(channel); + + // 远程绑定 + channel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId + "@" + proxyConfig.getTunnel().getLicenseKey())); + + realServerChannel.config().setOption(ChannelOption.AUTO_READ, true); + ProxyUtil.addRealServerChannel(visitorId, realServerChannel); + ProxyUtil.setRealServerChannelVisitorId(realServerChannel, visitorId); + } + + @Override + public void error(Throwable cause) { + ProxyMessage proxyMessage = new ProxyMessage(); + proxyMessage.setType(ProxyMessage.TYPE_DISCONNECT); + proxyMessage.setInfo(visitorId); + cmdChannel.writeAndFlush(proxyMessage); + } + }); + + } else { + cmdChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(visitorId)); + } + } + }); + } + + @Override + public String name() { + return ProxyDataTypeEnum.CONNECT.getDesc(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageDisconnectHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageDisconnectHandler.java new file mode 100644 index 00000000..1534aae1 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageDisconnectHandler.java @@ -0,0 +1,40 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.dispatcher.Match; + +/** + * 断开连接信息处理器 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +@Match(type = Constants.ProxyDataTypeName.DISCONNECT) +public class ProxyMessageDisconnectHandler implements ProxyMessageHandler { + + @Override + public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { + Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get(); + if (null != realServerChannel) { + ctx.channel().attr(Constants.NEXT_CHANNEL).remove(); + ProxyUtil.returnTcpProxyChanel(ctx.channel()); + realServerChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); + } + ctx.close(); + } + + @Override + public String name() { + return ProxyDataTypeEnum.DISCONNECT.getDesc(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageErrorHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageErrorHandler.java new file mode 100644 index 00000000..66ce7fea --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageErrorHandler.java @@ -0,0 +1,33 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.core.*; +import org.dromara.neutrinoproxy.core.dispatcher.Match; +import org.noear.snack.ONode; + +/** + * 异常信息处理器 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Slf4j +@Match(type = Constants.ProxyDataTypeName.ERROR) +public class ProxyMessageErrorHandler implements ProxyMessageHandler { + + @Override + public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { + log.info("error: {}", proxyMessage.getInfo()); + ONode load = ONode.load(proxyMessage.getInfo()); + Integer code = load.get("code").getInt(); + if (ExceptionEnum.AUTH_FAILED.getCode().equals(code)) { + System.exit(0); + } + } + + @Override + public String name() { + return ProxyDataTypeEnum.DISCONNECT.getDesc(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageTransferHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageTransferHandler.java new file mode 100644 index 00000000..1d2199aa --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/ProxyMessageTransferHandler.java @@ -0,0 +1,41 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.dispatcher.Match; + +/** + * 传输信息处理器 + * @author: aoshiguchen + * @date: 2022/6/16 + */ +@Match(type = Constants.ProxyDataTypeName.TRANSFER) +@Slf4j +public class ProxyMessageTransferHandler implements ProxyMessageHandler { + + @Override + public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { + Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get(); + if (realServerChannel != null) { + + // 自己可写,则设置来源可读。自己不可写,则设置来源不可读 + ctx.channel().config().setAutoRead(realServerChannel.isWritable()); + + ByteBuf buf = ctx.alloc().buffer(proxyMessage.getData().length); + buf.writeBytes(proxyMessage.getData()); + realServerChannel.writeAndFlush(buf); + } + } + + @Override + public String name() { + return ProxyDataTypeEnum.TRANSFER.getDesc(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageConnectHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageConnectHandler.java new file mode 100644 index 00000000..81dc9651 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageConnectHandler.java @@ -0,0 +1,66 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.core.ProxyChannelBorrowListener; +import org.dromara.neutrinoproxy.client.sdk.util.ProxyUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.dispatcher.Match; +import org.noear.snack.ONode; + +/** + * @author: aoshiguchen + * @date: 2023/9/19 + */ +@Slf4j +@Match(type = Constants.ProxyDataTypeName.UDP_CONNECT) +public class UdpProxyMessageConnectHandler implements ProxyMessageHandler { + + private ProxyConfig proxyConfig; + private Bootstrap udpProxyTunnelBootstrap; + + public UdpProxyMessageConnectHandler(ProxyConfig proxyConfig,Bootstrap udpProxyTunnelBootstrap){ + this.proxyConfig=proxyConfig; + this.udpProxyTunnelBootstrap=udpProxyTunnelBootstrap; + } + + @Override + public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { + final Channel cmdChannel = ctx.channel(); + final ProxyMessage.UdpBaseInfo udpBaseInfo = ONode.deserialize(proxyMessage.getInfo(), ProxyMessage.UdpBaseInfo.class); + log.info("[UDP connect]info:{}", proxyMessage.getInfo()); + + // 获取连接 + ProxyUtil.borrowUdpProxyChanel(udpProxyTunnelBootstrap, new ProxyChannelBorrowListener() { + + @Override + public void success(Channel channel) { + channel.writeAndFlush(ProxyMessage.buildUdpConnectMessage(new ProxyMessage.UdpBaseInfo() + .setVisitorId(udpBaseInfo.getVisitorId()) + .setServerPort(udpBaseInfo.getServerPort()) + .setTargetIp(udpBaseInfo.getTargetIp()) + .setTargetPort(udpBaseInfo.getTargetPort()) + ).setData(proxyConfig.getTunnel().getLicenseKey().getBytes())); + } + + @Override + public void error(Throwable cause) { + cmdChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(udpBaseInfo.toJsonString())); + } + }); + + + } + + @Override + public String name() { + return ProxyDataTypeEnum.UDP_CONNECT.getDesc(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageTransferHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageTransferHandler.java new file mode 100644 index 00000000..2e97adfa --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/handler/UdpProxyMessageTransferHandler.java @@ -0,0 +1,47 @@ +package org.dromara.neutrinoproxy.client.sdk.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.DatagramPacket; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.client.sdk.util.UdpServerUtil; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.dispatcher.Match; +import org.noear.snack.ONode; + +import java.net.InetSocketAddress; + +/** + * @author: aoshiguchen + * @date: 2023/9/20 + */ +@Slf4j +@Match(type = Constants.ProxyDataTypeName.UDP_TRANSFER) +public class UdpProxyMessageTransferHandler implements ProxyMessageHandler { + + @Override + public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { + final ProxyMessage.UdpBaseInfo udpBaseInfo = ONode.deserialize(proxyMessage.getInfo(), ProxyMessage.UdpBaseInfo.class); + log.debug("[UDP transfer]info:{} data:{}", proxyMessage.getInfo(), new String(proxyMessage.getData())); + Channel channel = UdpServerUtil.takeChannel(udpBaseInfo, ctx.channel()); + if (null == channel) { + log.error("[UDP transfer] take udp channel failed."); + return; + } + log.debug("chid--->:{} port:{}", ctx.channel().id().asLongText(), ((InetSocketAddress)channel.localAddress()).getPort()); + InetSocketAddress address = new InetSocketAddress(udpBaseInfo.getTargetIp(), udpBaseInfo.getTargetPort()); + ByteBuf byteBuf = Unpooled.copiedBuffer(proxyMessage.getData()); + channel.writeAndFlush(new DatagramPacket(byteBuf, address)); + } + + @Override + public String name() { + return ProxyDataTypeEnum.UDP_TRANSFER.getDesc(); + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/BeanHandler.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/BeanHandler.java new file mode 100644 index 00000000..ed8f3fb6 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/BeanHandler.java @@ -0,0 +1,19 @@ +package org.dromara.neutrinoproxy.client.sdk.solon; + +import org.dromara.neutrinoproxy.client.sdk.config.IBeanHandler; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher; +import org.noear.solon.Solon; + + +public class BeanHandler implements IBeanHandler { + @Override + public Dispatcher getDispatcher(){ + return Solon.context().getBean(Dispatcher.class); + } + + @Override + public ProxyConfig getProxyConfig() { + return Solon.context().getBean(ProxyConfig.class); + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/ProxyConfiguration.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/ProxyConfiguration.java new file mode 100644 index 00000000..eaeddcf1 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/solon/ProxyConfiguration.java @@ -0,0 +1,119 @@ +package org.dromara.neutrinoproxy.client.sdk.solon; + +import com.google.common.collect.Lists; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.nio.NioEventLoopGroup; +import org.dromara.neutrinoproxy.client.sdk.config.IBeanHandler; +import org.dromara.neutrinoproxy.client.sdk.config.IProxyConfiguration; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.handler.*; +import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.dromara.neutrinoproxy.core.ProxyMessageHandler; +import org.dromara.neutrinoproxy.core.aot.NeutrinoCoreRuntimeNativeRegistrar; +import org.dromara.neutrinoproxy.core.dispatcher.DefaultDispatcher; +import org.dromara.neutrinoproxy.core.dispatcher.Dispatcher; +import org.noear.solon.Solon; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.bean.LifecycleBean; + +import java.util.List; + +/** + * 代理配置 + * @author: aoshiguchen + * @date: 2022/10/8 + */ +@Configuration +public class ProxyConfiguration extends IProxyConfiguration implements LifecycleBean { + @Inject + private ProxyConfig proxyConfig; + @Inject("tcpProxyTunnelBootstrap") + private Bootstrap tcpProxyTunnelBootstrap; + @Inject("realServerBootstrap") + private Bootstrap realServerBootstrap; + + @Override + public void start() throws Throwable { + List list = Lists.newArrayList( + new ProxyMessageAuthHandler(proxyConfig), + new ProxyMessageConnectHandler(tcpProxyTunnelBootstrap,realServerBootstrap,proxyConfig), + new ProxyMessageDisconnectHandler(), + new ProxyMessageErrorHandler(), + new ProxyMessageTransferHandler(), + new UdpProxyMessageConnectHandler(proxyConfig,tcpProxyTunnelBootstrap), + new UdpProxyMessageTransferHandler() + ); + Dispatcher dispatcher = new DefaultDispatcher<>("MessageDispatcher", list, + proxyMessage -> ProxyDataTypeEnum.of((int)proxyMessage.getType()) == null ? + null : ProxyDataTypeEnum.of((int)proxyMessage.getType()).getName()); + Solon.context().wrapAndPut(Dispatcher.class, dispatcher); + } + + @Override + public IBeanHandler getBeanHandler() { + return new BeanHandler(); + } + @Bean("tunnelWorkGroup") + public NioEventLoopGroup tunnelWorkGroup(@Inject ProxyConfig proxyConfig) { + return super.tunnelWorkGroup(proxyConfig); + } + + @Bean("tcpRealServerWorkGroup") + public NioEventLoopGroup tcpRealServerWorkGroup(@Inject ProxyConfig proxyConfig) { + // 暂时先公用此配置 + return super.tcpRealServerWorkGroup(proxyConfig); + } + + @Bean("udpServerGroup") + public NioEventLoopGroup udpServerGroup(@Inject ProxyConfig proxyConfig) { + // 暂时先公用此配置 + return super.udpServerGroup(proxyConfig); + } + + @Bean("udpWorkGroup") + public NioEventLoopGroup udpWorkGroup(@Inject ProxyConfig proxyConfig) { + // 暂时先公用此配置 + return super.udpWorkGroup(proxyConfig); + } + + @Bean("cmdTunnelBootstrap") + public Bootstrap cmdTunnelBootstrap(@Inject ProxyConfig proxyConfig, + @Inject("tunnelWorkGroup") NioEventLoopGroup tunnelWorkGroup) { + return super.cmdTunnelBootstrap(proxyConfig,tunnelWorkGroup); + } + + @Bean("tcpProxyTunnelBootstrap") + public Bootstrap tcpProxyTunnelBootstrap(@Inject ProxyConfig proxyConfig, + @Inject("tunnelWorkGroup") NioEventLoopGroup tunnelWorkGroup) { + return super.tcpProxyTunnelBootstrap(proxyConfig,tunnelWorkGroup); + } + + @Bean("udpProxyTunnelBootstrap") + public Bootstrap udpProxyTunnelBootstrap(@Inject ProxyConfig proxyConfig, + @Inject("tunnelWorkGroup") NioEventLoopGroup tunnelWorkGroup) { + return super.udpProxyTunnelBootstrap(proxyConfig,tunnelWorkGroup); + } + + @Bean("realServerBootstrap") + public Bootstrap realServerBootstrap(@Inject ProxyConfig proxyConfig, + @Inject("tcpRealServerWorkGroup") NioEventLoopGroup tcpRealServerWorkGroup + ) { + return super.realServerBootstrap(proxyConfig,tcpRealServerWorkGroup); + } + + @Bean("udpServerBootstrap") + public Bootstrap udpServerBootstrap(@Inject ProxyConfig proxyConfig, + @Inject("udpServerGroup") NioEventLoopGroup udpServerGroup, + @Inject("udpWorkGroup") NioEventLoopGroup udpWorkGroup) { + return super.udpServerBootstrap(proxyConfig,udpServerGroup,udpWorkGroup); + } + + @Bean + public NeutrinoCoreRuntimeNativeRegistrar neutrinoCoreRuntimeNativeRegistrar() { + return super.neutrinoCoreRuntimeNativeRegistrar(); + } +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/LockChannel.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/LockChannel.java new file mode 100644 index 00000000..765e0ba1 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/LockChannel.java @@ -0,0 +1,28 @@ +package org.dromara.neutrinoproxy.client.sdk.util; + +import io.netty.channel.Channel; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * @author: aoshiguchen + * @date: 2023/9/21 + */ +@Accessors(chain = true) +@Data +public class LockChannel { + // 端口号 + private int port; + // 通道 + private Channel channel; + // 期望的响应次数 + private int proxyResponses; + // 超时时间(毫秒) + private long proxyTimeoutMs; + // 被获取的时间 + private Date takeTime; + // 已经响应的次数 + private int responseCount; +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/ProxyUtil.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/ProxyUtil.java new file mode 100644 index 00000000..3e28da24 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/ProxyUtil.java @@ -0,0 +1,226 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.dromara.neutrinoproxy.client.sdk.util; + +import io.netty.channel.ChannelHandler; +import io.netty.handler.ssl.SslHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.core.ProxyChannelBorrowListener; +import org.dromara.neutrinoproxy.core.Constants; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; +import io.netty.util.AttributeKey; +import org.dromara.neutrinoproxy.core.util.FileUtil; +import org.noear.solon.Solon; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import java.io.InputStream; +import java.security.KeyStore; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 代理工具 + * @author: aoshiguchen + * @date: 2022/8/31 + */ +@Slf4j +public class ProxyUtil { + private static final AttributeKey USER_CHANNEL_WRITEABLE = AttributeKey.newInstance("user_channel_writeable"); + + private static final AttributeKey CLIENT_CHANNEL_WRITEABLE = AttributeKey.newInstance("client_channel_writeable"); + + private static final int MAX_POOL_SIZE = 100; + + private static Map realServerChannels = new ConcurrentHashMap(); + + private static ConcurrentLinkedQueue tcpProxyChannelPool = new ConcurrentLinkedQueue(); + private static ConcurrentLinkedQueue udpProxyChannelPool = new ConcurrentLinkedQueue<>(); + + private static volatile Channel cmdChannel; + + private static String clientId; + private static final String CLIENT_ID_FILE = ".NEUTRINO_PROXY_CLIENT_ID"; + + public static void borrowTcpProxyChanel(Bootstrap tcpProxyTunnelBootstrap, final ProxyChannelBorrowListener borrowListener) { + Channel channel = tcpProxyChannelPool.poll(); + if (null != channel) { + borrowListener.success(channel); + return; + } + + tcpProxyTunnelBootstrap.connect().addListener((ChannelFutureListener) future -> { + if (future.isSuccess()) { + borrowListener.success(future.channel()); + } else { + borrowListener.error(future.cause()); + } + }); + } + + public static void returnTcpProxyChanel(Channel proxyChanel) { + if (tcpProxyChannelPool.size() > MAX_POOL_SIZE) { + proxyChanel.close(); + } else { + proxyChanel.config().setOption(ChannelOption.AUTO_READ, true); + proxyChanel.attr(Constants.NEXT_CHANNEL).remove(); + tcpProxyChannelPool.offer(proxyChanel); + } + } + + + + public static void removeTcpProxyChanel(Channel proxyChanel) { + tcpProxyChannelPool.remove(proxyChanel); + } + + public static void borrowUdpProxyChanel(Bootstrap tcpProxyTunnelBootstrap, final ProxyChannelBorrowListener borrowListener) { + Channel channel = udpProxyChannelPool.poll(); + if (null != channel) { + borrowListener.success(channel); + return; + } + + tcpProxyTunnelBootstrap.connect().addListener((ChannelFutureListener) future -> { + if (future.isSuccess()) { + borrowListener.success(future.channel()); + } else { + borrowListener.error(future.cause()); + } + }); + } + + public static void returnUdpProxyChanel(Channel proxyChanel) { + if (udpProxyChannelPool.size() > MAX_POOL_SIZE) { + proxyChanel.close(); + } else { + proxyChanel.config().setOption(ChannelOption.AUTO_READ, true); + proxyChanel.attr(Constants.NEXT_CHANNEL).remove(); + udpProxyChannelPool.offer(proxyChanel); + } + } + + + + public static void removeUdpProxyChanel(Channel proxyChanel) { + udpProxyChannelPool.remove(proxyChanel); + } + + public static void setCmdChannel(Channel cmdChannel) { + ProxyUtil.cmdChannel = cmdChannel; + } + + public static Channel getCmdChannel() { + return cmdChannel; + } + + public static void setRealServerChannelVisitorId(Channel realServerChannel, String visitorId) { + realServerChannel.attr(Constants.VISITOR_ID).set(visitorId); + } + + public static String getVisitorIdByRealServerChannel(Channel realServerChannel) { + return realServerChannel.attr(Constants.VISITOR_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() { + Iterator> 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(); + } + + public static String getClientId() { + if (StringUtils.isNotBlank(clientId)) { + return clientId; + } + ProxyConfig proxyConfig = Solon.context().getBean(ProxyConfig.class); + if (StringUtils.isNotBlank(proxyConfig.getTunnel().getClientId())) { + clientId = proxyConfig.getTunnel().getClientId(); + return clientId; + } + String id = FileUtil.readContentAsString(CLIENT_ID_FILE); + if (StringUtils.isNotBlank(id)) { + clientId = id; + return id; + } + id = UUID.randomUUID().toString().replace("-", ""); + FileUtil.write(CLIENT_ID_FILE, id); + clientId = id; + return id; + } + + public static ChannelHandler createSslHandler(ProxyConfig proxyConfig) { + try { + InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getTunnel().getJksPath()); + + SSLContext clientContext = SSLContext.getInstance("TLS"); + final KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(jksInputStream, proxyConfig.getTunnel().getKeyStorePassword().toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + TrustManager[] trustManagers = tmf.getTrustManagers(); + clientContext.init(null, trustManagers, null); + + SSLEngine sslEngine = clientContext.createSSLEngine(); + sslEngine.setUseClientMode(true); + + return new SslHandler(sslEngine); + } catch (Exception e) { + log.error("create SSL handler failed", e); + e.printStackTrace(); + } + return null; + } + +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpChannelBindInfo.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpChannelBindInfo.java new file mode 100644 index 00000000..21fa5366 --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpChannelBindInfo.java @@ -0,0 +1,22 @@ +package org.dromara.neutrinoproxy.client.sdk.util; + +import io.netty.channel.Channel; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author: aoshiguchen + * @date: 2023/9/21 + */ +@Accessors(chain = true) +@Data +public class UdpChannelBindInfo { + private Channel tunnelChannel; + private LockChannel lockChannel; + private String visitorId; + private String visitorIp; + private int visitorPort; + private int serverPort; + private String targetIp; + private int targetPort; +} diff --git a/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpServerUtil.java b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpServerUtil.java new file mode 100644 index 00000000..ba909d6d --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/java/org/dromara/neutrinoproxy/client/sdk/util/UdpServerUtil.java @@ -0,0 +1,201 @@ +package org.dromara.neutrinoproxy.client.sdk.util; + +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 lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.dromara.neutrinoproxy.client.sdk.config.ProxyConfig; +import org.dromara.neutrinoproxy.client.sdk.constant.Constants; +import org.dromara.neutrinoproxy.client.sdk.core.CustomThreadFactory; +import org.dromara.neutrinoproxy.core.ProxyMessage; +import org.noear.solon.core.runtime.NativeDetector; + +import java.util.*; +import java.util.concurrent.*; + +/** + * @author: aoshiguchen + * @date: 2023/9/21 + */ +@Slf4j +public class UdpServerUtil { + private static final Boolean isSupportUdp = Boolean.FALSE; + private static int udpServerPortMin = 0; + private static int udpServerPortMax = 0; + private static int nextUdpServerPort = 0; + private static Bootstrap udpServerBootstrap; + private static int defaultUdpServerPort; + private static Channel defaultUdpServerChannel; + private static Map portToChannelMap = new ConcurrentHashMap<>(); + /** + * udp服务空闲端口池 + */ + private static ConcurrentLinkedQueue udpServerFreePortPool = new ConcurrentLinkedQueue<>(); + private static List lockChannelList = new ArrayList<>(); + /** + * lockChannel扫描器 + */ + private static final ScheduledExecutorService lockChannelScanner = Executors.newSingleThreadScheduledExecutor(new CustomThreadFactory("lockChannelScanner")); + + /** + * 初始化UDP缓存 + * 1、初始化一个基础UDP服务,用于不需要响应的UDP转发 + * 2、维护一个UDP服务池,用于需要响应的UDP转发 + * @param proxyConfig + */ + public static void initCache(ProxyConfig proxyConfig, Bootstrap udpServerBootstrap) { + // aot 阶段,不初始化UDP服务 + if (NativeDetector.isAotRuntime()) { + return; + } + if (null == proxyConfig.getClient().getUdp() || StringUtils.isEmpty(proxyConfig.getClient().getUdp().getPuppetPortRange())) { + return; + } + ProxyConfig.Udp udpConfig = proxyConfig.getClient().getUdp(); + if (StringUtils.isEmpty(udpConfig.getPuppetPortRange())) { + return; + } + String[] tmp = udpConfig.getPuppetPortRange().split("-"); + if (null == tmp || tmp.length != 2) { + log.error("client udp config error!"); + return; + } + try { + udpServerPortMin = Integer.parseInt(tmp[0]); + udpServerPortMax = Integer.parseInt(tmp[1]); + if (udpServerPortMax <= udpServerPortMin) { + // 至少得给1个udp端口,一个用于基础无响应UDP转发 + throw new RuntimeException("client udp config error!"); + } + nextUdpServerPort = udpServerPortMin; + UdpServerUtil.udpServerBootstrap = udpServerBootstrap; + log.info("udp proxy server port: {} ~ {}", udpServerPortMin, udpServerPortMax); + // 初始化udp服务 + initUdpServer(); + // 初始化lockChannel扫描器 + lockChannelScanner.scheduleWithFixedDelay(UdpServerUtil::lockChannelScan, 5, 3, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("client udp config error!", e); + return; + } + } + + /** + * 初始化udp服务 + */ + private static void initUdpServer() { + defaultUdpServerPort = nextUdpServerPort(); + defaultUdpServerChannel = bindPort(defaultUdpServerPort); + // 初始化默认最多额外开启5个udp服务,其他的需要时再启动 + for (int i = 0; i < 5; i++) { + if (!hasNextUdpServerPort()) { + return; + } + int port = nextUdpServerPort(); + Channel ch = bindPort(port); + portToChannelMap.put(port, ch); + udpServerFreePortPool.offer(port); + } + while (hasNextUdpServerPort()) { + udpServerFreePortPool.offer(nextUdpServerPort()); + } + } + + private static Channel bindPort(int port) { + try { + ChannelFuture channelFuture = udpServerBootstrap.bind(port).sync(); + log.info("[udp server] bind port:{} success!", port); + return channelFuture.channel(); + } catch (InterruptedException e) { + log.error("[udp server] bind port:{} error!", port); + throw new RuntimeException(e); + } + } + + public static Boolean hasNextUdpServerPort() { + return nextUdpServerPort <= udpServerPortMax; + } + + public static synchronized int nextUdpServerPort() { + return nextUdpServerPort++; + } + + /** + * 获取一个可用的udp通道 + * 1、如果期待的响应为0,或者超时时间<=0,则认为不需要响应,直接返回默认的udp服务,否则继续下一步 + * 2、从可用端口队列中找到一个可用端口,若不存在可用端口,则降级为不需要响应,返回默认的udp服务。否则继续下一步 + * 3、根据该端口找到udp服务通道,找不到则绑定端口开启一个通道并返回。将该端口添加到锁定列表 + * 4、维护一个定时器的,定时扫描锁定列表,及时释放锁定的端口 + * @param info + * @return + */ + public static synchronized Channel takeChannel(ProxyMessage.UdpBaseInfo info, Channel tunnelChannel) { + if (info.getProxyResponses() <= 0 || info.getProxyTimeoutMs() <= 0) { + return defaultUdpServerChannel; + } + Integer port = udpServerFreePortPool.poll(); + if (null == port) { + return defaultUdpServerChannel; + } + Channel channel = portToChannelMap.get(port); + if (null == channel) { + channel = bindPort(port); + portToChannelMap.put(port, channel); + } + // 添加到锁定队列 + LockChannel lockChannel = new LockChannel() + .setPort(port) + .setChannel(channel) + .setProxyResponses(info.getProxyResponses()) + .setProxyTimeoutMs(info.getProxyTimeoutMs()) + .setTakeTime(new Date()) + .setResponseCount(0); + lockChannelList.add(lockChannel); + channel.attr(Constants.UDP_CHANNEL_BIND_KEY).set(new UdpChannelBindInfo() + .setTunnelChannel(tunnelChannel) + .setVisitorId(info.getVisitorId()) + .setVisitorIp(info.getVisitorIp()) + .setVisitorPort(info.getVisitorPort()) + .setServerPort(info.getServerPort()) + .setTargetIp(info.getTargetIp()) + .setTargetPort(info.getTargetPort()) + .setLockChannel(lockChannel) + ); + return channel; + } + + /** + * lockChannel扫描 + */ + public static synchronized void lockChannelScan() { + if (lockChannelList.isEmpty()) { + return; + } + Iterator iter = lockChannelList.iterator(); + if (iter.hasNext()) { + LockChannel lockChannel = iter.next(); + if (lockChannel.getResponseCount() >= lockChannel.getProxyResponses() || + System.currentTimeMillis() - lockChannel.getTakeTime().getTime() >= lockChannel.getProxyTimeoutMs() + ) { + iter.remove(); + UdpChannelBindInfo udpChannelBindInfo = lockChannel.getChannel().attr(Constants.UDP_CHANNEL_BIND_KEY).get(); + // 此处必须释放代理隧道 + closeChannel(udpChannelBindInfo.getTunnelChannel()); + lockChannel.getChannel().attr(Constants.UDP_CHANNEL_BIND_KEY).set(null); + udpServerFreePortPool.offer(lockChannel.getPort()); + log.debug("[udp channel]release udp channel port:{}", lockChannel.getPort()); + } + } + } + + private static void closeChannel(Channel channel) { + try { + channel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); + } catch (Exception e) { + // ignore + } + } +} diff --git a/neutrino-proxy-client-sdk/src/main/resources/app.yml b/neutrino-proxy-client-sdk/src/main/resources/app.yml new file mode 100644 index 00000000..a122416c --- /dev/null +++ b/neutrino-proxy-client-sdk/src/main/resources/app.yml @@ -0,0 +1,68 @@ +solon: + config: + add: ./app.yml + app: + name: neutrino-proxy-client + version: 2.0.1 +# 日志级别 +solon.logging.appender: + console: + pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta(${PID:-}) --- %-15([%15.15thread]) %-56(%cyan(%-40.40logger{39}%L)) : %msg%n" + file: + enable: true + pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level ${PID:-} --- %-15([%15.15thread]) %-56(%-40.40logger{39}%L) : %msg%n" + name: "logs/${neutrino.application.name}" + rolling: "logs/${neutrino.application.name}_%d{yyyy-MM-dd}_%i.log.gz" +solon.logging.logger: + "root": + level: info + +neutrino: + application: + name: neutrino-proxy-client + + proxy: + protocol: + max-frame-length: 2097152 + length-field-offset: 0 + length-field-length: 4 + initial-bytes-to-strip: 0 + length-adjustment: 0 + read-idle-time: 120 + write-idle-time: 20 + all-idle-time-seconds: 0 + tunnel: + # 线程池相关配置,用于技术调优,可忽略 + thread-count: 50 + # 隧道SSL证书配置 + key-store-password: ${STORE_PASS:123456} + jks-path: ${JKS_PATH:classpath:/test.jks} + # 服务端IP + server-ip: ${SERVER_IP:localhost} + # 服务端端口(对应服务端app.yml中的tunnel.port、tunnel.ssl-port) + server-port: ${SERVER_PORT:9002} + # 是否启用SSL(注意:该配置必须和server-port对应上) + ssl-enable: ${SSL_ENABLE:true} + # 客户端连接唯一凭证 + license-key: ${LICENSE_KEY:} + # 客户端唯一身份标识(可忽略,若不设置首次启动会自动生成) + client-id: ${CLIENT_ID:} + # 是否开启隧道传输报文日志(日志级别为debug时开启才有效) + transfer-log-enable: ${CLIENT_LOG:false} + # 是否开启心跳日志 + heartbeat-log-enable: ${HEARTBEAT_LOG:false} + # 重连设置 + reconnection: + # 重连间隔(秒) + interval-seconds: 10 + # 是否开启无限重连(未开启时,客户端license不合法会自动停止应用,开启了则不会,请谨慎开启) + unlimited: false + client: + udp: + # 线程池相关配置,用于技术调优,可忽略 + boss-thread-count: 5 + work-thread-count: 20 + # udp傀儡端口范围 + puppet-port-range: 10000-10500 + # 是否开启隧道传输报文日志(日志级别为debug时开启才有效) + transfer-log-enable: ${CLIENT_LOG:false} diff --git a/neutrino-proxy-client-sdk/src/main/resources/test.jks b/neutrino-proxy-client-sdk/src/main/resources/test.jks new file mode 100644 index 0000000000000000000000000000000000000000..3a4d1ad89e1a538f35c3b2996b2241d465ddedad GIT binary patch literal 1388 zcmezO_TO6u1_mY|W&~rN#Nzbi;^Lf=)Z!9DpkSlVC1(Smg53s9Oxq3k*toRW7+Dy# zm;@OaSs7TGm=?c3uQzq2R{u|yvt1m9yKNY51-F}KEJ}Vg&o%pt^a}sGfvLwnY29Gd zTK;ObR&0Uur4M;;GlS;#-gw>e_e9sjrFAU2jh#`x$0uYu?)VbdCRlQ;cbop#2hGK= ziyy2KJ)QF9`=T?|dPT+u;&T-HqqpXzeZ1Z;XvAW7?@w^7din<*!I_J#87{mRYhpFg z@10k1i)D8CPu+uI?f*lb3r_A`$5e44A#U5J9|1W>CN7u}_~lBo{fzJ1IL%&NU}<<@ z)V5RUV4@=b#-E8d!w;I?vHr&+aeRy5o2%mfvpOZ$tUL2=>Dq(Id<=a@GJI<6S5H4u znXcaTzss0K{no9IXN%k}NJdS5VmIZ|CVq(%7xkDWSNI2s*L3>c6XI2Qnk3%4xN7-Z z58Kt}*`GbsV867(r#9b5I?Mg%X4$%9wtrgW+e`}+Z6>$Y%)7nFiZNR81w$Cu_AqJb zzqyy!eHU5z`gxZ54>#VWPmcX@Kf_vD9oC%ove36y^O4cD+_1&y;RvIo@|$6)$zqXeQT_l?H8kLAw$r zW&M}^;8?ul=EW1DugazGOx|!dyk>gn(nEVXFF&u44!K+@p_`SSd3O6zjd|~vuP@Eo zy6e1E!t3PCp*e@H-@KvjCZD_N^S!@nri)hSm3uGuJ3nz&&gnN_*k`dPIdv?mE2<0) z56!D}6o2z@sq%h~hv(ulYQmY{DQ6#_`&EQZq|NmC&$fu4C+AjmZ+#GX^7=XZ1V5*` zm6c};Rx>@_RU~_-Y|8n6#)379wv)A$HviU1z3}*UbKtfqO~zM`OX=UypC-e+wJ?>- z;AcTcx$N;XYo=LxTh7|MHchRL>>MUXhuXmYSCi6X8aP@EUM~ z)G!OPR3_%78_0?C8W|aw85$T^n3@=xM~U+q1Gxs~P%eEO(KsL3@4!6G+}O)t(Ade; z*vN3L^6OR^0Yd^Q#lYBs__SiYmr~JCS z&z|e7)OnIFx8r=dQti~DZH^%(Dwy0jzrma7_ z+b`we=F3O6^6gRblG-}2vnTF(*1Cxb^(}4nPC0Sk=Q1%fGB7SyG>|ut1%{g}ABz}^ zNV)8t_!eH_w&B5RX^O|dbLmff|Ae|f#!{yG!~gS3T7CXD&12I zwcEQW@#FDqN5Xx ioku-b)|M>LROU)QG0ot&TE@jEVVOmyMi>7@Z~y={B|CNi literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 46b7b52b..70cd80d5 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ neutrino-proxy-core neutrino-proxy-client neutrino-proxy-server + neutrino-proxy-client-sdk