Ver código fonte

added a 30s timeout for frpc subcommands to avoid long delays (#4359)

fatedier 5 meses atrás
pai
commit
ae73ec2fed

+ 0 - 5
README.md

@@ -9,11 +9,6 @@
 
 <h3 align="center">Gold Sponsors</h3>
 <!--gold sponsors start-->
-<p align="center">
-  <a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
-    <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
-  </a>
-</p>
 <p align="center">
   <a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
     <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">

+ 0 - 5
README_zh.md

@@ -11,11 +11,6 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
 
 <h3 align="center">Gold Sponsors</h3>
 <!--gold sponsors start-->
-<p align="center">
-  <a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
-    <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
-  </a>
-</p>
 <p align="center">
   <a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
     <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">

+ 3 - 0
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.

+ 27 - 19
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")

+ 13 - 12
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
 	}

+ 1 - 1
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

+ 5 - 4
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)

+ 3 - 2
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)

+ 5 - 4
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)

+ 2 - 1
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))

+ 3 - 2
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)