1
0
mirror of https://github.com/fatedier/frp.git synced 2025-05-06 22:38:26 +00:00

Compare commits

..

No commits in common. "31b44c1feb382f58a4f509d083215364a7bdb5a6" and "2a7aa69890690229f9c0e67e1d360ee094a56c84" have entirely different histories.

35 changed files with 106 additions and 159 deletions

@ -2,7 +2,7 @@ version: 2
jobs: jobs:
go-version-latest: go-version-latest:
docker: docker:
- image: cimg/go:1.23-node - image: cimg/go:1.22-node
resource_class: large resource_class: large
steps: steps:
- checkout - checkout

@ -21,14 +21,14 @@ jobs:
steps: steps:
- uses: actions/stale@v9 - uses: actions/stale@v9
with: with:
stale-issue-message: 'Issues go stale after 14d of inactivity. Stale issues rot after an additional 3d of inactivity and eventually close.' stale-issue-message: 'Issues go stale after 21d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
stale-pr-message: "PRs go stale after 14d of inactivity. Stale PRs rot after an additional 3d of inactivity and eventually close." stale-pr-message: "PRs go stale after 21d of inactivity. Stale PRs rot after an additional 7d of inactivity and eventually close."
stale-issue-label: 'lifecycle/stale' stale-issue-label: 'lifecycle/stale'
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
stale-pr-label: 'lifecycle/stale' stale-pr-label: 'lifecycle/stale'
exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
days-before-stale: 14 days-before-stale: 21
days-before-close: 3 days-before-close: 7
debug-only: ${{ github.event.inputs.debug-only }} debug-only: ${{ github.event.inputs.debug-only }}
exempt-all-pr-milestones: true exempt-all-pr-milestones: true
exempt-all-pr-assignees: true exempt-all-pr-assignees: true

@ -48,8 +48,7 @@ linters-settings:
check-blank: false check-blank: false
govet: govet:
# report about shadowed variables # report about shadowed variables
disable: check-shadowing: false
- shadow
maligned: maligned:
# print struct with more effective memory layout or not, false by default # print struct with more effective memory layout or not, false by default
suggest-new: true suggest-new: true

