From 2d67e2e0c69e93a2465f7c954675247d7c0e3352 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 18 Dec 2023 10:53:02 +0800 Subject: [PATCH 1/4] remove copilot for pr (#3857) --- .github/pull_request_template.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d24eaf8..7724848 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,3 @@ -### Summary - -copilot:summary - ### WHY + From 354091087955f6fcb7216ee03a2397376e4526c4 Mon Sep 17 00:00:00 2001 From: Remember <36129334+wuqinqiang@users.noreply.github.com> Date: Thu, 21 Dec 2023 11:43:42 +0800 Subject: [PATCH 2/4] fix(backoff): close of closed out channel (#3871) * fix: close of closed channel * feat: replace Try0 to std --- pkg/util/wait/backoff.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/util/wait/backoff.go b/pkg/util/wait/backoff.go index 45e0ab6..048c959 100644 --- a/pkg/util/wait/backoff.go +++ b/pkg/util/wait/backoff.go @@ -16,10 +16,9 @@ package wait import ( "math/rand" + "sync" "time" - "github.com/samber/lo" - "github.com/fatedier/frp/pkg/util/util" ) @@ -182,16 +181,18 @@ func Until(f func(), period time.Duration, stopCh <-chan struct{}) { func MergeAndCloseOnAnyStopChannel[T any](upstreams ...<-chan T) <-chan T { out := make(chan T) - + closeOnce := sync.Once{} for _, upstream := range upstreams { ch := upstream - go lo.Try0(func() { + go func() { select { case <-ch: - close(out) + closeOnce.Do(func() { + close(out) + }) case <-out: } - }) + }() } return out } From 3bf6605e1a9a252d15df564e9c9e9ac68adc7068 Mon Sep 17 00:00:00 2001 From: im_zhou <32025208+im-zhou@users.noreply.github.com> Date: Thu, 21 Dec 2023 20:51:10 +0800 Subject: [PATCH 3/4] fix: duplicate call loginFunc (#3860) (#3875) modify ext func, specify whether exit immediately --- client/control.go | 6 +++--- client/service.go | 20 ++++++++------------ pkg/util/wait/backoff.go | 10 ++++++---- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/client/control.go b/client/control.go index e4b01ae..edfc726 100644 --- a/client/control.go +++ b/client/control.go @@ -239,15 +239,15 @@ func (ctl *Control) heartbeatWorker() { // Users can still enable heartbeat feature by setting HeartbeatInterval to a positive value. if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 { // send heartbeat to server - sendHeartBeat := func() error { + sendHeartBeat := func() (bool, error) { xl.Debug("send heartbeat to server") pingMsg := &msg.Ping{} if err := ctl.sessionCtx.AuthSetter.SetPing(pingMsg); err != nil { xl.Warn("error during ping authentication: %v, skip sending ping message", err) - return err + return false, err } _ = ctl.msgDispatcher.Send(pingMsg) - return nil + return false, nil } go wait.BackoffUntil(sendHeartBeat, diff --git a/client/service.go b/client/service.go index c43f8f6..e8ecc2b 100644 --- a/client/service.go +++ b/client/service.go @@ -192,16 +192,16 @@ func (svr *Service) keepControllerWorking() { // the control immediately exits. It is necessary to limit the frequency of reconnection in this case. // The interval for the first three retries in 1 minute will be very short, and then it will increase exponentially. // The maximum interval is 20 seconds. - wait.BackoffUntil(func() error { + wait.BackoffUntil(func() (bool, error) { // loopLoginUntilSuccess is another layer of loop that will continuously attempt to // login to the server until successful. svr.loopLoginUntilSuccess(20*time.Second, false) if svr.ctl != nil { <-svr.ctl.Done() - return errors.New("control is closed and try another loop") + return false, errors.New("control is closed and try another loop") } // If the control is nil, it means that the login failed and the service is also closed. - return nil + return false, nil }, wait.NewFastBackoffManager( wait.FastBackoffOptions{ Duration: time.Second, @@ -282,9 +282,8 @@ func (svr *Service) login() (conn net.Conn, connector Connector, err error) { func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginExit bool) { xl := xlog.FromContextSafe(svr.ctx) - successCh := make(chan struct{}) - loginFunc := func() error { + loginFunc := func() (bool, error) { xl.Info("try to connect to server...") conn, connector, err := svr.login() if err != nil { @@ -292,7 +291,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE if firstLoginExit { svr.cancel(cancelErr{Err: err}) } - return err + return false, err } svr.cfgMu.RLock() @@ -315,7 +314,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE if err != nil { conn.Close() xl.Error("NewControl error: %v", err) - return err + return false, err } ctl.SetInWorkConnCallback(svr.handleWorkConnCb) @@ -328,8 +327,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE svr.ctl = ctl svr.ctlMu.Unlock() - close(successCh) - return nil + return true, nil } // try to reconnect to server until success @@ -339,9 +337,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE Factor: 2, Jitter: 0.1, MaxDuration: maxInterval, - }), - true, - wait.MergeAndCloseOnAnyStopChannel(svr.ctx.Done(), successCh)) + }), true, svr.ctx.Done()) } func (svr *Service) UpdateAllConfigurer(proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error { diff --git a/pkg/util/wait/backoff.go b/pkg/util/wait/backoff.go index 048c959..4a0e7e0 100644 --- a/pkg/util/wait/backoff.go +++ b/pkg/util/wait/backoff.go @@ -113,7 +113,7 @@ func (f *fastBackoffImpl) Backoff(previousDuration time.Duration, previousCondit return f.options.Duration } -func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh <-chan struct{}) { +func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) { var delay time.Duration previousError := false @@ -131,7 +131,9 @@ func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh < delay = backoff.Backoff(delay, previousError) } - if err := f(); err != nil { + if done, err := f(); done { + return + } else if err != nil { previousError = true } else { previousError = false @@ -170,9 +172,9 @@ func Jitter(duration time.Duration, maxFactor float64) time.Duration { } func Until(f func(), period time.Duration, stopCh <-chan struct{}) { - ff := func() error { + ff := func() (bool, error) { f() - return nil + return false, nil } BackoffUntil(ff, BackoffFunc(func(time.Duration, bool) time.Duration { return period From 5e77c8e2d33317a73282483863c9bdb8e067cd1c Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 21 Dec 2023 21:19:49 +0800 Subject: [PATCH 4/4] fix lint (#3877) --- .golangci.yml | 3 +++ Release.md | 10 +--------- client/service.go | 3 +-- pkg/util/version/version.go | 2 +- pkg/util/wait/backoff.go | 6 ------ 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index f166e9d..8349c9e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -132,6 +132,9 @@ issues: - linters: - revive text: "unused-parameter" + - linters: + - unparam + text: "is always false" # Independently from option `exclude` we use default exclude patterns, # it can be disabled by this option. To list all diff --git a/Release.md b/Release.md index 8e1ea86..59c4803 100644 --- a/Release.md +++ b/Release.md @@ -1,11 +1,3 @@ -### Features - -* The new command line parameter `--strict_config` has been added to enable strict configuration validation mode. It will throw an error for unknown fields instead of ignoring them. In future versions, we will set the default value of this parameter to true to avoid misconfigurations. -* Support `SSH reverse tunneling`. With this feature, you can expose your local service without running frpc, only using SSH. The SSH reverse tunnel agent has many functional limitations compared to the frpc agent. The currently supported proxy types are tcp, http, https, tcpmux, and stcp. -* The frpc tcpmux command line parameters have been updated to support configuring `http_user` and `http_pwd`. -* The frpc stcp/sudp/xtcp command line parameters have been updated to support configuring `allow_users`. - ### Fixes -* frpc: Return code 1 when the first login attempt fails and exits. -* When auth.method is `oidc` and auth.additionalScopes contains `HeartBeats`, if obtaining AccessToken fails, the application will be unresponsive. +* frpc has a certain chance to panic when login: close of closed channel. diff --git a/client/service.go b/client/service.go index e8ecc2b..74ac419 100644 --- a/client/service.go +++ b/client/service.go @@ -326,14 +326,13 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE } svr.ctl = ctl svr.ctlMu.Unlock() - return true, nil } // try to reconnect to server until success wait.BackoffUntil(loginFunc, wait.NewFastBackoffManager( wait.FastBackoffOptions{ - Duration: time.Second, + Duration: time.Millisecond, Factor: 2, Jitter: 0.1, MaxDuration: maxInterval, diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go index ab79a55..680fbdc 100644 --- a/pkg/util/version/version.go +++ b/pkg/util/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version = "0.53.0" +var version = "0.53.1" func Full() string { return version diff --git a/pkg/util/wait/backoff.go b/pkg/util/wait/backoff.go index 4a0e7e0..e07c531 100644 --- a/pkg/util/wait/backoff.go +++ b/pkg/util/wait/backoff.go @@ -144,12 +144,6 @@ func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool, } ticker.Reset(delay) - select { - case <-stopCh: - return - default: - } - select { case <-stopCh: return