Compare commits

..
1 Commits
Author SHA1 Message Date
fatedier 443b9bca66 server: introduce SessionContext to encapsulate NewControl parameters
Replace 10 positional parameters in NewControl() with a single
SessionContext struct, matching the client-side pattern. This also
eliminates the post-construction mutation of clientRegistry and
removes two TODO comments.
2026-03-07 18:26:56 +08:00
46 changed files with 325 additions and 277 deletions
-4
View File
@@ -18,7 +18,6 @@ linters:
- lll - lll
- makezero - makezero
- misspell - misspell
- modernize
- prealloc - prealloc
- predeclared - predeclared
- revive - revive
@@ -48,9 +47,6 @@ linters:
ignore-rules: ignore-rules:
- cancelled - cancelled
- marshalled - marshalled
modernize:
disable:
- omitzero
unparam: unparam:
check-exported: false check-exported: false
exclusions: exclusions:
+1 -1
View File
@@ -32,7 +32,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.SUDPProxyConfig](), NewSUDPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.SUDPProxyConfig{}), NewSUDPProxy)
} }
type SUDPProxy struct { type SUDPProxy struct {
+1 -1
View File
@@ -31,7 +31,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.UDPProxyConfig](), NewUDPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.UDPProxyConfig{}), NewUDPProxy)
} }
type UDPProxy struct { type UDPProxy struct {
+1 -1
View File
@@ -34,7 +34,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.XTCPProxyConfig](), NewXTCPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.XTCPProxyConfig{}), NewXTCPProxy)
} }
type XTCPProxy struct { type XTCPProxy struct {
+53 -5
View File
@@ -15,12 +15,18 @@
package visitor package visitor
import ( import (
"fmt"
"io"
"net" "net"
"strconv" "strconv"
"time"
libio "github.com/fatedier/golib/io" libio "github.com/fatedier/golib/io"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/naming"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
) )
@@ -55,6 +61,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
var tunnelErr error var tunnelErr error
defer func() { defer func() {
// If there was an error and connection supports CloseWithError, use it
if tunnelErr != nil { if tunnelErr != nil {
if eConn, ok := userConn.(interface{ CloseWithError(error) error }); ok { if eConn, ok := userConn.(interface{ CloseWithError(error) error }); ok {
_ = eConn.CloseWithError(tunnelErr) _ = eConn.CloseWithError(tunnelErr)
@@ -65,21 +72,62 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
}() }()
xl.Debugf("get a new stcp user connection") xl.Debugf("get a new stcp user connection")
visitorConn, err := sv.dialRawVisitorConn(sv.cfg.GetBaseConfig()) visitorConn, err := sv.helper.ConnectServer()
if err != nil { if err != nil {
xl.Warnf("dialRawVisitorConn error: %v", err)
tunnelErr = err tunnelErr = err
return return
} }
defer visitorConn.Close() defer visitorConn.Close()
remote, recycleFn, err := wrapVisitorConn(visitorConn, sv.cfg.GetBaseConfig()) now := time.Now().Unix()
targetProxyName := naming.BuildTargetServerProxyName(sv.clientCfg.User, sv.cfg.ServerUser, sv.cfg.ServerName)
newVisitorConnMsg := &msg.NewVisitorConn{
RunID: sv.helper.RunID(),
ProxyName: targetProxyName,
SignKey: util.GetAuthKey(sv.cfg.SecretKey, now),
Timestamp: now,
UseEncryption: sv.cfg.Transport.UseEncryption,
UseCompression: sv.cfg.Transport.UseCompression,
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil { if err != nil {
xl.Warnf("wrapVisitorConn error: %v", err) xl.Warnf("send newVisitorConnMsg to server error: %v", err)
tunnelErr = err tunnelErr = err
return return
} }
defer recycleFn()
var newVisitorConnRespMsg msg.NewVisitorConnResp
_ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
xl.Warnf("get newVisitorConnRespMsg error: %v", err)
tunnelErr = err
return
}
_ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
xl.Warnf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
tunnelErr = fmt.Errorf("%s", newVisitorConnRespMsg.Error)
return
}
var remote io.ReadWriteCloser
remote = visitorConn
if sv.cfg.Transport.UseEncryption {
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
if err != nil {
xl.Errorf("create encryption stream error: %v", err)
tunnelErr = err
return
}
}
if sv.cfg.Transport.UseCompression {
var recycleFn func()
remote, recycleFn = libio.WithCompressionFromPool(remote)
defer recycleFn()
}
libio.Join(userConn, remote) libio.Join(userConn, remote)
} }
+53 -13
View File
@@ -16,17 +16,21 @@ package visitor
import ( import (
"fmt" "fmt"
"io"
"net" "net"
"strconv" "strconv"
"sync" "sync"
"time" "time"
"github.com/fatedier/golib/errors" "github.com/fatedier/golib/errors"
libio "github.com/fatedier/golib/io"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/naming"
"github.com/fatedier/frp/pkg/proto/udp" "github.com/fatedier/frp/pkg/proto/udp"
netpkg "github.com/fatedier/frp/pkg/util/net" netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
) )
@@ -72,7 +76,6 @@ func (sv *SUDPVisitor) dispatcher() {
var ( var (
visitorConn net.Conn visitorConn net.Conn
recycleFn func()
err error err error
firstPacket *msg.UDPPacket firstPacket *msg.UDPPacket
@@ -90,17 +93,14 @@ func (sv *SUDPVisitor) dispatcher() {
return return
} }
visitorConn, recycleFn, err = sv.getNewVisitorConn() visitorConn, err = sv.getNewVisitorConn()
if err != nil { if err != nil {
xl.Warnf("newVisitorConn to frps error: %v, try to reconnect", err) xl.Warnf("newVisitorConn to frps error: %v, try to reconnect", err)
continue continue
} }
// visitorConn always be closed when worker done. // visitorConn always be closed when worker done.
func() { sv.worker(visitorConn, firstPacket)
defer recycleFn()
sv.worker(visitorConn, firstPacket)
}()
select { select {
case <-sv.checkCloseCh: case <-sv.checkCloseCh:
@@ -198,17 +198,57 @@ func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
xl.Infof("sudp worker is closed") xl.Infof("sudp worker is closed")
} }
func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, func(), error) { func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
rawConn, err := sv.dialRawVisitorConn(sv.cfg.GetBaseConfig()) xl := xlog.FromContextSafe(sv.ctx)
visitorConn, err := sv.helper.ConnectServer()
if err != nil { if err != nil {
return nil, func() {}, err return nil, fmt.Errorf("frpc connect frps error: %v", err)
} }
rwc, recycleFn, err := wrapVisitorConn(rawConn, sv.cfg.GetBaseConfig())
now := time.Now().Unix()
targetProxyName := naming.BuildTargetServerProxyName(sv.clientCfg.User, sv.cfg.ServerUser, sv.cfg.ServerName)
newVisitorConnMsg := &msg.NewVisitorConn{
RunID: sv.helper.RunID(),
ProxyName: targetProxyName,
SignKey: util.GetAuthKey(sv.cfg.SecretKey, now),
Timestamp: now,
UseEncryption: sv.cfg.Transport.UseEncryption,
UseCompression: sv.cfg.Transport.UseCompression,
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil { if err != nil {
rawConn.Close() visitorConn.Close()
return nil, func() {}, err return nil, fmt.Errorf("frpc send newVisitorConnMsg to frps error: %v", err)
} }
return netpkg.WrapReadWriteCloserToConn(rwc, rawConn), recycleFn, nil
var newVisitorConnRespMsg msg.NewVisitorConnResp
_ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
visitorConn.Close()
return nil, fmt.Errorf("frpc read newVisitorConnRespMsg error: %v", err)
}
_ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
visitorConn.Close()
return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
}
var remote io.ReadWriteCloser
remote = visitorConn
if sv.cfg.Transport.UseEncryption {
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
if err != nil {
xl.Errorf("create encryption stream error: %v", err)
visitorConn.Close()
return nil, err
}
}
if sv.cfg.Transport.UseCompression {
remote = libio.WithCompression(remote)
}
return netpkg.WrapReadWriteCloserToConn(remote, visitorConn), nil
} }
func (sv *SUDPVisitor) Close() { func (sv *SUDPVisitor) Close() {
-62
View File
@@ -16,21 +16,13 @@ package visitor
import ( import (
"context" "context"
"fmt"
"io"
"net" "net"
"sync" "sync"
"time"
libio "github.com/fatedier/golib/io"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/naming"
plugin "github.com/fatedier/frp/pkg/plugin/visitor" plugin "github.com/fatedier/frp/pkg/plugin/visitor"
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
netpkg "github.com/fatedier/frp/pkg/util/net" netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/frp/pkg/vnet" "github.com/fatedier/frp/pkg/vnet"
) )
@@ -150,57 +142,3 @@ func (v *BaseVisitor) Close() {
v.plugin.Close() v.plugin.Close()
} }
} }
func (v *BaseVisitor) dialRawVisitorConn(cfg *v1.VisitorBaseConfig) (net.Conn, error) {
visitorConn, err := v.helper.ConnectServer()
if err != nil {
return nil, fmt.Errorf("connect to server error: %v", err)
}
now := time.Now().Unix()
targetProxyName := naming.BuildTargetServerProxyName(v.clientCfg.User, cfg.ServerUser, cfg.ServerName)
newVisitorConnMsg := &msg.NewVisitorConn{
RunID: v.helper.RunID(),
ProxyName: targetProxyName,
SignKey: util.GetAuthKey(cfg.SecretKey, now),
Timestamp: now,
UseEncryption: cfg.Transport.UseEncryption,
UseCompression: cfg.Transport.UseCompression,
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil {
visitorConn.Close()
return nil, fmt.Errorf("send newVisitorConnMsg to server error: %v", err)
}
var newVisitorConnRespMsg msg.NewVisitorConnResp
_ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
visitorConn.Close()
return nil, fmt.Errorf("read newVisitorConnRespMsg error: %v", err)
}
_ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
visitorConn.Close()
return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
}
return visitorConn, nil
}
func wrapVisitorConn(conn io.ReadWriteCloser, cfg *v1.VisitorBaseConfig) (io.ReadWriteCloser, func(), error) {
rwc := conn
if cfg.Transport.UseEncryption {
var err error
rwc, err = libio.WithEncryption(rwc, []byte(cfg.SecretKey))
if err != nil {
return nil, func() {}, fmt.Errorf("create encryption stream error: %v", err)
}
}
recycleFn := func() {}
if cfg.Transport.UseCompression {
rwc, recycleFn = libio.WithCompressionFromPool(rwc)
}
return rwc, recycleFn, nil
}
+14 -7
View File
@@ -182,14 +182,21 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
return return
} }
muxConnRWCloser, recycleFn, err := wrapVisitorConn(tunnelConn, sv.cfg.GetBaseConfig()) var muxConnRWCloser io.ReadWriteCloser = tunnelConn
if err != nil { if sv.cfg.Transport.UseEncryption {
xl.Errorf("%v", err) muxConnRWCloser, err = libio.WithEncryption(muxConnRWCloser, []byte(sv.cfg.SecretKey))
tunnelConn.Close() if err != nil {
tunnelErr = err xl.Errorf("create encryption stream error: %v", err)
return tunnelConn.Close()
tunnelErr = err
return
}
}
if sv.cfg.Transport.UseCompression {
var recycleFn func()
muxConnRWCloser, recycleFn = libio.WithCompressionFromPool(muxConnRWCloser)
defer recycleFn()
} }
defer recycleFn()
_, _, errs := libio.Join(userConn, muxConnRWCloser) _, _, errs := libio.Join(userConn, muxConnRWCloser)
xl.Debugf("join connections closed") xl.Debugf("join connections closed")
+1 -1
View File
@@ -47,7 +47,7 @@ var natholeDiscoveryCmd = &cobra.Command{
Use: "discover", Use: "discover",
Short: "Discover nathole information from stun server", Short: "Discover nathole information from stun server",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
// ignore error here, because we can use command line parameters // ignore error here, because we can use command line pameters
cfg, _, _, _, err := config.LoadClientConfig(cfgFile, strictConfigMode) cfg, _, _, _, err := config.LoadClientConfig(cfgFile, strictConfigMode)
if err != nil { if err != nil {
cfg = &v1.ClientCommonConfig{} cfg = &v1.ClientCommonConfig{}
+8 -8
View File
@@ -39,14 +39,14 @@ const (
// Proxy // Proxy
var ( var (
proxyConfTypeMap = map[ProxyType]reflect.Type{ proxyConfTypeMap = map[ProxyType]reflect.Type{
ProxyTypeTCP: reflect.TypeFor[TCPProxyConf](), ProxyTypeTCP: reflect.TypeOf(TCPProxyConf{}),
ProxyTypeUDP: reflect.TypeFor[UDPProxyConf](), ProxyTypeUDP: reflect.TypeOf(UDPProxyConf{}),
ProxyTypeTCPMUX: reflect.TypeFor[TCPMuxProxyConf](), ProxyTypeTCPMUX: reflect.TypeOf(TCPMuxProxyConf{}),
ProxyTypeHTTP: reflect.TypeFor[HTTPProxyConf](), ProxyTypeHTTP: reflect.TypeOf(HTTPProxyConf{}),
ProxyTypeHTTPS: reflect.TypeFor[HTTPSProxyConf](), ProxyTypeHTTPS: reflect.TypeOf(HTTPSProxyConf{}),
ProxyTypeSTCP: reflect.TypeFor[STCPProxyConf](), ProxyTypeSTCP: reflect.TypeOf(STCPProxyConf{}),
ProxyTypeXTCP: reflect.TypeFor[XTCPProxyConf](), ProxyTypeXTCP: reflect.TypeOf(XTCPProxyConf{}),
ProxyTypeSUDP: reflect.TypeFor[SUDPProxyConf](), ProxyTypeSUDP: reflect.TypeOf(SUDPProxyConf{}),
} }
) )
+3 -3
View File
@@ -32,9 +32,9 @@ const (
// Visitor // Visitor
var ( var (
visitorConfTypeMap = map[VisitorType]reflect.Type{ visitorConfTypeMap = map[VisitorType]reflect.Type{
VisitorTypeSTCP: reflect.TypeFor[STCPVisitorConf](), VisitorTypeSTCP: reflect.TypeOf(STCPVisitorConf{}),
VisitorTypeXTCP: reflect.TypeFor[XTCPVisitorConf](), VisitorTypeXTCP: reflect.TypeOf(XTCPVisitorConf{}),
VisitorTypeSUDP: reflect.TypeFor[SUDPVisitorConf](), VisitorTypeSUDP: reflect.TypeOf(SUDPVisitorConf{}),
} }
) )
+1 -1
View File
@@ -38,7 +38,7 @@ func parseNumberRangePair(firstRangeStr, secondRangeStr string) ([]NumberPair, e
return nil, fmt.Errorf("first and second range numbers are not in pairs") return nil, fmt.Errorf("first and second range numbers are not in pairs")
} }
pairs := make([]NumberPair, 0, len(firstRangeNumbers)) pairs := make([]NumberPair, 0, len(firstRangeNumbers))
for i := range firstRangeNumbers { for i := 0; i < len(firstRangeNumbers); i++ {
pairs = append(pairs, NumberPair{ pairs = append(pairs, NumberPair{
First: firstRangeNumbers[i], First: firstRangeNumbers[i],
Second: secondRangeNumbers[i], Second: secondRangeNumbers[i],
+2 -2
View File
@@ -137,8 +137,8 @@ func (p PortsRangeSlice) String() string {
func NewPortsRangeSliceFromString(str string) ([]PortsRange, error) { func NewPortsRangeSliceFromString(str string) ([]PortsRange, error) {
str = strings.TrimSpace(str) str = strings.TrimSpace(str)
out := []PortsRange{} out := []PortsRange{}
numRanges := strings.SplitSeq(str, ",") numRanges := strings.Split(str, ",")
for numRangeStr := range numRanges { for _, numRangeStr := range numRanges {
// 1000-2000 or 2001 // 1000-2000 or 2001
numArray := strings.Split(numRangeStr, "-") numArray := strings.Split(numRangeStr, "-")
// length: only 1 or 2 is correct // length: only 1 or 2 is correct
+8 -8
View File
@@ -239,14 +239,14 @@ const (
) )
var proxyConfigTypeMap = map[ProxyType]reflect.Type{ var proxyConfigTypeMap = map[ProxyType]reflect.Type{
ProxyTypeTCP: reflect.TypeFor[TCPProxyConfig](), ProxyTypeTCP: reflect.TypeOf(TCPProxyConfig{}),
ProxyTypeUDP: reflect.TypeFor[UDPProxyConfig](), ProxyTypeUDP: reflect.TypeOf(UDPProxyConfig{}),
ProxyTypeHTTP: reflect.TypeFor[HTTPProxyConfig](), ProxyTypeHTTP: reflect.TypeOf(HTTPProxyConfig{}),
ProxyTypeHTTPS: reflect.TypeFor[HTTPSProxyConfig](), ProxyTypeHTTPS: reflect.TypeOf(HTTPSProxyConfig{}),
ProxyTypeTCPMUX: reflect.TypeFor[TCPMuxProxyConfig](), ProxyTypeTCPMUX: reflect.TypeOf(TCPMuxProxyConfig{}),
ProxyTypeSTCP: reflect.TypeFor[STCPProxyConfig](), ProxyTypeSTCP: reflect.TypeOf(STCPProxyConfig{}),
ProxyTypeXTCP: reflect.TypeFor[XTCPProxyConfig](), ProxyTypeXTCP: reflect.TypeOf(XTCPProxyConfig{}),
ProxyTypeSUDP: reflect.TypeFor[SUDPProxyConfig](), ProxyTypeSUDP: reflect.TypeOf(SUDPProxyConfig{}),
} }
func NewProxyConfigurerByType(proxyType ProxyType) ProxyConfigurer { func NewProxyConfigurerByType(proxyType ProxyType) ProxyConfigurer {
+10 -10
View File
@@ -37,16 +37,16 @@ const (
) )
var clientPluginOptionsTypeMap = map[string]reflect.Type{ var clientPluginOptionsTypeMap = map[string]reflect.Type{
PluginHTTP2HTTPS: reflect.TypeFor[HTTP2HTTPSPluginOptions](), PluginHTTP2HTTPS: reflect.TypeOf(HTTP2HTTPSPluginOptions{}),
PluginHTTPProxy: reflect.TypeFor[HTTPProxyPluginOptions](), PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}),
PluginHTTPS2HTTP: reflect.TypeFor[HTTPS2HTTPPluginOptions](), PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}),
PluginHTTPS2HTTPS: reflect.TypeFor[HTTPS2HTTPSPluginOptions](), PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}),
PluginHTTP2HTTP: reflect.TypeFor[HTTP2HTTPPluginOptions](), PluginHTTP2HTTP: reflect.TypeOf(HTTP2HTTPPluginOptions{}),
PluginSocks5: reflect.TypeFor[Socks5PluginOptions](), PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}),
PluginStaticFile: reflect.TypeFor[StaticFilePluginOptions](), PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}),
PluginUnixDomainSocket: reflect.TypeFor[UnixDomainSocketPluginOptions](), PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}),
PluginTLS2Raw: reflect.TypeFor[TLS2RawPluginOptions](), PluginTLS2Raw: reflect.TypeOf(TLS2RawPluginOptions{}),
PluginVirtualNet: reflect.TypeFor[VirtualNetPluginOptions](), PluginVirtualNet: reflect.TypeOf(VirtualNetPluginOptions{}),
} }
type ClientPluginOptions interface { type ClientPluginOptions interface {
+3 -3
View File
@@ -79,9 +79,9 @@ const (
) )
var visitorConfigTypeMap = map[VisitorType]reflect.Type{ var visitorConfigTypeMap = map[VisitorType]reflect.Type{
VisitorTypeSTCP: reflect.TypeFor[STCPVisitorConfig](), VisitorTypeSTCP: reflect.TypeOf(STCPVisitorConfig{}),
VisitorTypeXTCP: reflect.TypeFor[XTCPVisitorConfig](), VisitorTypeXTCP: reflect.TypeOf(XTCPVisitorConfig{}),
VisitorTypeSUDP: reflect.TypeFor[SUDPVisitorConfig](), VisitorTypeSUDP: reflect.TypeOf(SUDPVisitorConfig{}),
} }
type TypedVisitorConfig struct { type TypedVisitorConfig struct {
+1 -1
View File
@@ -25,7 +25,7 @@ const (
) )
var visitorPluginOptionsTypeMap = map[string]reflect.Type{ var visitorPluginOptionsTypeMap = map[string]reflect.Type{
VisitorPluginVirtualNet: reflect.TypeFor[VirtualNetVisitorPluginOptions](), VisitorPluginVirtualNet: reflect.TypeOf(VirtualNetVisitorPluginOptions{}),
} }
type VisitorPluginOptions interface { type VisitorPluginOptions interface {
+1 -1
View File
@@ -61,7 +61,7 @@ var msgTypeMap = map[byte]any{
TypeNatHoleReport: NatHoleReport{}, TypeNatHoleReport: NatHoleReport{},
} }
var TypeNameNatHoleResp = reflect.TypeFor[NatHoleResp]().Name() var TypeNameNatHoleResp = reflect.TypeOf(&NatHoleResp{}).Elem().Name()
type ClientSpec struct { type ClientSpec struct {
// Due to the support of VirtualClient, frps needs to know the client type in order to // Due to the support of VirtualClient, frps needs to know the client type in order to
+1 -1
View File
@@ -151,7 +151,7 @@ func getBehaviorScoresByMode(mode int, defaultScore int) []*BehaviorScore {
func getBehaviorScoresByMode2(mode int, senderScore, receiverScore int) []*BehaviorScore { func getBehaviorScoresByMode2(mode int, senderScore, receiverScore int) []*BehaviorScore {
behaviors := getBehaviorByMode(mode) behaviors := getBehaviorByMode(mode)
scores := make([]*BehaviorScore, 0, len(behaviors)) scores := make([]*BehaviorScore, 0, len(behaviors))
for i := range behaviors { for i := 0; i < len(behaviors); i++ {
score := receiverScore score := receiverScore
if behaviors[i].A.Role == DetectRoleSender { if behaviors[i].A.Role == DetectRoleSender {
score = senderScore score = senderScore
+2 -2
View File
@@ -410,7 +410,7 @@ func sendSidMessageToRandomPorts(
xl := xlog.FromContextSafe(ctx) xl := xlog.FromContextSafe(ctx)
used := sets.New[int]() used := sets.New[int]()
getUnusedPort := func() int { getUnusedPort := func() int {
for range 10 { for i := 0; i < 10; i++ {
port := rand.IntN(65535-1024) + 1024 port := rand.IntN(65535-1024) + 1024
if !used.Has(port) { if !used.Has(port) {
used.Insert(port) used.Insert(port)
@@ -420,7 +420,7 @@ func sendSidMessageToRandomPorts(
return 0 return 0
} }
for range count { for i := 0; i < count; i++ {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
+4 -1
View File
@@ -153,7 +153,10 @@ func (p *VirtualNetPlugin) run() {
// Exponential backoff: 60s, 120s, 240s, 300s (capped) // Exponential backoff: 60s, 120s, 240s, 300s (capped)
baseDelay := 60 * time.Second baseDelay := 60 * time.Second
reconnectDelay = min(baseDelay*time.Duration(1<<uint(p.consecutiveErrors-1)), 300*time.Second) reconnectDelay = baseDelay * time.Duration(1<<uint(p.consecutiveErrors-1))
if reconnectDelay > 300*time.Second {
reconnectDelay = 300 * time.Second
}
} else { } else {
// Reset consecutive errors on successful connection // Reset consecutive errors on successful connection
if p.consecutiveErrors > 0 { if p.consecutiveErrors > 0 {
+12 -5
View File
@@ -16,7 +16,6 @@ package featuregate
import ( import (
"fmt" "fmt"
"maps"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -94,7 +93,9 @@ type featureGate struct {
// NewFeatureGate creates a new feature gate with the default features // NewFeatureGate creates a new feature gate with the default features
func NewFeatureGate() MutableFeatureGate { func NewFeatureGate() MutableFeatureGate {
known := map[Feature]FeatureSpec{} known := map[Feature]FeatureSpec{}
maps.Copy(known, defaultFeatures) for k, v := range defaultFeatures {
known[k] = v
}
f := &featureGate{} f := &featureGate{}
f.known.Store(known) f.known.Store(known)
@@ -109,9 +110,13 @@ func (f *featureGate) SetFromMap(m map[string]bool) error {
// Copy existing state // Copy existing state
known := map[Feature]FeatureSpec{} known := map[Feature]FeatureSpec{}
maps.Copy(known, f.known.Load().(map[Feature]FeatureSpec)) for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
known[k] = v
}
enabled := map[Feature]bool{} enabled := map[Feature]bool{}
maps.Copy(enabled, f.enabled.Load().(map[Feature]bool)) for k, v := range f.enabled.Load().(map[Feature]bool) {
enabled[k] = v
}
// Apply the new settings // Apply the new settings
for k, v := range m { for k, v := range m {
@@ -143,7 +148,9 @@ func (f *featureGate) Add(features map[Feature]FeatureSpec) error {
// Copy existing state // Copy existing state
known := map[Feature]FeatureSpec{} known := map[Feature]FeatureSpec{}
maps.Copy(known, f.known.Load().(map[Feature]FeatureSpec)) for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
known[k] = v
}
// Add new features // Add new features
for name, spec := range features { for name, spec := range features {
+3 -3
View File
@@ -89,11 +89,11 @@ func ParseBasicAuth(auth string) (username, password string, ok bool) {
return return
} }
cs := string(c) cs := string(c)
before, after, found := strings.Cut(cs, ":") s := strings.IndexByte(cs, ':')
if !found { if s < 0 {
return return
} }
return before, after, true return cs[:s], cs[s+1:], true
} }
func BasicAuth(username, passwd string) string { func BasicAuth(username, passwd string) string {
+5 -1
View File
@@ -86,7 +86,11 @@ func (c *FakeUDPConn) Read(b []byte) (n int, err error) {
c.lastActive = time.Now() c.lastActive = time.Now()
c.mu.Unlock() c.mu.Unlock()
n = min(len(b), len(content)) if len(b) < len(content) {
n = len(b)
} else {
n = len(content)
}
copy(b, content) copy(b, content)
return n, nil return n, nil
} }
+2 -2
View File
@@ -68,8 +68,8 @@ func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) {
rangeStr = strings.TrimSpace(rangeStr) rangeStr = strings.TrimSpace(rangeStr)
numbers = make([]int64, 0) numbers = make([]int64, 0)
// e.g. 1000-2000,2001,2002,3000-4000 // e.g. 1000-2000,2001,2002,3000-4000
numRanges := strings.SplitSeq(rangeStr, ",") numRanges := strings.Split(rangeStr, ",")
for numRangeStr := range numRanges { for _, numRangeStr := range numRanges {
// 1000-2000 or 2001 // 1000-2000 or 2001
numArray := strings.Split(numRangeStr, "-") numArray := strings.Split(numRangeStr, "-")
// length: only 1 or 2 is correct // length: only 1 or 2 is correct
+51 -33
View File
@@ -158,7 +158,10 @@ type Control struct {
} }
func NewControl(ctx context.Context, sessionCtx *SessionContext) (*Control, error) { func NewControl(ctx context.Context, sessionCtx *SessionContext) (*Control, error) {
poolCount := min(sessionCtx.LoginMsg.PoolCount, int(sessionCtx.ServerCfg.Transport.MaxPoolCount)) poolCount := sessionCtx.LoginMsg.PoolCount
if poolCount > int(sessionCtx.ServerCfg.Transport.MaxPoolCount) {
poolCount = int(sessionCtx.ServerCfg.Transport.MaxPoolCount)
}
ctl := &Control{ ctl := &Control{
sessionCtx: sessionCtx, sessionCtx: sessionCtx,
workConnCh: make(chan net.Conn, poolCount+10), workConnCh: make(chan net.Conn, poolCount+10),
@@ -303,30 +306,6 @@ func (ctl *Control) WaitClosed() {
<-ctl.doneCh <-ctl.doneCh
} }
func (ctl *Control) loginUserInfo() plugin.UserInfo {
return plugin.UserInfo{
User: ctl.sessionCtx.LoginMsg.User,
Metas: ctl.sessionCtx.LoginMsg.Metas,
RunID: ctl.sessionCtx.LoginMsg.RunID,
}
}
func (ctl *Control) closeProxy(pxy proxy.Proxy) {
pxy.Close()
ctl.sessionCtx.PxyManager.Del(pxy.GetName())
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConfigurer().GetBaseConfig().Type)
notifyContent := &plugin.CloseProxyContent{
User: ctl.loginUserInfo(),
CloseProxy: msg.CloseProxy{
ProxyName: pxy.GetName(),
},
}
go func() {
_ = ctl.sessionCtx.PluginManager.CloseProxy(notifyContent)
}()
}
func (ctl *Control) worker() { func (ctl *Control) worker() {
xl := ctl.xl xl := ctl.xl
@@ -337,16 +316,31 @@ func (ctl *Control) worker() {
ctl.sessionCtx.Conn.Close() ctl.sessionCtx.Conn.Close()
ctl.mu.Lock() ctl.mu.Lock()
defer ctl.mu.Unlock()
close(ctl.workConnCh) close(ctl.workConnCh)
for workConn := range ctl.workConnCh { for workConn := range ctl.workConnCh {
workConn.Close() workConn.Close()
} }
proxies := ctl.proxies
ctl.proxies = make(map[string]proxy.Proxy)
ctl.mu.Unlock()
for _, pxy := range proxies { for _, pxy := range ctl.proxies {
ctl.closeProxy(pxy) pxy.Close()
ctl.sessionCtx.PxyManager.Del(pxy.GetName())
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConfigurer().GetBaseConfig().Type)
notifyContent := &plugin.CloseProxyContent{
User: plugin.UserInfo{
User: ctl.sessionCtx.LoginMsg.User,
Metas: ctl.sessionCtx.LoginMsg.Metas,
RunID: ctl.sessionCtx.LoginMsg.RunID,
},
CloseProxy: msg.CloseProxy{
ProxyName: pxy.GetName(),
},
}
go func() {
_ = ctl.sessionCtx.PluginManager.CloseProxy(notifyContent)
}()
} }
metrics.Server.CloseClient() metrics.Server.CloseClient()
@@ -369,7 +363,11 @@ func (ctl *Control) handleNewProxy(m msg.Message) {
inMsg := m.(*msg.NewProxy) inMsg := m.(*msg.NewProxy)
content := &plugin.NewProxyContent{ content := &plugin.NewProxyContent{
User: ctl.loginUserInfo(), User: plugin.UserInfo{
User: ctl.sessionCtx.LoginMsg.User,
Metas: ctl.sessionCtx.LoginMsg.Metas,
RunID: ctl.sessionCtx.LoginMsg.RunID,
},
NewProxy: *inMsg, NewProxy: *inMsg,
} }
var remoteAddr string var remoteAddr string
@@ -404,7 +402,11 @@ func (ctl *Control) handlePing(m msg.Message) {
inMsg := m.(*msg.Ping) inMsg := m.(*msg.Ping)
content := &plugin.PingContent{ content := &plugin.PingContent{
User: ctl.loginUserInfo(), User: plugin.UserInfo{
User: ctl.sessionCtx.LoginMsg.User,
Metas: ctl.sessionCtx.LoginMsg.Metas,
RunID: ctl.sessionCtx.LoginMsg.RunID,
},
Ping: *inMsg, Ping: *inMsg,
} }
retContent, err := ctl.sessionCtx.PluginManager.Ping(content) retContent, err := ctl.sessionCtx.PluginManager.Ping(content)
@@ -534,9 +536,25 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
if ctl.sessionCtx.ServerCfg.MaxPortsPerClient > 0 { if ctl.sessionCtx.ServerCfg.MaxPortsPerClient > 0 {
ctl.portsUsedNum -= pxy.GetUsedPortsNum() ctl.portsUsedNum -= pxy.GetUsedPortsNum()
} }
pxy.Close()
ctl.sessionCtx.PxyManager.Del(pxy.GetName())
delete(ctl.proxies, closeMsg.ProxyName) delete(ctl.proxies, closeMsg.ProxyName)
ctl.mu.Unlock() ctl.mu.Unlock()
ctl.closeProxy(pxy) metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConfigurer().GetBaseConfig().Type)
notifyContent := &plugin.CloseProxyContent{
User: plugin.UserInfo{
User: ctl.sessionCtx.LoginMsg.User,
Metas: ctl.sessionCtx.LoginMsg.Metas,
RunID: ctl.sessionCtx.LoginMsg.RunID,
},
CloseProxy: msg.CloseProxy{
ProxyName: pxy.GetName(),
},
}
go func() {
_ = ctl.sessionCtx.PluginManager.CloseProxy(notifyContent)
}()
return return
} }
+1 -1
View File
@@ -31,7 +31,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.HTTPProxyConfig](), NewHTTPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.HTTPProxyConfig{}), NewHTTPProxy)
} }
type HTTPProxy struct { type HTTPProxy struct {
+1 -1
View File
@@ -25,7 +25,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.HTTPSProxyConfig](), NewHTTPSProxy) RegisterProxyFactory(reflect.TypeOf(&v1.HTTPSProxyConfig{}), NewHTTPSProxy)
} }
type HTTPSProxy struct { type HTTPSProxy struct {
+1 -1
View File
@@ -21,7 +21,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.STCPProxyConfig](), NewSTCPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.STCPProxyConfig{}), NewSTCPProxy)
} }
type STCPProxy struct { type STCPProxy struct {
+1 -1
View File
@@ -21,7 +21,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.SUDPProxyConfig](), NewSUDPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.SUDPProxyConfig{}), NewSUDPProxy)
} }
type SUDPProxy struct { type SUDPProxy struct {
+1 -1
View File
@@ -24,7 +24,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.TCPProxyConfig](), NewTCPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.TCPProxyConfig{}), NewTCPProxy)
} }
type TCPProxy struct { type TCPProxy struct {
+1 -1
View File
@@ -26,7 +26,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.TCPMuxProxyConfig](), NewTCPMuxProxy) RegisterProxyFactory(reflect.TypeOf(&v1.TCPMuxProxyConfig{}), NewTCPMuxProxy)
} }
type TCPMuxProxy struct { type TCPMuxProxy struct {
+1 -1
View File
@@ -35,7 +35,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.UDPProxyConfig](), NewUDPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.UDPProxyConfig{}), NewUDPProxy)
} }
type UDPProxy struct { type UDPProxy struct {
+1 -1
View File
@@ -24,7 +24,7 @@ import (
) )
func init() { func init() {
RegisterProxyFactory(reflect.TypeFor[*v1.XTCPProxyConfig](), NewXTCPProxy) RegisterProxyFactory(reflect.TypeOf(&v1.XTCPProxyConfig{}), NewXTCPProxy)
} }
type XTCPProxy struct { type XTCPProxy struct {
+1 -1
View File
@@ -193,7 +193,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("create vhost tcpMuxer error, %v", err) return nil, fmt.Errorf("create vhost tcpMuxer error, %v", err)
} }
log.Infof("tcpmux httpconnect multiplexer listen on %s, passthrough: %v", address, cfg.TCPMuxPassthrough) log.Infof("tcpmux httpconnect multiplexer listen on %s, passthough: %v", address, cfg.TCPMuxPassthrough)
} }
// Init all plugins // Init all plugins
+3 -2
View File
@@ -2,7 +2,6 @@ package framework
import ( import (
"fmt" "fmt"
"maps"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
@@ -21,7 +20,9 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
ExpectNoError(err) ExpectNoError(err)
ExpectTrue(len(templates) > 0) ExpectTrue(len(templates) > 0)
maps.Copy(f.usedPorts, ports) for name, port := range ports {
f.usedPorts[name] = port
}
currentServerProcesses := make([]*process.Process, 0, len(serverTemplates)) currentServerProcesses := make([]*process.Process, 0, len(serverTemplates))
for i := range serverTemplates { for i := range serverTemplates {
+21 -28
View File
@@ -26,8 +26,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
proxyType := t proxyType := t
ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() { ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() {
serverConf := consts.LegacyDefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
var clientConf strings.Builder clientConf := consts.LegacyDefaultClientConfig
clientConf.WriteString(consts.LegacyDefaultClientConfig)
localPortName := "" localPortName := ""
protocol := "tcp" protocol := "tcp"
@@ -79,10 +78,10 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientConf.WriteString(getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests { for _, test := range tests {
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
@@ -103,8 +102,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
vhost_http_port = %d vhost_http_port = %d
`, vhostHTTPPort) `, vhostHTTPPort)
var clientConf strings.Builder clientConf := consts.LegacyDefaultClientConfig
clientConf.WriteString(consts.LegacyDefaultClientConfig)
getProxyConf := func(proxyName string, customDomains string, extra string) string { getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
@@ -149,13 +147,13 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
if tests[i].customDomains == "" { if tests[i].customDomains == "" {
tests[i].customDomains = test.proxyName + ".example.com" tests[i].customDomains = test.proxyName + ".example.com"
} }
clientConf.WriteString(getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests { for _, test := range tests {
for domain := range strings.SplitSeq(test.customDomains, ",") { for _, domain := range strings.Split(test.customDomains, ",") {
domain = strings.TrimSpace(domain) domain = strings.TrimSpace(domain)
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
Explain(test.proxyName + "-" + domain). Explain(test.proxyName + "-" + domain).
@@ -187,8 +185,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
`, vhostHTTPSPort) `, vhostHTTPSPort)
localPort := f.AllocPort() localPort := f.AllocPort()
var clientConf strings.Builder clientConf := consts.LegacyDefaultClientConfig
clientConf.WriteString(consts.LegacyDefaultClientConfig)
getProxyConf := func(proxyName string, customDomains string, extra string) string { getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
[%s] [%s]
@@ -232,10 +229,10 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
if tests[i].customDomains == "" { if tests[i].customDomains == "" {
tests[i].customDomains = test.proxyName + ".example.com" tests[i].customDomains = test.proxyName + ".example.com"
} }
clientConf.WriteString(getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
tlsConfig, err := transport.NewServerTLSConfig("", "", "") tlsConfig, err := transport.NewServerTLSConfig("", "", "")
framework.ExpectNoError(err) framework.ExpectNoError(err)
@@ -247,7 +244,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
f.RunServer("", localServer) f.RunServer("", localServer)
for _, test := range tests { for _, test := range tests {
for domain := range strings.SplitSeq(test.customDomains, ",") { for _, domain := range strings.Split(test.customDomains, ",") {
domain = strings.TrimSpace(domain) domain = strings.TrimSpace(domain)
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
Explain(test.proxyName + "-" + domain). Explain(test.proxyName + "-" + domain).
@@ -285,12 +282,9 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
proxyType := t proxyType := t
ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() { ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() {
serverConf := consts.LegacyDefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
var clientServerConf strings.Builder clientServerConf := consts.LegacyDefaultClientConfig + "\nuser = user1"
clientServerConf.WriteString(consts.LegacyDefaultClientConfig + "\nuser = user1") clientVisitorConf := consts.LegacyDefaultClientConfig + "\nuser = user1"
var clientVisitorConf strings.Builder clientUser2VisitorConf := consts.LegacyDefaultClientConfig + "\nuser = user2"
clientVisitorConf.WriteString(consts.LegacyDefaultClientConfig + "\nuser = user1")
var clientUser2VisitorConf strings.Builder
clientUser2VisitorConf.WriteString(consts.LegacyDefaultClientConfig + "\nuser = user2")
localPortName := "" localPortName := ""
protocol := "tcp" protocol := "tcp"
@@ -406,20 +400,20 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientServerConf.WriteString(getProxyServerConf(test.proxyName, test.commonExtraConfig+"\n"+test.proxyExtraConfig) + "\n") clientServerConf += getProxyServerConf(test.proxyName, test.commonExtraConfig+"\n"+test.proxyExtraConfig) + "\n"
} }
for _, test := range tests { for _, test := range tests {
config := getProxyVisitorConf( config := getProxyVisitorConf(
test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig, test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig,
) + "\n" ) + "\n"
if test.deployUser2Client { if test.deployUser2Client {
clientUser2VisitorConf.WriteString(config) clientUser2VisitorConf += config
} else { } else {
clientVisitorConf.WriteString(config) clientVisitorConf += config
} }
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientServerConf.String(), clientVisitorConf.String(), clientUser2VisitorConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientServerConf, clientVisitorConf, clientUser2VisitorConf})
for _, test := range tests { for _, test := range tests {
timeout := time.Second timeout := time.Second
@@ -446,8 +440,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
ginkgo.Describe("TCPMUX", func() { ginkgo.Describe("TCPMUX", func() {
ginkgo.It("Type tcpmux", func() { ginkgo.It("Type tcpmux", func() {
serverConf := consts.LegacyDefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
var clientConf strings.Builder clientConf := consts.LegacyDefaultClientConfig
clientConf.WriteString(consts.LegacyDefaultClientConfig)
tcpmuxHTTPConnectPortName := port.GenName("TCPMUX") tcpmuxHTTPConnectPortName := port.GenName("TCPMUX")
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
@@ -490,14 +483,14 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientConf.WriteString(getProxyConf(test.proxyName, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, test.extraConfig) + "\n"
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName))) localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName)))
f.RunServer(port.GenName(test.proxyName), localServer) f.RunServer(port.GenName(test.proxyName), localServer)
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
// Request without HTTP connect should get error // Request without HTTP connect should get error
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
+1 -1
View File
@@ -28,7 +28,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000)) tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000))
udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000)) udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000))
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp-allowed-in-range] [tcp-allowded-in-range]
type = tcp type = tcp
local_port = {{ .%s }} local_port = {{ .%s }}
remote_port = {{ .%s }} remote_port = {{ .%s }}
+9 -7
View File
@@ -48,10 +48,12 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
return true return true
}) })
} }
for range 10 { for i := 0; i < 10; i++ {
wait.Go(func() { wait.Add(1)
go func() {
defer wait.Done()
expectFn() expectFn()
}) }()
} }
wait.Wait() wait.Wait()
@@ -92,7 +94,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
fooCount := 0 fooCount := 0
barCount := 0 barCount := 0
for i := range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Explain("times " + strconv.Itoa(i)).Port(remotePort).Ensure(func(resp *request.Response) bool { framework.NewRequestExpect(f).Explain("times " + strconv.Itoa(i)).Port(remotePort).Ensure(func(resp *request.Response) bool {
switch string(resp.Content) { switch string(resp.Content) {
case "foo": case "foo":
@@ -148,7 +150,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
// check foo and bar is ok // check foo and bar is ok
results := []string{} results := []string{}
for range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool { framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
results = append(results, string(resp.Content)) results = append(results, string(resp.Content))
return true return true
@@ -159,7 +161,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
// close bar server, check foo is ok // close bar server, check foo is ok
barServer.Close() barServer.Close()
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
for range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure() framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure()
} }
@@ -167,7 +169,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
f.RunServer("", barServer) f.RunServer("", barServer)
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
results = []string{} results = []string{}
for range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool { framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
results = append(results, string(resp.Content)) results = append(results, string(resp.Content))
return true return true
+3 -5
View File
@@ -4,7 +4,6 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"strconv" "strconv"
"strings"
"github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2"
@@ -23,8 +22,7 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
ginkgo.Describe("UnixDomainSocket", func() { ginkgo.Describe("UnixDomainSocket", func() {
ginkgo.It("Expose a unix domain socket echo server", func() { ginkgo.It("Expose a unix domain socket echo server", func() {
serverConf := consts.LegacyDefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
var clientConf strings.Builder clientConf := consts.LegacyDefaultClientConfig
clientConf.WriteString(consts.LegacyDefaultClientConfig)
getProxyConf := func(proxyName string, portName string, extra string) string { getProxyConf := func(proxyName string, portName string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
@@ -67,10 +65,10 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientConf.WriteString(getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests { for _, test := range tests {
framework.NewRequestExpect(f).Port(f.PortByName(test.portName)).Ensure() framework.NewRequestExpect(f).Port(f.PortByName(test.portName)).Ensure()
+1 -1
View File
@@ -52,7 +52,7 @@ func (pa *Allocator) GetByName(portName string) int {
pa.mu.Lock() pa.mu.Lock()
defer pa.mu.Unlock() defer pa.mu.Unlock()
for range 20 { for i := 0; i < 20; i++ {
port := pa.getByRange(builder.rangePortFrom, builder.rangePortTo) port := pa.getByRange(builder.rangePortFrom, builder.rangePortTo)
if port == 0 { if port == 0 {
return 0 return 0
+2 -2
View File
@@ -75,11 +75,11 @@ func (c *TunnelClient) serveListener() {
if err != nil { if err != nil {
return return
} }
go c.handleConn(conn) go c.hanldeConn(conn)
} }
} }
func (c *TunnelClient) handleConn(conn net.Conn) { func (c *TunnelClient) hanldeConn(conn net.Conn) {
defer conn.Close() defer conn.Close()
local, err := net.Dial("tcp", c.localAddr) local, err := net.Dial("tcp", c.localAddr)
if err != nil { if err != nil {
+21 -28
View File
@@ -26,8 +26,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
proxyType := t proxyType := t
ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() { ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() {
serverConf := consts.DefaultServerConfig serverConf := consts.DefaultServerConfig
var clientConf strings.Builder clientConf := consts.DefaultClientConfig
clientConf.WriteString(consts.DefaultClientConfig)
localPortName := "" localPortName := ""
protocol := "tcp" protocol := "tcp"
@@ -80,10 +79,10 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientConf.WriteString(getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests { for _, test := range tests {
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
@@ -104,8 +103,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
vhostHTTPPort = %d vhostHTTPPort = %d
`, vhostHTTPPort) `, vhostHTTPPort)
var clientConf strings.Builder clientConf := consts.DefaultClientConfig
clientConf.WriteString(consts.DefaultClientConfig)
getProxyConf := func(proxyName string, customDomains string, extra string) string { getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
@@ -151,13 +149,13 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
if tests[i].customDomains == "" { if tests[i].customDomains == "" {
tests[i].customDomains = fmt.Sprintf(`["%s"]`, test.proxyName+".example.com") tests[i].customDomains = fmt.Sprintf(`["%s"]`, test.proxyName+".example.com")
} }
clientConf.WriteString(getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests { for _, test := range tests {
for domain := range strings.SplitSeq(test.customDomains, ",") { for _, domain := range strings.Split(test.customDomains, ",") {
domain = strings.TrimSpace(domain) domain = strings.TrimSpace(domain)
domain = strings.TrimLeft(domain, "[\"") domain = strings.TrimLeft(domain, "[\"")
domain = strings.TrimRight(domain, "]\"") domain = strings.TrimRight(domain, "]\"")
@@ -191,8 +189,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
`, vhostHTTPSPort) `, vhostHTTPSPort)
localPort := f.AllocPort() localPort := f.AllocPort()
var clientConf strings.Builder clientConf := consts.DefaultClientConfig
clientConf.WriteString(consts.DefaultClientConfig)
getProxyConf := func(proxyName string, customDomains string, extra string) string { getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
[[proxies]] [[proxies]]
@@ -237,10 +234,10 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
if tests[i].customDomains == "" { if tests[i].customDomains == "" {
tests[i].customDomains = fmt.Sprintf(`["%s"]`, test.proxyName+".example.com") tests[i].customDomains = fmt.Sprintf(`["%s"]`, test.proxyName+".example.com")
} }
clientConf.WriteString(getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
tlsConfig, err := transport.NewServerTLSConfig("", "", "") tlsConfig, err := transport.NewServerTLSConfig("", "", "")
framework.ExpectNoError(err) framework.ExpectNoError(err)
@@ -252,7 +249,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
f.RunServer("", localServer) f.RunServer("", localServer)
for _, test := range tests { for _, test := range tests {
for domain := range strings.SplitSeq(test.customDomains, ",") { for _, domain := range strings.Split(test.customDomains, ",") {
domain = strings.TrimSpace(domain) domain = strings.TrimSpace(domain)
domain = strings.TrimLeft(domain, "[\"") domain = strings.TrimLeft(domain, "[\"")
domain = strings.TrimRight(domain, "]\"") domain = strings.TrimRight(domain, "]\"")
@@ -292,12 +289,9 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
proxyType := t proxyType := t
ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() { ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() {
serverConf := consts.DefaultServerConfig serverConf := consts.DefaultServerConfig
var clientServerConf strings.Builder clientServerConf := consts.DefaultClientConfig + "\nuser = \"user1\""
clientServerConf.WriteString(consts.DefaultClientConfig + "\nuser = \"user1\"") clientVisitorConf := consts.DefaultClientConfig + "\nuser = \"user1\""
var clientVisitorConf strings.Builder clientUser2VisitorConf := consts.DefaultClientConfig + "\nuser = \"user2\""
clientVisitorConf.WriteString(consts.DefaultClientConfig + "\nuser = \"user1\"")
var clientUser2VisitorConf strings.Builder
clientUser2VisitorConf.WriteString(consts.DefaultClientConfig + "\nuser = \"user2\"")
localPortName := "" localPortName := ""
protocol := "tcp" protocol := "tcp"
@@ -413,20 +407,20 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientServerConf.WriteString(getProxyServerConf(test.proxyName, test.commonExtraConfig+"\n"+test.proxyExtraConfig) + "\n") clientServerConf += getProxyServerConf(test.proxyName, test.commonExtraConfig+"\n"+test.proxyExtraConfig) + "\n"
} }
for _, test := range tests { for _, test := range tests {
config := getProxyVisitorConf( config := getProxyVisitorConf(
test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig, test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig,
) + "\n" ) + "\n"
if test.deployUser2Client { if test.deployUser2Client {
clientUser2VisitorConf.WriteString(config) clientUser2VisitorConf += config
} else { } else {
clientVisitorConf.WriteString(config) clientVisitorConf += config
} }
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientServerConf.String(), clientVisitorConf.String(), clientUser2VisitorConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientServerConf, clientVisitorConf, clientUser2VisitorConf})
for _, test := range tests { for _, test := range tests {
timeout := time.Second timeout := time.Second
@@ -453,8 +447,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
ginkgo.Describe("TCPMUX", func() { ginkgo.Describe("TCPMUX", func() {
ginkgo.It("Type tcpmux", func() { ginkgo.It("Type tcpmux", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.DefaultServerConfig
var clientConf strings.Builder clientConf := consts.DefaultClientConfig
clientConf.WriteString(consts.DefaultClientConfig)
tcpmuxHTTPConnectPortName := port.GenName("TCPMUX") tcpmuxHTTPConnectPortName := port.GenName("TCPMUX")
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
@@ -498,14 +491,14 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientConf.WriteString(getProxyConf(test.proxyName, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, test.extraConfig) + "\n"
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName))) localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName)))
f.RunServer(port.GenName(test.proxyName), localServer) f.RunServer(port.GenName(test.proxyName), localServer)
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
// Request without HTTP connect should get error // Request without HTTP connect should get error
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
+1 -1
View File
@@ -33,7 +33,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000)) udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000))
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[[proxies]] [[proxies]]
name = "tcp-allowed-in-range" name = "tcp-allowded-in-range"
type = "tcp" type = "tcp"
localPort = {{ .%s }} localPort = {{ .%s }}
remotePort = {{ .%s }} remotePort = {{ .%s }}
+10 -8
View File
@@ -50,10 +50,12 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
return true return true
}) })
} }
for range 10 { for i := 0; i < 10; i++ {
wait.Go(func() { wait.Add(1)
go func() {
defer wait.Done()
expectFn() expectFn()
}) }()
} }
wait.Wait() wait.Wait()
@@ -96,7 +98,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
fooCount := 0 fooCount := 0
barCount := 0 barCount := 0
for i := range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Explain("times " + strconv.Itoa(i)).Port(remotePort).Ensure(func(resp *request.Response) bool { framework.NewRequestExpect(f).Explain("times " + strconv.Itoa(i)).Port(remotePort).Ensure(func(resp *request.Response) bool {
switch string(resp.Content) { switch string(resp.Content) {
case "foo": case "foo":
@@ -161,7 +163,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
fooCount := 0 fooCount := 0
barCount := 0 barCount := 0
for i := range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f). framework.NewRequestExpect(f).
Explain("times " + strconv.Itoa(i)). Explain("times " + strconv.Itoa(i)).
Port(vhostHTTPSPort). Port(vhostHTTPSPort).
@@ -228,7 +230,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
// check foo and bar is ok // check foo and bar is ok
results := []string{} results := []string{}
for range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool { framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
results = append(results, string(resp.Content)) results = append(results, string(resp.Content))
return true return true
@@ -239,7 +241,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
// close bar server, check foo is ok // close bar server, check foo is ok
barServer.Close() barServer.Close()
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
for range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure() framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure()
} }
@@ -247,7 +249,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
f.RunServer("", barServer) f.RunServer("", barServer)
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
results = []string{} results = []string{}
for range 10 { for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool { framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
results = append(results, string(resp.Content)) results = append(results, string(resp.Content))
return true return true
+3 -5
View File
@@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2"
@@ -24,8 +23,7 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
ginkgo.Describe("UnixDomainSocket", func() { ginkgo.Describe("UnixDomainSocket", func() {
ginkgo.It("Expose a unix domain socket echo server", func() { ginkgo.It("Expose a unix domain socket echo server", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.DefaultServerConfig
var clientConf strings.Builder clientConf := consts.DefaultClientConfig
clientConf.WriteString(consts.DefaultClientConfig)
getProxyConf := func(proxyName string, portName string, extra string) string { getProxyConf := func(proxyName string, portName string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
@@ -71,10 +69,10 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
// build all client config // build all client config
for _, test := range tests { for _, test := range tests {
clientConf.WriteString(getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n") clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
} }
// run frps and frpc // run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf.String()}) f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests { for _, test := range tests {
framework.NewRequestExpect(f).Port(f.PortByName(test.portName)).Ensure() framework.NewRequestExpect(f).Port(f.PortByName(test.portName)).Ensure()