mirror of
https://github.com/fatedier/frp.git
synced 2025-07-27 07:35:07 +00:00
Compare commits
15 Commits
v0.53.0
...
3aeaeccfa5
Author | SHA1 | Date | |
---|---|---|---|
|
3aeaeccfa5 | ||
|
7418ae098d | ||
|
7999791708 | ||
|
f7efbfeec5 | ||
|
1e8806d26b | ||
|
d01f4a3ec1 | ||
|
596262d5e0 | ||
|
cdfa8fa66f | ||
|
256b87321d | ||
|
5b7b81a117 | ||
|
2a9a7a0e4a | ||
|
5e77c8e2d3 | ||
|
3bf6605e1a | ||
|
3540910879 | ||
|
2d67e2e0c6 |
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -1,6 +1,3 @@
|
||||
### Summary
|
||||
|
||||
copilot:summary
|
||||
|
||||
### WHY
|
||||
|
||||
<!-- author to complete -->
|
||||
|
10
.github/workflows/stale.yml
vendored
10
.github/workflows/stale.yml
vendored
@@ -18,17 +18,17 @@ jobs:
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
|
||||
stale-pr-message: "PRs go stale after 30d of inactivity. Stale PRs rot after an additional 7d 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 21d of inactivity. Stale PRs rot after an additional 7d 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: 30
|
||||
days-before-stale: 21
|
||||
days-before-close: 7
|
||||
debug-only: ${{ github.event.inputs.debug-only }}
|
||||
exempt-all-pr-milestones: true
|
||||
exempt-all-pr-assignees: true
|
||||
operations-per-run: 200
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,6 +34,8 @@ dist/
|
||||
.idea/
|
||||
.vscode/
|
||||
.autogen_ssh_key
|
||||
client.crt
|
||||
client.key
|
||||
|
||||
# Cache
|
||||
*.swp
|
||||
|
@@ -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
|
||||
|
12
Release.md
12
Release.md
@@ -1,11 +1,3 @@
|
||||
### Features
|
||||
### Deprecation Notices
|
||||
|
||||
* 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.
|
||||
* Using an underscore in a flag name is deprecated and has been replaced by a hyphen. The underscore format will remain compatible for some time, until it is completely removed in a future version. For example, `--remote_port` is replaced with `--remote-port`.
|
||||
|
@@ -35,7 +35,7 @@ import (
|
||||
"github.com/fatedier/frp/pkg/util/xlog"
|
||||
)
|
||||
|
||||
// Connector is a interface for establishing connections to the server.
|
||||
// Connector is an interface for establishing connections to the server.
|
||||
type Connector interface {
|
||||
Open() error
|
||||
Connect() (net.Conn, error)
|
||||
@@ -59,7 +59,7 @@ func NewConnector(ctx context.Context, cfg *v1.ClientCommonConfig) Connector {
|
||||
}
|
||||
}
|
||||
|
||||
// Open opens a underlying connection to the server.
|
||||
// Open opens an underlying connection to the server.
|
||||
// The underlying connection is either a TCP connection or a QUIC connection.
|
||||
// After the underlying connection is established, you can call Connect() to get a stream.
|
||||
// If TCPMux isn't enabled, the underlying connection is nil, you will get a new real TCP connection every time you call Connect().
|
||||
|
@@ -133,6 +133,7 @@ func (ctl *Control) handleReqWorkConn(_ msg.Message) {
|
||||
}
|
||||
if err = ctl.sessionCtx.AuthSetter.SetNewWorkConn(m); err != nil {
|
||||
xl.Warn("error during NewWorkConn authentication: %v", err)
|
||||
workConn.Close()
|
||||
return
|
||||
}
|
||||
if err = msg.WriteMsg(workConn, m); err != nil {
|
||||
@@ -239,15 +240,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,
|
||||
|
@@ -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)
|
||||
|
||||
@@ -327,9 +326,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 +336,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 {
|
||||
|
@@ -97,6 +97,7 @@ func runMultipleClients(cfgDir string) error {
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
rootCmd.SetGlobalNormalizationFunc(config.WordSepNormalizeFunc)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@@ -92,6 +92,7 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
rootCmd.SetGlobalNormalizationFunc(config.WordSepNormalizeFunc)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ transport.maxPoolCount = 5
|
||||
# transport.tcpKeepalive = 7200
|
||||
|
||||
# transport.tls.force specifies whether to only accept TLS-encrypted connections. By default, the value is false.
|
||||
tls.force = false
|
||||
transport.tls.force = false
|
||||
|
||||
# transport.tls.certFile = "server.crt"
|
||||
# transport.tls.keyFile = "server.key"
|
||||
@@ -129,7 +129,7 @@ allowPorts = [
|
||||
maxPortsPerClient = 0
|
||||
|
||||
# If subDomainHost is not empty, you can set subdomain when type is http or https in frpc's configure file
|
||||
# When subdomain is est, the host used by routing is test.frps.com
|
||||
# When subdomain is test, the host used by routing is test.frps.com
|
||||
subDomainHost = "frps.com"
|
||||
|
||||
# custom 404 page for HTTP requests
|
||||
|
6
go.mod
6
go.mod
@@ -24,7 +24,7 @@ require (
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/crypto v0.15.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/sync v0.3.0
|
||||
@@ -39,7 +39,7 @@ require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
@@ -67,7 +67,7 @@ require (
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.9.3 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
14
go.sum
14
go.sum
@@ -32,8 +32,8 @@ github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
|
||||
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/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
@@ -157,8 +157,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
@@ -210,13 +210,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
@@ -26,5 +26,9 @@ frpsPath=${ROOT}/bin/frps
|
||||
if [ "${FRPS_PATH}" ]; then
|
||||
frpsPath="${FRPS_PATH}"
|
||||
fi
|
||||
concurrency="16"
|
||||
if [ "${CONCURRENCY}" ]; then
|
||||
concurrency="${CONCURRENCY}"
|
||||
fi
|
||||
|
||||
ginkgo -nodes=8 --poll-progress-after=60s ${ROOT}/test/e2e -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug}
|
||||
ginkgo -nodes=${concurrency} --poll-progress-after=60s ${ROOT}/test/e2e -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug}
|
||||
|
@@ -17,14 +17,24 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/fatedier/frp/pkg/config/types"
|
||||
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||
)
|
||||
|
||||
// WordSepNormalizeFunc changes all flags that contain "_" separators
|
||||
func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
if strings.Contains(name, "_") {
|
||||
return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
}
|
||||
|
||||
type RegisterFlagOption func(*registerFlagOptions)
|
||||
|
||||
type registerFlagOptions struct {
|
||||
|
@@ -195,7 +195,7 @@ type ProxyConfigurer interface {
|
||||
// MarshalToMsg marshals this config into a msg.NewProxy message. This
|
||||
// function will be called on the frpc side.
|
||||
MarshalToMsg(*msg.NewProxy)
|
||||
// UnmarshalFromMsg unmarshals a msg.NewProxy message into this config.
|
||||
// UnmarshalFromMsg unmarshal a msg.NewProxy message into this config.
|
||||
// This function will be called on the frps side.
|
||||
UnmarshalFromMsg(*msg.NewProxy)
|
||||
}
|
||||
|
@@ -254,6 +254,8 @@ func (s *TunnelServer) parseClientAndProxyConfigurer(_ *tcpipForward, extraPaylo
|
||||
Short: "ssh v0@{address} [command]",
|
||||
Run: func(*cobra.Command, []string) {},
|
||||
}
|
||||
cmd.SetGlobalNormalizationFunc(config.WordSepNormalizeFunc)
|
||||
|
||||
args := strings.Split(extraPayload, " ")
|
||||
if len(args) < 1 {
|
||||
return nil, nil, helpMessage, fmt.Errorf("invalid extra payload")
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var version = "0.53.0"
|
||||
var version = "0.53.2"
|
||||
|
||||
func Full() string {
|
||||
return version
|
||||
|
@@ -16,10 +16,9 @@ package wait
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/fatedier/frp/pkg/util/util"
|
||||
)
|
||||
|
||||
@@ -114,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
|
||||
|
||||
@@ -132,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
|
||||
@@ -143,12 +144,6 @@ func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh <
|
||||
}
|
||||
|
||||
ticker.Reset(delay)
|
||||
select {
|
||||
case <-stopCh:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case <-stopCh:
|
||||
return
|
||||
@@ -171,9 +166,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
|
||||
@@ -182,16 +177,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
|
||||
}
|
||||
|
@@ -67,7 +67,7 @@ func NewDefaultFramework() *Framework {
|
||||
TotalParallelNode: suiteConfig.ParallelTotal,
|
||||
CurrentNodeIndex: suiteConfig.ParallelProcess,
|
||||
FromPortIndex: 10000,
|
||||
ToPortIndex: 60000,
|
||||
ToPortIndex: 30000,
|
||||
}
|
||||
return NewFramework(options)
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
||||
ExpectNoError(err)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
currentClientProcesses := make([]*process.Process, 0, len(clientTemplates))
|
||||
for i := range clientTemplates {
|
||||
@@ -76,7 +76,7 @@ func (f *Framework) RunFrps(args ...string) (*process.Process, string, error) {
|
||||
return p, p.StdOutput(), err
|
||||
}
|
||||
// sleep for a while to get std output
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(2 * time.Second)
|
||||
return p, p.StdOutput(), nil
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (f *Framework) RunFrpc(args ...string) (*process.Process, string, error) {
|
||||
if err != nil {
|
||||
return p, p.StdOutput(), err
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(2 * time.Second)
|
||||
return p, p.StdOutput(), nil
|
||||
}
|
||||
|
||||
|
@@ -22,11 +22,11 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
clientConf := consts.LegacyDefaultClientConfig
|
||||
|
||||
serverConf += `
|
||||
allow_ports = 20000-25000,25002,30000-50000
|
||||
allow_ports = 10000-11000,11002,12000-13000
|
||||
`
|
||||
|
||||
tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000))
|
||||
udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000))
|
||||
tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000))
|
||||
udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000))
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp-allowded-in-range]
|
||||
type = tcp
|
||||
@@ -37,7 +37,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
[tcp-port-not-allowed]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = 25001
|
||||
remote_port = 11001
|
||||
`, framework.TCPEchoServerPort)
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp-port-unavailable]
|
||||
@@ -55,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
[udp-port-not-allowed]
|
||||
type = udp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = 25003
|
||||
remote_port = 11003
|
||||
`, framework.UDPEchoServerPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
@@ -65,7 +65,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
|
||||
|
||||
// Not Allowed
|
||||
framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure()
|
||||
framework.NewRequestExpect(f).Port(11001).ExpectError(true).Ensure()
|
||||
|
||||
// Unavailable, already bind by frps
|
||||
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
|
||||
@@ -76,7 +76,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
|
||||
// Not Allowed
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.UDP().Port(25003)
|
||||
r.UDP().Port(11003)
|
||||
}).ExpectError(true).Ensure()
|
||||
})
|
||||
|
||||
|
@@ -79,6 +79,7 @@ func (pa *Allocator) GetByName(portName string) int {
|
||||
udpConn.Close()
|
||||
|
||||
pa.used.Insert(port)
|
||||
pa.reserved.Delete(port)
|
||||
return port
|
||||
}
|
||||
return 0
|
||||
|
@@ -90,12 +90,12 @@ var _ = ginkgo.Describe("[Feature: Cmd]", func() {
|
||||
ginkgo.It("HTTP", func() {
|
||||
serverPort := f.AllocPort()
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort), "--vhost_http_port", strconv.Itoa(vhostHTTPPort))
|
||||
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort), "--vhost-http-port", strconv.Itoa(vhostHTTPPort))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
_, _, err = f.RunFrpc("http", "-s", "127.0.0.1", "-P", strconv.Itoa(serverPort), "-t", "123", "-u", "test",
|
||||
"-n", "udp_test", "-l", strconv.Itoa(f.PortByName(framework.HTTPSimpleServerPort)),
|
||||
"--custom_domain", "test.example.com")
|
||||
"--custom-domain", "test.example.com")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
|
@@ -23,14 +23,14 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
|
||||
serverConf += `
|
||||
allowPorts = [
|
||||
{ start = 20000, end = 25000 },
|
||||
{ single = 25002 },
|
||||
{ start = 30000, end = 50000 },
|
||||
{ start = 10000, end = 11000 },
|
||||
{ single = 11002 },
|
||||
{ start = 12000, end = 13000 },
|
||||
]
|
||||
`
|
||||
|
||||
tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000))
|
||||
udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000))
|
||||
tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000))
|
||||
udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000))
|
||||
clientConf += fmt.Sprintf(`
|
||||
[[proxies]]
|
||||
name = "tcp-allowded-in-range"
|
||||
@@ -43,7 +43,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
name = "tcp-port-not-allowed"
|
||||
type = "tcp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = 25001
|
||||
remotePort = 11001
|
||||
`, framework.TCPEchoServerPort)
|
||||
clientConf += fmt.Sprintf(`
|
||||
[[proxies]]
|
||||
@@ -64,7 +64,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
name = "udp-port-not-allowed"
|
||||
type = "udp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = 25003
|
||||
remotePort = 11003
|
||||
`, framework.UDPEchoServerPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
@@ -74,7 +74,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
|
||||
|
||||
// Not Allowed
|
||||
framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure()
|
||||
framework.NewRequestExpect(f).Port(11001).ExpectError(true).Ensure()
|
||||
|
||||
// Unavailable, already bind by frps
|
||||
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
|
||||
@@ -85,7 +85,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
||||
|
||||
// Not Allowed
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.UDP().Port(25003)
|
||||
r.UDP().Port(11003)
|
||||
}).ExpectError(true).Ensure()
|
||||
})
|
||||
|
||||
|
@@ -32,7 +32,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
|
||||
tc := ssh.NewTunnelClient(
|
||||
fmt.Sprintf("127.0.0.1:%d", localPort),
|
||||
fmt.Sprintf("127.0.0.1:%d", sshPort),
|
||||
fmt.Sprintf("tcp --remote_port %d", remotePort),
|
||||
fmt.Sprintf("tcp --remote-port %d", remotePort),
|
||||
)
|
||||
framework.ExpectNoError(tc.Start())
|
||||
defer tc.Close()
|
||||
@@ -55,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
|
||||
tc := ssh.NewTunnelClient(
|
||||
fmt.Sprintf("127.0.0.1:%d", localPort),
|
||||
fmt.Sprintf("127.0.0.1:%d", sshPort),
|
||||
"http --custom_domain test.example.com",
|
||||
"http --custom-domain test.example.com",
|
||||
)
|
||||
framework.ExpectNoError(tc.Start())
|
||||
defer tc.Close()
|
||||
@@ -83,7 +83,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
|
||||
tc := ssh.NewTunnelClient(
|
||||
fmt.Sprintf("127.0.0.1:%d", localPort),
|
||||
fmt.Sprintf("127.0.0.1:%d", sshPort),
|
||||
fmt.Sprintf("https --custom_domain %s", testDomain),
|
||||
fmt.Sprintf("https --custom-domain %s", testDomain),
|
||||
)
|
||||
framework.ExpectNoError(tc.Start())
|
||||
defer tc.Close()
|
||||
@@ -125,7 +125,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
|
||||
tc := ssh.NewTunnelClient(
|
||||
fmt.Sprintf("127.0.0.1:%d", localPort),
|
||||
fmt.Sprintf("127.0.0.1:%d", sshPort),
|
||||
fmt.Sprintf("tcpmux --mux=httpconnect --custom_domain %s", testDomain),
|
||||
fmt.Sprintf("tcpmux --mux=httpconnect --custom-domain %s", testDomain),
|
||||
)
|
||||
framework.ExpectNoError(tc.Start())
|
||||
defer tc.Close()
|
||||
@@ -179,7 +179,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
|
||||
tc := ssh.NewTunnelClient(
|
||||
fmt.Sprintf("127.0.0.1:%d", localPort),
|
||||
fmt.Sprintf("127.0.0.1:%d", sshPort),
|
||||
"stcp -n stcp-test --sk=abcdefg --allow_users=\"*\"",
|
||||
"stcp -n stcp-test --sk=abcdefg --allow-users=\"*\"",
|
||||
)
|
||||
framework.ExpectNoError(tc.Start())
|
||||
defer tc.Close()
|
||||
|
Reference in New Issue
Block a user