@ -97,7 +97,7 @@ frp also offers a P2P connect mode.
* [Client Plugins](#client-plugins) * [Client Plugins](#client-plugins)
* [Server Manage Plugins](#server-manage-plugins) * [Server Manage Plugins](#server-manage-plugins)
* [SSH Tunnel Gateway](#ssh-tunnel-gateway) * [SSH Tunnel Gateway](#ssh-tunnel-gateway)
* [Related Projects](#related-projects) * [Releated Projects](#releated-projects)
* [Contributing](#contributing) * [Contributing](#contributing)
* [Donation](#donation) * [Donation](#donation)
* [GitHub Sponsors](#github-sponsors) * [GitHub Sponsors](#github-sponsors)
@ -1260,7 +1260,7 @@ frpc tcp --proxy_name "test-tcp" --local_ip 127.0.0.1 --local_port 8080 --remote
Please refer to this [document](/doc/ssh_tunnel_gateway.md) for more information. Please refer to this [document](/doc/ssh_tunnel_gateway.md) for more information.
## Related Projects ## Releated Projects
* [gofrp/plugin](https://github.com/gofrp/plugin) - A repository for frp plugins that contains a variety of plugins implemented based on the frp extension mechanism, meeting the customization needs of different scenarios. * [gofrp/plugin](https://github.com/gofrp/plugin) - A repository for frp plugins that contains a variety of plugins implemented based on the frp extension mechanism, meeting the customization needs of different scenarios.
* [gofrp/tiny-frpc](https://github.com/gofrp/tiny-frpc) - A lightweight version of the frp client (around 3.5MB at minimum) implemented using the ssh protocol, supporting some of the most commonly used features, suitable for devices with limited resources. * [gofrp/tiny-frpc](https://github.com/gofrp/tiny-frpc) - A lightweight version of the frp client (around 3.5MB at minimum) implemented using the ssh protocol, supporting some of the most commonly used features, suitable for devices with limited resources.

@ -1,7 +1,5 @@
### Features ### Features
* Support metadatas and annotations in frpc proxy commands. * `tzdata` is installed by default in the container image, and the time zone can be set using the `TZ` environment variable.
* The `quic-bind-port` command line parameter is supported in frps, which specifies the port for accepting frpc connections using the QUIC protocol.
### Fixes * The vhost HTTP proxy of frps supports the h2c protocol.
* Properly release resources in service.Close() to prevent resource leaks when used as a library.

@ -8,7 +8,7 @@ import (
var ErrPayloadType = errors.New("error payload type") var ErrPayloadType = errors.New("error payload type")
type Handler func(payload any) error type Handler func(payload interface{}) error
type StartProxyPayload struct { type StartProxyPayload struct {
NewProxyMsg *msg.NewProxy NewProxyMsg *msg.NewProxy

@ -96,7 +96,7 @@ func (pm *Manager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWo
} }
} }
func (pm *Manager) HandleEvent(payload any) error { func (pm *Manager) HandleEvent(payload interface{}) error {
var m msg.Message var m msg.Message
switch e := payload.(type) { switch e := payload.(type) {
case *event.StartProxyPayload: case *event.StartProxyPayload:

4
go.mod

@ -1,11 +1,11 @@
module github.com/fatedier/frp module github.com/fatedier/frp
go 1.23.0 go 1.22.0
require ( require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/coreos/go-oidc/v3 v3.10.0 github.com/coreos/go-oidc/v3 v3.10.0
github.com/fatedier/golib v0.5.1 github.com/fatedier/golib v0.5.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0

4
go.sum

@ -21,8 +21,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatedier/golib v0.5.1 h1:hcKAnaw5mdI/1KWRGejxR+i1Hn/NvbY5UsMKDr7o13M= github.com/fatedier/golib v0.5.0 h1:hNcH7hgfIFqVWbP+YojCCAj4eO94pPf4dEF8lmq2jWs=
github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ= github.com/fatedier/golib v0.5.0/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo= github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=

@ -106,8 +106,6 @@ func registerProxyBaseConfigFlags(cmd *cobra.Command, c *v1.ProxyBaseConfig, opt
} }
cmd.Flags().StringVarP(&c.Name, "proxy_name", "n", "", "proxy name") cmd.Flags().StringVarP(&c.Name, "proxy_name", "n", "", "proxy name")
cmd.Flags().StringToStringVarP(&c.Metadatas, "metadatas", "", nil, "metadata key-value pairs (e.g., key1=value1,key2=value2)")
cmd.Flags().StringToStringVarP(&c.Annotations, "annotations", "", nil, "annotation key-value pairs (e.g., key1=value1,key2=value2)")
if !options.sshMode { if !options.sshMode {
cmd.Flags().StringVarP(&c.LocalIP, "local_ip", "i", "127.0.0.1", "local ip") cmd.Flags().StringVarP(&c.LocalIP, "local_ip", "i", "127.0.0.1", "local ip")

@ -170,7 +170,7 @@ type ClientCommonConf struct {
} }
// Supported sources including: string(file path), []byte, Reader interface. // Supported sources including: string(file path), []byte, Reader interface.
func UnmarshalClientConfFromIni(source any) (ClientCommonConf, error) { func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: false, Insensitive: false,
InsensitiveSections: false, InsensitiveSections: false,
@ -203,7 +203,7 @@ func UnmarshalClientConfFromIni(source any) (ClientCommonConf, error) {
// otherwise just start proxies in startProxy map // otherwise just start proxies in startProxy map
func LoadAllProxyConfsFromIni( func LoadAllProxyConfsFromIni(
prefix string, prefix string,
source any, source interface{},
start []string, start []string,
) (map[string]ProxyConf, map[string]VisitorConf, error) { ) (map[string]ProxyConf, map[string]VisitorConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{

@ -217,7 +217,7 @@ func GetDefaultServerConf() ServerCommonConf {
} }
} }
func UnmarshalServerConfFromIni(source any) (ServerCommonConf, error) { func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: false, Insensitive: false,
InsensitiveSections: false, InsensitiveSections: false,

@ -18,10 +18,10 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"text/template"
toml "github.com/pelletier/go-toml/v2" toml "github.com/pelletier/go-toml/v2"
"github.com/samber/lo" "github.com/samber/lo"
@ -118,7 +118,7 @@ func LoadConfigure(b []byte, c any, strict bool) error {
defer v1.DisallowUnknownFieldsMu.Unlock() defer v1.DisallowUnknownFieldsMu.Unlock()
v1.DisallowUnknownFields = strict v1.DisallowUnknownFields = strict
var tomlObj any var tomlObj interface{}
// Try to unmarshal as TOML first; swallow errors from that (assume it's not valid TOML). // Try to unmarshal as TOML first; swallow errors from that (assume it's not valid TOML).
if err := toml.Unmarshal(b, &tomlObj); err == nil { if err := toml.Unmarshal(b, &tomlObj); err == nil {
b, err = json.Marshal(&tomlObj) b, err = json.Marshal(&tomlObj)

@ -112,29 +112,6 @@ func TestLoadServerConfigStrictMode(t *testing.T) {
} }
} }
func TestRenderWithTemplate(t *testing.T) {
tests := []struct {
name string
content string
want string
}{
{"toml", tomlServerContent, tomlServerContent},
{"yaml", yamlServerContent, yamlServerContent},
{"json", jsonServerContent, jsonServerContent},
{"template numeric", `key = {{ 123 }}`, "key = 123"},
{"template string", `key = {{ "xyz" }}`, "key = xyz"},
{"template quote", `key = {{ printf "%q" "with space" }}`, `key = "with space"`},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
got, err := RenderWithTemplate([]byte(test.content), nil)
require.NoError(err)
require.EqualValues(test.want, string(got))
})
}
}
func TestCustomStructStrictMode(t *testing.T) { func TestCustomStructStrictMode(t *testing.T) {
require := require.New(t) require := require.New(t)

@ -39,6 +39,6 @@ func ReadMsgInto(c io.Reader, msg Message) (err error) {
return msgCtl.ReadMsgInto(c, msg) return msgCtl.ReadMsgInto(c, msg)
} }
func WriteMsg(c io.Writer, msg any) (err error) { func WriteMsg(c io.Writer, msg interface{}) (err error) {
return msgCtl.WriteMsg(c, msg) return msgCtl.WriteMsg(c, msg)
} }

@ -40,7 +40,7 @@ const (
TypeNatHoleReport = '6' TypeNatHoleReport = '6'
) )
var msgTypeMap = map[byte]any{ var msgTypeMap = map[byte]interface{}{
TypeLogin: Login{}, TypeLogin: Login{},
TypeLoginResp: LoginResp{}, TypeLoginResp: LoginResp{},
TypeNewProxy: NewProxy{}, TypeNewProxy: NewProxy{},

@ -72,7 +72,7 @@ func (p *httpPlugin) IsSupport(op string) bool {
return false return false
} }
func (p *httpPlugin) Handle(ctx context.Context, op string, content any) (*Response, any, error) { func (p *httpPlugin) Handle(ctx context.Context, op string, content interface{}) (*Response, interface{}, error) {
r := &Request{ r := &Request{
Version: APIVersion, Version: APIVersion,
Op: op, Op: op,

@ -75,7 +75,7 @@ func (m *Manager) Login(content *LoginContent) (*LoginContent, error) {
Reject: false, Reject: false,
Unchange: true, Unchange: true,
} }
retContent any retContent interface{}
err error err error
) )
reqid, _ := util.RandID() reqid, _ := util.RandID()
@ -109,7 +109,7 @@ func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) {
Reject: false, Reject: false,
Unchange: true, Unchange: true,
} }
retContent any retContent interface{}
err error err error
) )
reqid, _ := util.RandID() reqid, _ := util.RandID()
@ -168,7 +168,7 @@ func (m *Manager) Ping(content *PingContent) (*PingContent, error) {
Reject: false, Reject: false,
Unchange: true, Unchange: true,
} }
retContent any retContent interface{}
err error err error
) )
reqid, _ := util.RandID() reqid, _ := util.RandID()
@ -202,7 +202,7 @@ func (m *Manager) NewWorkConn(content *NewWorkConnContent) (*NewWorkConnContent,
Reject: false, Reject: false,
Unchange: true, Unchange: true,
} }
retContent any retContent interface{}
err error err error
) )
reqid, _ := util.RandID() reqid, _ := util.RandID()
@ -236,7 +236,7 @@ func (m *Manager) NewUserConn(content *NewUserConnContent) (*NewUserConnContent,
Reject: false, Reject: false,
Unchange: true, Unchange: true,
} }
retContent any retContent interface{}
err error err error
) )
reqid, _ := util.RandID() reqid, _ := util.RandID()

@ -32,5 +32,5 @@ const (
type Plugin interface { type Plugin interface {
Name() string Name() string
IsSupport(op string) bool IsSupport(op string) bool
Handle(ctx context.Context, op string, content any) (res *Response, retContent any, err error) Handle(ctx context.Context, op string, content interface{}) (res *Response, retContent interface{}, err error)
} }

@ -19,16 +19,16 @@ import (
) )
type Request struct { type Request struct {
Version string `json:"version"` Version string `json:"version"`
Op string `json:"op"` Op string `json:"op"`
Content any `json:"content"` Content interface{} `json:"content"`
} }
type Response struct { type Response struct {
Reject bool `json:"reject"` Reject bool `json:"reject"`
RejectReason string `json:"reject_reason"` RejectReason string `json:"reject_reason"`
Unchange bool `json:"unchange"` Unchange bool `json:"unchange"`
Content any `json:"content"` Content interface{} `json:"content"`
} }
type LoginContent struct { type LoginContent struct {

@ -112,10 +112,6 @@ func (g *Gateway) Run() {
} }
} }
func (g *Gateway) Close() error {
return g.ln.Close()
}
func (g *Gateway) handleConn(conn net.Conn) { func (g *Gateway) handleConn(conn net.Conn) {
defer conn.Close() defer conn.Close()

@ -67,27 +67,27 @@ func InitLogger(logPath string, levelStr string, maxDays int, disableLogColor bo
Logger = Logger.WithOptions(options...) Logger = Logger.WithOptions(options...)
} }
func Errorf(format string, v ...any) { func Errorf(format string, v ...interface{}) {
Logger.Errorf(format, v...) Logger.Errorf(format, v...)
} }
func Warnf(format string, v ...any) { func Warnf(format string, v ...interface{}) {
Logger.Warnf(format, v...) Logger.Warnf(format, v...)
} }
func Infof(format string, v ...any) { func Infof(format string, v ...interface{}) {
Logger.Infof(format, v...) Logger.Infof(format, v...)
} }
func Debugf(format string, v ...any) { func Debugf(format string, v ...interface{}) {
Logger.Debugf(format, v...) Logger.Debugf(format, v...)
} }
func Tracef(format string, v ...any) { func Tracef(format string, v ...interface{}) {
Logger.Tracef(format, v...) Logger.Tracef(format, v...)
} }
func Logf(level log.Level, offset int, format string, v ...any) { func Logf(level log.Level, offset int, format string, v ...interface{}) {
Logger.Logf(level, offset, format, v...) Logger.Logf(level, offset, format, v...)
} }

@ -14,7 +14,7 @@
package version package version
var version = "0.61.2" var version = "0.61.1"
func Full() string { func Full() string {
return version return version

@ -24,7 +24,7 @@ type Router struct {
httpUser string httpUser string
// store any object here // store any object here
payload any payload interface{}
} }
func NewRouters() *Routers { func NewRouters() *Routers {
@ -33,7 +33,7 @@ func NewRouters() *Routers {
} }
} }
func (r *Routers) Add(domain, location, httpUser string, payload any) error { func (r *Routers) Add(domain, location, httpUser string, payload interface{}) error {
domain = strings.ToLower(domain) domain = strings.ToLower(domain)
r.mutex.Lock() r.mutex.Lock()

@ -100,10 +100,6 @@ func (v *Muxer) SetRewriteHostFunc(f hostRewriteFunc) *Muxer {
return v return v
} }
func (v *Muxer) Close() error {
return v.listener.Close()
}
type ChooseEndpointFunc func() (string, error) type ChooseEndpointFunc func() (string, error)
type CreateConnFunc func(remoteAddr string) (net.Conn, error) type CreateConnFunc func(remoteAddr string) (net.Conn, error)

@ -94,22 +94,22 @@ func (l *Logger) Spawn() *Logger {
return nl return nl
} }
func (l *Logger) Errorf(format string, v ...any) { func (l *Logger) Errorf(format string, v ...interface{}) {
log.Logger.Errorf(l.prefixString+format, v...) log.Logger.Errorf(l.prefixString+format, v...)
} }
func (l *Logger) Warnf(format string, v ...any) { func (l *Logger) Warnf(format string, v ...interface{}) {
log.Logger.Warnf(l.prefixString+format, v...) log.Logger.Warnf(l.prefixString+format, v...)
} }
func (l *Logger) Infof(format string, v ...any) { func (l *Logger) Infof(format string, v ...interface{}) {
log.Logger.Infof(l.prefixString+format, v...) log.Logger.Infof(l.prefixString+format, v...)
} }
func (l *Logger) Debugf(format string, v ...any) { func (l *Logger) Debugf(format string, v ...interface{}) {
log.Logger.Debugf(l.prefixString+format, v...) log.Logger.Debugf(l.prefixString+format, v...)
} }
func (l *Logger) Tracef(format string, v ...any) { func (l *Logger) Tracef(format string, v ...interface{}) {
log.Logger.Tracef(l.prefixString+format, v...) log.Logger.Tracef(l.prefixString+format, v...)
} }

@ -59,13 +59,3 @@ type ResourceController struct {
// All server manager plugin // All server manager plugin
PluginManager *plugin.Manager PluginManager *plugin.Manager
} }
func (rc *ResourceController) Close() error {
if rc.VhostHTTPSMuxer != nil {
rc.VhostHTTPSMuxer.Close()
}
if rc.TCPMuxHTTPConnectMuxer != nil {
rc.TCPMuxHTTPConnectMuxer.Close()
}
return nil
}

@ -196,15 +196,15 @@ func getConfByType(proxyType string) any {
// Get proxy info. // Get proxy info.
type ProxyStatsInfo struct { type ProxyStatsInfo struct {
Name string `json:"name"` Name string `json:"name"`
Conf any `json:"conf"` Conf interface{} `json:"conf"`
ClientVersion string `json:"clientVersion,omitempty"` ClientVersion string `json:"clientVersion,omitempty"`
TodayTrafficIn int64 `json:"todayTrafficIn"` TodayTrafficIn int64 `json:"todayTrafficIn"`
TodayTrafficOut int64 `json:"todayTrafficOut"` TodayTrafficOut int64 `json:"todayTrafficOut"`
CurConns int64 `json:"curConns"` CurConns int64 `json:"curConns"`
LastStartTime string `json:"lastStartTime"` LastStartTime string `json:"lastStartTime"`
LastCloseTime string `json:"lastCloseTime"` LastCloseTime string `json:"lastCloseTime"`
Status string `json:"status"` Status string `json:"status"`
} }
type GetProxyInfoResp struct { type GetProxyInfoResp struct {
@ -272,14 +272,14 @@ func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxySt
// Get proxy info by name. // Get proxy info by name.
type GetProxyStatsResp struct { type GetProxyStatsResp struct {
Name string `json:"name"` Name string `json:"name"`
Conf any `json:"conf"` Conf interface{} `json:"conf"`
TodayTrafficIn int64 `json:"todayTrafficIn"` TodayTrafficIn int64 `json:"todayTrafficIn"`
TodayTrafficOut int64 `json:"todayTrafficOut"` TodayTrafficOut int64 `json:"todayTrafficOut"`
CurConns int64 `json:"curConns"` CurConns int64 `json:"curConns"`
LastStartTime string `json:"lastStartTime"` LastStartTime string `json:"lastStartTime"`
LastCloseTime string `json:"lastCloseTime"` LastCloseTime string `json:"lastCloseTime"`
Status string `json:"status"` Status string `json:"status"`
} }
// /api/proxy/:type/:name // /api/proxy/:type/:name

@ -17,7 +17,8 @@ package proxy
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"sync"
"github.com/fatedier/golib/errors"
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"
@ -31,8 +32,7 @@ type XTCPProxy struct {
*BaseProxy *BaseProxy
cfg *v1.XTCPProxyConfig cfg *v1.XTCPProxyConfig
closeCh chan struct{} closeCh chan struct{}
closeOnce sync.Once
} }
func NewXTCPProxy(baseProxy *BaseProxy) Proxy { func NewXTCPProxy(baseProxy *BaseProxy) Proxy {
@ -43,7 +43,6 @@ func NewXTCPProxy(baseProxy *BaseProxy) Proxy {
return &XTCPProxy{ return &XTCPProxy{
BaseProxy: baseProxy, BaseProxy: baseProxy,
cfg: unwrapped, cfg: unwrapped,
closeCh: make(chan struct{}),
} }
} }
@ -88,9 +87,9 @@ func (pxy *XTCPProxy) Run() (remoteAddr string, err error) {
} }
func (pxy *XTCPProxy) Close() { func (pxy *XTCPProxy) Close() {
pxy.closeOnce.Do(func() { pxy.BaseProxy.Close()
pxy.BaseProxy.Close() pxy.rc.NatHoleController.CloseClient(pxy.GetName())
pxy.rc.NatHoleController.CloseClient(pxy.GetName()) _ = errors.PanicToError(func() {
close(pxy.closeCh) close(pxy.closeCh)
}) })
} }

@ -386,30 +386,24 @@ func (svr *Service) Run(ctx context.Context) {
func (svr *Service) Close() error { func (svr *Service) Close() error {
if svr.kcpListener != nil { if svr.kcpListener != nil {
svr.kcpListener.Close() svr.kcpListener.Close()
svr.kcpListener = nil
} }
if svr.quicListener != nil { if svr.quicListener != nil {
svr.quicListener.Close() svr.quicListener.Close()
svr.quicListener = nil
} }
if svr.websocketListener != nil { if svr.websocketListener != nil {
svr.websocketListener.Close() svr.websocketListener.Close()
svr.websocketListener = nil
} }
if svr.tlsListener != nil { if svr.tlsListener != nil {
svr.tlsListener.Close() svr.tlsListener.Close()
} svr.tlsConfig = nil
if svr.sshTunnelListener != nil {
svr.sshTunnelListener.Close()
} }
if svr.listener != nil { if svr.listener != nil {
svr.listener.Close() svr.listener.Close()
svr.listener = nil
} }
if svr.webServer != nil {
svr.webServer.Close()
}
if svr.sshTunnelGateway != nil {
svr.sshTunnelGateway.Close()
}
svr.rc.Close()
svr.muxer.Close()
svr.ctlManager.Close() svr.ctlManager.Close()
if svr.cancel != nil { if svr.cancel != nil {
svr.cancel() svr.cancel()

@ -5,75 +5,75 @@ import (
) )
// ExpectEqual expects the specified two are the same, otherwise an exception raises // ExpectEqual expects the specified two are the same, otherwise an exception raises
func ExpectEqual(actual any, extra any, explain ...any) { func ExpectEqual(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.Equal(extra), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.Equal(extra), explain...)
} }
// ExpectEqualValues expects the specified two are the same, it not strict about type // ExpectEqualValues expects the specified two are the same, it not strict about type
func ExpectEqualValues(actual any, extra any, explain ...any) { func ExpectEqualValues(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.BeEquivalentTo(extra), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.BeEquivalentTo(extra), explain...)
} }
func ExpectEqualValuesWithOffset(offset int, actual any, extra any, explain ...any) { func ExpectEqualValuesWithOffset(offset int, actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1+offset, actual).To(gomega.BeEquivalentTo(extra), explain...) gomega.ExpectWithOffset(1+offset, actual).To(gomega.BeEquivalentTo(extra), explain...)
} }
// ExpectNotEqual expects the specified two are not the same, otherwise an exception raises // ExpectNotEqual expects the specified two are not the same, otherwise an exception raises
func ExpectNotEqual(actual any, extra any, explain ...any) { func ExpectNotEqual(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).NotTo(gomega.Equal(extra), explain...) gomega.ExpectWithOffset(1, actual).NotTo(gomega.Equal(extra), explain...)
} }
// ExpectError expects an error happens, otherwise an exception raises // ExpectError expects an error happens, otherwise an exception raises
func ExpectError(err error, explain ...any) { func ExpectError(err error, explain ...interface{}) {
gomega.ExpectWithOffset(1, err).To(gomega.HaveOccurred(), explain...) gomega.ExpectWithOffset(1, err).To(gomega.HaveOccurred(), explain...)
} }
func ExpectErrorWithOffset(offset int, err error, explain ...any) { func ExpectErrorWithOffset(offset int, err error, explain ...interface{}) {
gomega.ExpectWithOffset(1+offset, err).To(gomega.HaveOccurred(), explain...) gomega.ExpectWithOffset(1+offset, err).To(gomega.HaveOccurred(), explain...)
} }
// ExpectNoError checks if "err" is set, and if so, fails assertion while logging the error. // ExpectNoError checks if "err" is set, and if so, fails assertion while logging the error.
func ExpectNoError(err error, explain ...any) { func ExpectNoError(err error, explain ...interface{}) {
ExpectNoErrorWithOffset(1, err, explain...) ExpectNoErrorWithOffset(1, err, explain...)
} }
// ExpectNoErrorWithOffset checks if "err" is set, and if so, fails assertion while logging the error at "offset" levels above its caller // ExpectNoErrorWithOffset checks if "err" is set, and if so, fails assertion while logging the error at "offset" levels above its caller
// (for example, for call chain f -> g -> ExpectNoErrorWithOffset(1, ...) error would be logged for "f"). // (for example, for call chain f -> g -> ExpectNoErrorWithOffset(1, ...) error would be logged for "f").
func ExpectNoErrorWithOffset(offset int, err error, explain ...any) { func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...) gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
} }
func ExpectContainSubstring(actual, substr string, explain ...any) { func ExpectContainSubstring(actual, substr string, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.ContainSubstring(substr), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.ContainSubstring(substr), explain...)
} }
// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter. // ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
func ExpectConsistOf(actual any, extra any, explain ...any) { func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
} }
func ExpectContainElements(actual any, extra any, explain ...any) { func ExpectContainElements(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.ContainElements(extra), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.ContainElements(extra), explain...)
} }
func ExpectNotContainElements(actual any, extra any, explain ...any) { func ExpectNotContainElements(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).NotTo(gomega.ContainElements(extra), explain...) gomega.ExpectWithOffset(1, actual).NotTo(gomega.ContainElements(extra), explain...)
} }
// ExpectHaveKey expects the actual map has the key in the keyset // ExpectHaveKey expects the actual map has the key in the keyset
func ExpectHaveKey(actual any, key any, explain ...any) { func ExpectHaveKey(actual interface{}, key interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.HaveKey(key), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.HaveKey(key), explain...)
} }
// ExpectEmpty expects actual is empty // ExpectEmpty expects actual is empty
func ExpectEmpty(actual any, explain ...any) { func ExpectEmpty(actual interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.BeEmpty(), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.BeEmpty(), explain...)
} }
func ExpectTrue(actual any, explain ...any) { func ExpectTrue(actual interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).Should(gomega.BeTrue(), explain...) gomega.ExpectWithOffset(1, actual).Should(gomega.BeTrue(), explain...)
} }
func ExpectTrueWithOffset(offset int, actual any, explain ...any) { func ExpectTrueWithOffset(offset int, actual interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1+offset, actual).Should(gomega.BeTrue(), explain...) gomega.ExpectWithOffset(1+offset, actual).Should(gomega.BeTrue(), explain...)
} }

