Browse Source

Merge pull request #4700 from fatedier/dev

bump version
fatedier 6 days ago
parent
commit
31b44c1feb

+ 1 - 1
.circleci/config.yml

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

+ 4 - 4
.github/workflows/stale.yml

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

+ 2 - 1
.golangci.yml

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

+ 2 - 2
README.md

@@ -97,7 +97,7 @@ frp also offers a P2P connect mode.
     * [Client Plugins](#client-plugins)
     * [Server Manage Plugins](#server-manage-plugins)
     * [SSH Tunnel Gateway](#ssh-tunnel-gateway)
-* [Releated Projects](#releated-projects)
+* [Related Projects](#related-projects)
 * [Contributing](#contributing)
 * [Donation](#donation)
     * [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.
 
-## Releated Projects
+## Related 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/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.

+ 5 - 3
Release.md

@@ -1,5 +1,7 @@
 ### Features
 
-* `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.
-* The vhost HTTP proxy of frps supports the h2c protocol.
+* Support metadatas and annotations in frpc proxy commands.
+
+### Fixes
+
+* Properly release resources in service.Close() to prevent resource leaks when used as a library.

+ 1 - 1
client/event/event.go

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

+ 1 - 1
client/proxy/proxy_manager.go

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

+ 2 - 2
go.mod

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

+ 2 - 2
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.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatedier/golib v0.5.0 h1:hNcH7hgfIFqVWbP+YojCCAj4eO94pPf4dEF8lmq2jWs=
-github.com/fatedier/golib v0.5.0/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
+github.com/fatedier/golib v0.5.1 h1:hcKAnaw5mdI/1KWRGejxR+i1Hn/NvbY5UsMKDr7o13M=
+github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
 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/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=

+ 2 - 0
pkg/config/flags.go

@@ -106,6 +106,8 @@ func registerProxyBaseConfigFlags(cmd *cobra.Command, c *v1.ProxyBaseConfig, opt
 	}
 
 	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 {
 		cmd.Flags().StringVarP(&c.LocalIP, "local_ip", "i", "127.0.0.1", "local ip")

+ 2 - 2
pkg/config/legacy/client.go

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

+ 1 - 1
pkg/config/legacy/server.go

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

+ 2 - 2
pkg/config/load.go

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

+ 23 - 0
pkg/config/load_test.go

@@ -112,6 +112,29 @@ 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) {
 	require := require.New(t)
 

+ 1 - 1
pkg/msg/ctl.go

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

+ 1 - 1
pkg/msg/msg.go

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

+ 1 - 1
pkg/plugin/server/http.go

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

+ 5 - 5
pkg/plugin/server/manager.go

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

+ 1 - 1
pkg/plugin/server/plugin.go

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

+ 7 - 7
pkg/plugin/server/types.go

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

+ 4 - 0
pkg/ssh/gateway.go

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

+ 6 - 6
pkg/util/log/log.go

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

+ 1 - 1
pkg/util/version/version.go

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

+ 2 - 2
pkg/util/vhost/router.go

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

+ 4 - 0
pkg/util/vhost/vhost.go

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

+ 5 - 5
pkg/util/xlog/xlog.go

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

+ 10 - 0
server/controller/resource.go

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

+ 17 - 17
server/dashboard_api.go

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

+ 7 - 6
server/proxy/xtcp.go

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

+ 11 - 5
server/service.go

@@ -386,24 +386,30 @@ func (svr *Service) Run(ctx context.Context) {
 func (svr *Service) Close() error {
 	if svr.kcpListener != nil {
 		svr.kcpListener.Close()
-		svr.kcpListener = nil
 	}
 	if svr.quicListener != nil {
 		svr.quicListener.Close()
-		svr.quicListener = nil
 	}
 	if svr.websocketListener != nil {
 		svr.websocketListener.Close()
-		svr.websocketListener = nil
 	}
 	if svr.tlsListener != nil {
 		svr.tlsListener.Close()
-		svr.tlsConfig = nil
+	}
+	if svr.sshTunnelListener != nil {
+		svr.sshTunnelListener.Close()
 	}
 	if svr.listener != nil {
 		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()
 	if svr.cancel != nil {
 		svr.cancel()

+ 16 - 16
test/e2e/framework/expect.go

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

+ 3 - 3
test/e2e/framework/log.go

@@ -11,18 +11,18 @@ func nowStamp() string {
 	return time.Now().Format(time.StampMilli)
 }
 
-func log(level string, format string, args ...interface{}) {
+func log(level string, format string, args ...any) {
 	fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...)
 }
 
 // Logf logs the info.
-func Logf(format string, args ...interface{}) {
+func Logf(format string, args ...any) {
 	log("INFO", format, args...)
 }
 
 // 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").
-func Failf(format string, args ...interface{}) {
+func Failf(format string, args ...any) {
 	msg := fmt.Sprintf(format, args...)
 	skip := 1
 	ginkgo.Fail(msg, skip)

+ 3 - 3
test/e2e/framework/mockservers.go

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

+ 3 - 3
test/e2e/framework/request.go

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

+ 1 - 1
web/frpc/src/components/ClientConfigure.vue

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