From ae73ec2fedc13cc2c44039dabba719a318221be5 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 30 Jul 2024 18:12:22 +0800 Subject: [PATCH] added a 30s timeout for frpc subcommands to avoid long delays (#4359) --- README.md | 5 ---- README_zh.md | 5 ---- Release.md | 3 +++ cmd/frpc/sub/admin.go | 46 +++++++++++++++++++-------------- pkg/sdk/client/client.go | 25 +++++++++--------- pkg/util/version/version.go | 2 +- test/e2e/legacy/basic/client.go | 9 ++++--- test/e2e/legacy/basic/server.go | 5 ++-- test/e2e/v1/basic/client.go | 9 ++++--- test/e2e/v1/basic/config.go | 3 ++- test/e2e/v1/basic/server.go | 5 ++-- 11 files changed, 62 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 915e1e8..97f1483 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,6 @@

Gold Sponsors

-

- - - -

diff --git a/README_zh.md b/README_zh.md index 95ede3e..a07e840 100644 --- a/README_zh.md +++ b/README_zh.md @@ -11,11 +11,6 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP

Gold Sponsors

-

- - - -

diff --git a/Release.md b/Release.md index c1649b9..d0414ef 100644 --- a/Release.md +++ b/Release.md @@ -1,5 +1,8 @@ ### Features * Added a new plugin `tls2raw`: Enables TLS termination and forwarding of decrypted raw traffic to local service. +* Added a default timeout of 30 seconds for the frpc subcommands to prevent commands from being stuck for a long time due to network issues. + +### Fixes * Fixed the issue that when `loginFailExit = false`, the frpc stop command cannot be stopped correctly if the server is not successfully connected after startup. diff --git a/cmd/frpc/sub/admin.go b/cmd/frpc/sub/admin.go index 5d478d4..abe8063 100644 --- a/cmd/frpc/sub/admin.go +++ b/cmd/frpc/sub/admin.go @@ -15,9 +15,11 @@ package sub import ( + "context" "fmt" "os" "strings" + "time" "github.com/rodaine/table" "github.com/spf13/cobra" @@ -27,24 +29,24 @@ import ( clientsdk "github.com/fatedier/frp/pkg/sdk/client" ) -func init() { - rootCmd.AddCommand(NewAdminCommand( - "reload", - "Hot-Reload frpc configuration", - ReloadHandler, - )) +var adminAPITimeout = 30 * time.Second - rootCmd.AddCommand(NewAdminCommand( - "status", - "Overview of all proxies status", - StatusHandler, - )) +func init() { + commands := []struct { + name string + description string + handler func(*v1.ClientCommonConfig) error + }{ + {"reload", "Hot-Reload frpc configuration", ReloadHandler}, + {"status", "Overview of all proxies status", StatusHandler}, + {"stop", "Stop the running frpc", StopHandler}, + } - rootCmd.AddCommand(NewAdminCommand( - "stop", - "Stop the running frpc", - StopHandler, - )) + for _, cmdConfig := range commands { + cmd := NewAdminCommand(cmdConfig.name, cmdConfig.description, cmdConfig.handler) + cmd.Flags().DurationVar(&adminAPITimeout, "api-timeout", adminAPITimeout, "Timeout for admin API calls") + rootCmd.AddCommand(cmd) + } } func NewAdminCommand(name, short string, handler func(*v1.ClientCommonConfig) error) *cobra.Command { @@ -73,7 +75,9 @@ func NewAdminCommand(name, short string, handler func(*v1.ClientCommonConfig) er func ReloadHandler(clientCfg *v1.ClientCommonConfig) error { client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port) client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password) - if err := client.Reload(strictConfigMode); err != nil { + ctx, cancel := context.WithTimeout(context.Background(), adminAPITimeout) + defer cancel() + if err := client.Reload(ctx, strictConfigMode); err != nil { return err } fmt.Println("reload success") @@ -83,7 +87,9 @@ func ReloadHandler(clientCfg *v1.ClientCommonConfig) error { func StatusHandler(clientCfg *v1.ClientCommonConfig) error { client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port) client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password) - res, err := client.GetAllProxyStatus() + ctx, cancel := context.WithTimeout(context.Background(), adminAPITimeout) + defer cancel() + res, err := client.GetAllProxyStatus(ctx) if err != nil { return err } @@ -109,7 +115,9 @@ func StatusHandler(clientCfg *v1.ClientCommonConfig) error { func StopHandler(clientCfg *v1.ClientCommonConfig) error { client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port) client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password) - if err := client.Stop(); err != nil { + ctx, cancel := context.WithTimeout(context.Background(), adminAPITimeout) + defer cancel() + if err := client.Stop(ctx); err != nil { return err } fmt.Println("stop success") diff --git a/pkg/sdk/client/client.go b/pkg/sdk/client/client.go index 57bf774..13713e2 100644 --- a/pkg/sdk/client/client.go +++ b/pkg/sdk/client/client.go @@ -1,6 +1,7 @@ package client import ( + "context" "encoding/json" "fmt" "io" @@ -31,8 +32,8 @@ func (c *Client) SetAuth(user, pwd string) { c.authPwd = pwd } -func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) { - req, err := http.NewRequest("GET", "http://"+c.address+"/api/status", nil) +func (c *Client) GetProxyStatus(ctx context.Context, name string) (*client.ProxyStatusResp, error) { + req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/status", nil) if err != nil { return nil, err } @@ -54,8 +55,8 @@ func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) { return nil, fmt.Errorf("no proxy status found") } -func (c *Client) GetAllProxyStatus() (client.StatusResp, error) { - req, err := http.NewRequest("GET", "http://"+c.address+"/api/status", nil) +func (c *Client) GetAllProxyStatus(ctx context.Context) (client.StatusResp, error) { + req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/status", nil) if err != nil { return nil, err } @@ -70,7 +71,7 @@ func (c *Client) GetAllProxyStatus() (client.StatusResp, error) { return allStatus, nil } -func (c *Client) Reload(strictMode bool) error { +func (c *Client) Reload(ctx context.Context, strictMode bool) error { v := url.Values{} if strictMode { v.Set("strictConfig", "true") @@ -79,7 +80,7 @@ func (c *Client) Reload(strictMode bool) error { if len(v) > 0 { queryStr = "?" + v.Encode() } - req, err := http.NewRequest("GET", "http://"+c.address+"/api/reload"+queryStr, nil) + req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/reload"+queryStr, nil) if err != nil { return err } @@ -87,8 +88,8 @@ func (c *Client) Reload(strictMode bool) error { return err } -func (c *Client) Stop() error { - req, err := http.NewRequest("POST", "http://"+c.address+"/api/stop", nil) +func (c *Client) Stop(ctx context.Context) error { + req, err := http.NewRequestWithContext(ctx, "POST", "http://"+c.address+"/api/stop", nil) if err != nil { return err } @@ -96,16 +97,16 @@ func (c *Client) Stop() error { return err } -func (c *Client) GetConfig() (string, error) { - req, err := http.NewRequest("GET", "http://"+c.address+"/api/config", nil) +func (c *Client) GetConfig(ctx context.Context) (string, error) { + req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/config", nil) if err != nil { return "", err } return c.do(req) } -func (c *Client) UpdateConfig(content string) error { - req, err := http.NewRequest("PUT", "http://"+c.address+"/api/config", strings.NewReader(content)) +func (c *Client) UpdateConfig(ctx context.Context, content string) error { + req, err := http.NewRequestWithContext(ctx, "PUT", "http://"+c.address+"/api/config", strings.NewReader(content)) if err != nil { return err } diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go index 561a52e..a60d71d 100644 --- a/pkg/util/version/version.go +++ b/pkg/util/version/version.go @@ -14,7 +14,7 @@ package version -var version = "0.59.0" +var version = "0.60.0" func Full() string { return version diff --git a/test/e2e/legacy/basic/client.go b/test/e2e/legacy/basic/client.go index 98f675b..daed8b2 100644 --- a/test/e2e/legacy/basic/client.go +++ b/test/e2e/legacy/basic/client.go @@ -1,6 +1,7 @@ package basic import ( + "context" "fmt" "strconv" "strings" @@ -54,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() { framework.NewRequestExpect(f).Port(p3Port).Ensure() client := f.APIClientForFrpc(adminPort) - conf, err := client.GetConfig() + conf, err := client.GetConfig(context.Background()) framework.ExpectNoError(err) newP2Port := f.AllocPort() @@ -65,10 +66,10 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() { newClientConf = newClientConf[:p3Index] } - err = client.UpdateConfig(newClientConf) + err = client.UpdateConfig(context.Background(), newClientConf) framework.ExpectNoError(err) - err = client.Reload(true) + err = client.Reload(context.Background(), true) framework.ExpectNoError(err) time.Sleep(time.Second) @@ -120,7 +121,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() { framework.NewRequestExpect(f).Port(testPort).Ensure() client := f.APIClientForFrpc(adminPort) - err := client.Stop() + err := client.Stop(context.Background()) framework.ExpectNoError(err) time.Sleep(3 * time.Second) diff --git a/test/e2e/legacy/basic/server.go b/test/e2e/legacy/basic/server.go index 62bfd62..4399439 100644 --- a/test/e2e/legacy/basic/server.go +++ b/test/e2e/legacy/basic/server.go @@ -1,6 +1,7 @@ package basic import ( + "context" "fmt" "net" "strconv" @@ -101,7 +102,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() { client := f.APIClientForFrpc(adminPort) // tcp random port - status, err := client.GetProxyStatus("tcp") + status, err := client.GetProxyStatus(context.Background(), "tcp") framework.ExpectNoError(err) _, portStr, err := net.SplitHostPort(status.RemoteAddr) @@ -112,7 +113,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() { framework.NewRequestExpect(f).Port(port).Ensure() // udp random port - status, err = client.GetProxyStatus("udp") + status, err = client.GetProxyStatus(context.Background(), "udp") framework.ExpectNoError(err) _, portStr, err = net.SplitHostPort(status.RemoteAddr) diff --git a/test/e2e/v1/basic/client.go b/test/e2e/v1/basic/client.go index ef5e262..fd269d7 100644 --- a/test/e2e/v1/basic/client.go +++ b/test/e2e/v1/basic/client.go @@ -1,6 +1,7 @@ package basic import ( + "context" "fmt" "strconv" "strings" @@ -57,7 +58,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() { framework.NewRequestExpect(f).Port(p3Port).Ensure() client := f.APIClientForFrpc(adminPort) - conf, err := client.GetConfig() + conf, err := client.GetConfig(context.Background()) framework.ExpectNoError(err) newP2Port := f.AllocPort() @@ -68,10 +69,10 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() { newClientConf = newClientConf[:p3Index] } - err = client.UpdateConfig(newClientConf) + err = client.UpdateConfig(context.Background(), newClientConf) framework.ExpectNoError(err) - err = client.Reload(true) + err = client.Reload(context.Background(), true) framework.ExpectNoError(err) time.Sleep(time.Second) @@ -124,7 +125,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() { framework.NewRequestExpect(f).Port(testPort).Ensure() client := f.APIClientForFrpc(adminPort) - err := client.Stop() + err := client.Stop(context.Background()) framework.ExpectNoError(err) time.Sleep(3 * time.Second) diff --git a/test/e2e/v1/basic/config.go b/test/e2e/v1/basic/config.go index 7dc3ced..314e7fc 100644 --- a/test/e2e/v1/basic/config.go +++ b/test/e2e/v1/basic/config.go @@ -1,6 +1,7 @@ package basic import ( + "context" "fmt" "github.com/onsi/ginkgo/v2" @@ -72,7 +73,7 @@ var _ = ginkgo.Describe("[Feature: Config]", func() { client := f.APIClientForFrpc(adminPort) checkProxyFn := func(name string, localPort, remotePort int) { - status, err := client.GetProxyStatus(name) + status, err := client.GetProxyStatus(context.Background(), name) framework.ExpectNoError(err) framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort)) diff --git a/test/e2e/v1/basic/server.go b/test/e2e/v1/basic/server.go index fe8af4d..a3fe599 100644 --- a/test/e2e/v1/basic/server.go +++ b/test/e2e/v1/basic/server.go @@ -1,6 +1,7 @@ package basic import ( + "context" "fmt" "net" "strconv" @@ -112,7 +113,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() { client := f.APIClientForFrpc(adminPort) // tcp random port - status, err := client.GetProxyStatus("tcp") + status, err := client.GetProxyStatus(context.Background(), "tcp") framework.ExpectNoError(err) _, portStr, err := net.SplitHostPort(status.RemoteAddr) @@ -123,7 +124,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() { framework.NewRequestExpect(f).Port(port).Ensure() // udp random port - status, err = client.GetProxyStatus("udp") + status, err = client.GetProxyStatus(context.Background(), "udp") framework.ExpectNoError(err) _, portStr, err = net.SplitHostPort(status.RemoteAddr)