@ -11,18 +11,18 @@ func nowStamp() string {
return time.Now().Format(time.StampMilli) return time.Now().Format(time.StampMilli)
} }
func log(level string, format string, args ...any) { func log(level string, format string, args ...interface{}) {
fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...) fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...)
} }
// Logf logs the info. // Logf logs the info.
func Logf(format string, args ...any) { func Logf(format string, args ...interface{}) {
log("INFO", format, args...) log("INFO", format, args...)
} }
// Failf logs the fail info, including a stack trace starts with its direct caller // Failf logs the fail info, including a stack trace starts with its direct caller
// (for example, for call chain f -> g -> Failf("foo", ...) error would be logged for "g"). // (for example, for call chain f -> g -> Failf("foo", ...) error would be logged for "g").
func Failf(format string, args ...any) { func Failf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...) msg := fmt.Sprintf(format, args...)
skip := 1 skip := 1
ginkgo.Fail(msg, skip) ginkgo.Fail(msg, skip)

@ -67,8 +67,8 @@ func (m *MockServers) Close() {
os.Remove(m.udsEchoServer.BindAddr()) os.Remove(m.udsEchoServer.BindAddr())
} }
func (m *MockServers) GetTemplateParams() map[string]any { func (m *MockServers) GetTemplateParams() map[string]interface{} {
ret := make(map[string]any) ret := make(map[string]interface{})
ret[TCPEchoServerPort] = m.tcpEchoServer.BindPort() ret[TCPEchoServerPort] = m.tcpEchoServer.BindPort()
ret[UDPEchoServerPort] = m.udpEchoServer.BindPort() ret[UDPEchoServerPort] = m.udpEchoServer.BindPort()
ret[UDSEchoServerAddr] = m.udsEchoServer.BindAddr() ret[UDSEchoServerAddr] = m.udsEchoServer.BindAddr()
@ -76,7 +76,7 @@ func (m *MockServers) GetTemplateParams() map[string]any {
return ret return ret
} }
func (m *MockServers) GetParam(key string) any { func (m *MockServers) GetParam(key string) interface{} {
params := m.GetTemplateParams() params := m.GetTemplateParams()
if v, ok := params[key]; ok { if v, ok := params[key]; ok {
return v return v

@ -42,7 +42,7 @@ type RequestExpect struct {
f *Framework f *Framework
expectResp []byte expectResp []byte
expectError bool expectError bool
explain []any explain []interface{}
} }
func NewRequestExpect(f *Framework) *RequestExpect { func NewRequestExpect(f *Framework) *RequestExpect {
@ -51,7 +51,7 @@ func NewRequestExpect(f *Framework) *RequestExpect {
f: f, f: f,
expectResp: []byte(consts.TestString), expectResp: []byte(consts.TestString),
expectError: false, expectError: false,
explain: make([]any, 0), explain: make([]interface{}, 0),
} }
} }
@ -94,7 +94,7 @@ func (e *RequestExpect) ExpectError(expectErr bool) *RequestExpect {
return e return e
} }
func (e *RequestExpect) Explain(explain ...any) *RequestExpect { func (e *RequestExpect) Explain(explain ...interface{}) *RequestExpect {
e.explain = explain e.explain = explain
return e return e
} }

@ -8,7 +8,7 @@
type="textarea" type="textarea"
autosize autosize
v-model="textarea" v-model="textarea"
placeholder="frpc configure file, can not be empty..." placeholder="frpc configrue file, can not be empty..."
></el-input> ></el-input>
</div> </div>
</template> </template>