add e2e tests for v1 config (#3608)

This commit is contained in:
fatedier 2023-09-13 16:32:39 +08:00 committed by GitHub
parent c95311d1a0
commit 7cd02f5bd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 3697 additions and 159 deletions

View File

@ -1 +1,3 @@
### Features ### Features
* Configuration: We now support TOML, YAML, and JSON for configuration. Please note that INI is deprecated and will be removed in future releases. New features will only be available in TOML, YAML, or JSON. Users wanting these new features should switch their configuration format accordingly. #2521

View File

@ -186,7 +186,7 @@ func parseClientCommonCfgFromCmd() (*v1.ClientCommonConfig, error) {
cfg.Complete() cfg.Complete()
err, warning := validation.ValidateClientCommonConfig(cfg) warning, err := validation.ValidateClientCommonConfig(cfg)
if warning != nil { if warning != nil {
fmt.Printf("WARNING: %v\n", warning) fmt.Printf("WARNING: %v\n", warning)
} }

View File

@ -108,7 +108,8 @@ var rootCmd = &cobra.Command{
if cfgFile != "" { if cfgFile != "" {
svrCfg, isLegacyFormat, err = config.LoadServerConfig(cfgFile) svrCfg, isLegacyFormat, err = config.LoadServerConfig(cfgFile)
if err != nil { if err != nil {
return err fmt.Println(err)
os.Exit(1)
} }
if isLegacyFormat { if isLegacyFormat {
fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " + fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " +
@ -116,7 +117,8 @@ var rootCmd = &cobra.Command{
} }
} else { } else {
if svrCfg, err = parseServerConfigFromCmd(); err != nil { if svrCfg, err = parseServerConfigFromCmd(); err != nil {
return err fmt.Println(err)
os.Exit(1)
} }
} }
@ -125,7 +127,8 @@ var rootCmd = &cobra.Command{
fmt.Printf("WARNING: %v\n", warning) fmt.Printf("WARNING: %v\n", warning)
} }
if err != nil { if err != nil {
return err fmt.Println(err)
os.Exit(1)
} }
if err := runServer(svrCfg); err != nil { if err := runServer(svrCfg); err != nil {
@ -168,7 +171,7 @@ func parseServerConfigFromCmd() (*v1.ServerConfig, error) {
cfg.Log.MaxDays = logMaxDays cfg.Log.MaxDays = logMaxDays
cfg.Log.DisablePrintColor = disableLogColor cfg.Log.DisablePrintColor = disableLogColor
cfg.SubDomainHost = subDomainHost cfg.SubDomainHost = subDomainHost
cfg.TLS.Force = tlsOnly cfg.Transport.TLS.Force = tlsOnly
cfg.MaxPortsPerClient = maxPortsPerClient cfg.MaxPortsPerClient = maxPortsPerClient
// Only token authentication is supported in cmd mode // Only token authentication is supported in cmd mode

View File

@ -1,9 +0,0 @@
[common]
server_addr = 127.0.0.1
server_port = 7000
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

View File

@ -1,2 +0,0 @@
[common]
bind_port = 7000

2
go.mod
View File

@ -3,7 +3,6 @@ module github.com/fatedier/frp
go 1.20 go 1.20
require ( require (
github.com/BurntSushi/toml v0.3.1
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/coreos/go-oidc/v3 v3.6.0 github.com/coreos/go-oidc/v3 v3.6.0
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
@ -15,6 +14,7 @@ require (
github.com/hashicorp/yamux v0.1.1 github.com/hashicorp/yamux v0.1.1
github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/ginkgo/v2 v2.11.0
github.com/onsi/gomega v1.27.8 github.com/onsi/gomega v1.27.8
github.com/pelletier/go-toml/v2 v2.1.0
github.com/pion/stun v0.6.1 github.com/pion/stun v0.6.1
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.0
github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_golang v1.16.0

3
go.sum
View File

@ -1,7 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
@ -93,6 +92,8 @@ github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=

View File

@ -6,7 +6,7 @@ ROOT=$(unset CDPATH && cd "$(dirname "$SCRIPT")/.." && pwd)
ginkgo_command=$(which ginkgo 2>/dev/null) ginkgo_command=$(which ginkgo 2>/dev/null)
if [ -z "$ginkgo_command" ]; then if [ -z "$ginkgo_command" ]; then
echo "ginkgo not found, try to install..." echo "ginkgo not found, try to install..."
go install github.com/onsi/ginkgo/v2/ginkgo@v2.8.3 go install github.com/onsi/ginkgo/v2/ginkgo@v2.11.0
fi fi
debug=false debug=false

View File

@ -31,9 +31,9 @@ type Setter interface {
func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter) { func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter) {
switch cfg.Method { switch cfg.Method {
case consts.TokenAuthMethod: case consts.TokenAuthMethod:
authProvider = NewTokenAuth(cfg.AdditionalAuthScopes, cfg.Token) authProvider = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
case consts.OidcAuthMethod: case consts.OidcAuthMethod:
authProvider = NewOidcAuthSetter(cfg.AdditionalAuthScopes, cfg.OIDC) authProvider = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
default: default:
panic(fmt.Sprintf("wrong method: '%s'", cfg.Method)) panic(fmt.Sprintf("wrong method: '%s'", cfg.Method))
} }
@ -49,9 +49,9 @@ type Verifier interface {
func NewAuthVerifier(cfg v1.AuthServerConfig) (authVerifier Verifier) { func NewAuthVerifier(cfg v1.AuthServerConfig) (authVerifier Verifier) {
switch cfg.Method { switch cfg.Method {
case consts.TokenAuthMethod: case consts.TokenAuthMethod:
authVerifier = NewTokenAuth(cfg.AdditionalAuthScopes, cfg.Token) authVerifier = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
case consts.OidcAuthMethod: case consts.OidcAuthMethod:
authVerifier = NewOidcAuthVerifier(cfg.AdditionalAuthScopes, cfg.OIDC) authVerifier = NewOidcAuthVerifier(cfg.AdditionalScopes, cfg.OIDC)
} }
return authVerifier return authVerifier
} }

View File

@ -29,10 +29,10 @@ func Convert_ClientCommonConf_To_v1(conf *ClientCommonConf) *v1.ClientCommonConf
out.Auth.Method = conf.ClientConfig.AuthenticationMethod out.Auth.Method = conf.ClientConfig.AuthenticationMethod
out.Auth.Token = conf.ClientConfig.Token out.Auth.Token = conf.ClientConfig.Token
if conf.ClientConfig.AuthenticateHeartBeats { if conf.ClientConfig.AuthenticateHeartBeats {
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeHeartBeats) out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeHeartBeats)
} }
if conf.ClientConfig.AuthenticateNewWorkConns { if conf.ClientConfig.AuthenticateNewWorkConns {
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeNewWorkConns) out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeNewWorkConns)
} }
out.Auth.OIDC.ClientID = conf.ClientConfig.OidcClientID out.Auth.OIDC.ClientID = conf.ClientConfig.OidcClientID
out.Auth.OIDC.ClientSecret = conf.ClientConfig.OidcClientSecret out.Auth.OIDC.ClientSecret = conf.ClientConfig.OidcClientSecret
@ -89,10 +89,10 @@ func Convert_ServerCommonConf_To_v1(conf *ServerCommonConf) *v1.ServerConfig {
out.Auth.Method = conf.ServerConfig.AuthenticationMethod out.Auth.Method = conf.ServerConfig.AuthenticationMethod
out.Auth.Token = conf.ServerConfig.Token out.Auth.Token = conf.ServerConfig.Token
if conf.ServerConfig.AuthenticateHeartBeats { if conf.ServerConfig.AuthenticateHeartBeats {
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeHeartBeats) out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeHeartBeats)
} }
if conf.ServerConfig.AuthenticateNewWorkConns { if conf.ServerConfig.AuthenticateNewWorkConns {
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeNewWorkConns) out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeNewWorkConns)
} }
out.Auth.OIDC.Audience = conf.ServerConfig.OidcAudience out.Auth.OIDC.Audience = conf.ServerConfig.OidcAudience
out.Auth.OIDC.Issuer = conf.ServerConfig.OidcIssuer out.Auth.OIDC.Issuer = conf.ServerConfig.OidcIssuer
@ -146,12 +146,12 @@ func Convert_ServerCommonConf_To_v1(conf *ServerCommonConf) *v1.ServerConfig {
out.Transport.MaxPoolCount = conf.MaxPoolCount out.Transport.MaxPoolCount = conf.MaxPoolCount
out.Transport.HeartbeatTimeout = conf.HeartbeatTimeout out.Transport.HeartbeatTimeout = conf.HeartbeatTimeout
out.MaxPortsPerClient = conf.MaxPortsPerClient out.Transport.TLS.Force = conf.TLSOnly
out.Transport.TLS.CertFile = conf.TLSCertFile
out.Transport.TLS.KeyFile = conf.TLSKeyFile
out.Transport.TLS.TrustedCaFile = conf.TLSTrustedCaFile
out.TLS.Force = conf.TLSOnly out.MaxPortsPerClient = conf.MaxPortsPerClient
out.TLS.CertFile = conf.TLSCertFile
out.TLS.KeyFile = conf.TLSKeyFile
out.TLS.TrustedCaFile = conf.TLSTrustedCaFile
for _, v := range conf.HTTPPlugins { for _, v := range conf.HTTPPlugins {
out.HTTPPlugins = append(out.HTTPPlugins, v1.HTTPPluginOptions{ out.HTTPPlugins = append(out.HTTPPlugins, v1.HTTPPluginOptions{

View File

@ -23,7 +23,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/BurntSushi/toml" toml "github.com/pelletier/go-toml/v2"
"github.com/samber/lo" "github.com/samber/lo"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
@ -119,7 +119,6 @@ func LoadConfigure(b []byte, c any) error {
return err return err
} }
} }
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(b), 4096) decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(b), 4096)
return decoder.Decode(c) return decoder.Decode(c)
} }

View File

@ -168,7 +168,7 @@ type AuthClientConfig struct {
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
// Specify whether to include auth info in additional scope. // Specify whether to include auth info in additional scope.
// Current supported scopes are: "HeartBeats", "NewWorkConns". // Current supported scopes are: "HeartBeats", "NewWorkConns".
AdditionalAuthScopes []AuthScope `json:"additionalAuthScopes,omitempty"` AdditionalScopes []AuthScope `json:"additionalScopes,omitempty"`
// Token specifies the authorization token used to create keys to be sent // Token specifies the authorization token used to create keys to be sent
// to the server. The server must have a matching token for authorization // to the server. The server must have a matching token for authorization
// to succeed. By default, this value is "". // to succeed. By default, this value is "".

View File

@ -46,10 +46,11 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
if !ok { if !ok {
return fmt.Errorf("unknown plugin type: %s", typeStruct.Type) return fmt.Errorf("unknown plugin type: %s", typeStruct.Type)
} }
if err := json.Unmarshal(b, v); err != nil { options := reflect.New(v).Interface().(ClientPluginOptions)
if err := json.Unmarshal(b, options); err != nil {
return err return err
} }
c.ClientPluginOptions = v c.ClientPluginOptions = options
return nil return nil
} }

View File

@ -76,8 +76,6 @@ type ServerConfig struct {
Transport ServerTransportConfig `json:"transport,omitempty"` Transport ServerTransportConfig `json:"transport,omitempty"`
TLS TLSServerConfig `json:"tls,omitempty"`
// DetailedErrorsToClient defines whether to send the specific error (with // DetailedErrorsToClient defines whether to send the specific error (with
// debug info) to frpc. By default, this value is true. // debug info) to frpc. By default, this value is true.
DetailedErrorsToClient *bool `json:"detailedErrorsToClient,omitempty"` DetailedErrorsToClient *bool `json:"detailedErrorsToClient,omitempty"`
@ -109,9 +107,6 @@ func (c *ServerConfig) Complete() {
if c.ProxyBindAddr == "" { if c.ProxyBindAddr == "" {
c.ProxyBindAddr = c.BindAddr c.ProxyBindAddr = c.BindAddr
} }
if c.TLS.TrustedCaFile != "" {
c.TLS.Force = true
}
if c.WebServer.Port > 0 { if c.WebServer.Port > 0 {
c.WebServer.Addr = util.EmptyOr(c.WebServer.Addr, "0.0.0.0") c.WebServer.Addr = util.EmptyOr(c.WebServer.Addr, "0.0.0.0")
@ -125,10 +120,10 @@ func (c *ServerConfig) Complete() {
} }
type AuthServerConfig struct { type AuthServerConfig struct {
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
AdditionalAuthScopes []AuthScope `json:"additionalAuthScopes,omitempty"` AdditionalScopes []AuthScope `json:"additionalScopes,omitempty"`
Token string `json:"token,omitempty"` Token string `json:"token,omitempty"`
OIDC AuthOIDCServerConfig `json:"oidc,omitempty"` OIDC AuthOIDCServerConfig `json:"oidc,omitempty"`
} }
func (c *AuthServerConfig) Complete() { func (c *AuthServerConfig) Complete() {
@ -171,6 +166,8 @@ type ServerTransportConfig struct {
HeartbeatTimeout int64 `json:"heartbeatTimeout,omitempty"` HeartbeatTimeout int64 `json:"heartbeatTimeout,omitempty"`
// QUIC options. // QUIC options.
QUIC QUICOptions `json:"quic,omitempty"` QUIC QUICOptions `json:"quic,omitempty"`
// TLS specifies TLS settings for the connection from the client.
TLS TLSServerConfig `json:"tls,omitempty"`
} }
func (c *ServerTransportConfig) Complete() { func (c *ServerTransportConfig) Complete() {
@ -180,6 +177,9 @@ func (c *ServerTransportConfig) Complete() {
c.MaxPoolCount = util.EmptyOr(c.MaxPoolCount, 5) c.MaxPoolCount = util.EmptyOr(c.MaxPoolCount, 5)
c.HeartbeatTimeout = util.EmptyOr(c.HeartbeatTimeout, 90) c.HeartbeatTimeout = util.EmptyOr(c.HeartbeatTimeout, 90)
c.QUIC.Complete() c.QUIC.Complete()
if c.TLS.TrustedCaFile != "" {
c.TLS.Force = true
}
} }
type TLSServerConfig struct { type TLSServerConfig struct {

View File

@ -71,7 +71,7 @@ func ValidateAllClientConfig(c *v1.ClientCommonConfig, pxyCfgs []v1.ProxyConfigu
warning, err := ValidateClientCommonConfig(c) warning, err := ValidateClientCommonConfig(c)
warnings = AppendError(warnings, warning) warnings = AppendError(warnings, warning)
if err != nil { if err != nil {
return err, warnings return warnings, err
} }
} }

View File

@ -35,7 +35,7 @@ type VisitorBaseConfig struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
Transport VisitorTransport `json:"transport,omitempty"` Transport VisitorTransport `json:"transport,omitempty"`
SecretKey string `json:"sk,omitempty"` SecretKey string `json:"secretKey,omitempty"`
// if the server user is not set, it defaults to the current user // if the server user is not set, it defaults to the current user
ServerUser string `json:"serverUser,omitempty"` ServerUser string `json:"serverUser,omitempty"`
ServerName string `json:"serverName,omitempty"` ServerName string `json:"serverName,omitempty"`

View File

@ -32,6 +32,9 @@ var creators = make(map[string]CreatorFn)
type CreatorFn func(options v1.ClientPluginOptions) (Plugin, error) type CreatorFn func(options v1.ClientPluginOptions) (Plugin, error)
func Register(name string, fn CreatorFn) { func Register(name string, fn CreatorFn) {
if _, exist := creators[name]; exist {
panic(fmt.Sprintf("plugin [%s] is already registered", name))
}
creators[name] = fn creators[name] = fn
} }

View File

@ -86,7 +86,7 @@ func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
MaxPortsPerClient: svr.cfg.MaxPortsPerClient, MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
HeartBeatTimeout: svr.cfg.Transport.HeartbeatTimeout, HeartBeatTimeout: svr.cfg.Transport.HeartbeatTimeout,
AllowPortsStr: types.PortsRangeSlice(svr.cfg.AllowPorts).String(), AllowPortsStr: types.PortsRangeSlice(svr.cfg.AllowPorts).String(),
TLSOnly: svr.cfg.TLS.Force, TLSOnly: svr.cfg.Transport.TLS.Force,
TotalTrafficIn: serverStats.TotalTrafficIn, TotalTrafficIn: serverStats.TotalTrafficIn,
TotalTrafficOut: serverStats.TotalTrafficOut, TotalTrafficOut: serverStats.TotalTrafficOut,

View File

@ -108,9 +108,9 @@ type Service struct {
func NewService(cfg *v1.ServerConfig) (svr *Service, err error) { func NewService(cfg *v1.ServerConfig) (svr *Service, err error) {
tlsConfig, err := transport.NewServerTLSConfig( tlsConfig, err := transport.NewServerTLSConfig(
cfg.TLS.CertFile, cfg.Transport.TLS.CertFile,
cfg.TLS.KeyFile, cfg.Transport.TLS.KeyFile,
cfg.TLS.TrustedCaFile) cfg.Transport.TLS.TrustedCaFile)
if err != nil { if err != nil {
return return
} }
@ -455,7 +455,7 @@ func (svr *Service) HandleListener(l net.Listener) {
log.Trace("start check TLS connection...") log.Trace("start check TLS connection...")
originConn := c originConn := c
var isTLS, custom bool var isTLS, custom bool
c, isTLS, custom, err = utilnet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.TLS.Force, connReadTimeout) c, isTLS, custom, err = utilnet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.Transport.TLS.Force, connReadTimeout)
if err != nil { if err != nil {
log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err) log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err)
originConn.Close() originConn.Close()

View File

@ -10,10 +10,13 @@ import (
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
// test source // test source
_ "github.com/fatedier/frp/test/e2e/basic"
_ "github.com/fatedier/frp/test/e2e/features"
"github.com/fatedier/frp/test/e2e/framework" "github.com/fatedier/frp/test/e2e/framework"
_ "github.com/fatedier/frp/test/e2e/plugin" _ "github.com/fatedier/frp/test/e2e/legacy/basic"
_ "github.com/fatedier/frp/test/e2e/legacy/features"
_ "github.com/fatedier/frp/test/e2e/legacy/plugin"
_ "github.com/fatedier/frp/test/e2e/v1/basic"
_ "github.com/fatedier/frp/test/e2e/v1/features"
_ "github.com/fatedier/frp/test/e2e/v1/plugin"
) )
// handleFlags sets up all flags and parses the command line. // handleFlags sets up all flags and parses the command line.

View File

@ -19,10 +19,11 @@ var _ = ginkgo.Describe("[Feature: Example]", func() {
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp] [[proxies]]
type = tcp name = "tcp"
local_port = {{ .%s }} type = "tcp"
remote_port = %d localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort) `, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf}) f.RunProcesses([]string{serverConf}, []string{clientConf})

View File

@ -18,12 +18,23 @@ var (
PortClientAdmin string PortClientAdmin string
DefaultServerConfig = ` DefaultServerConfig = `
bindPort = {{ .%s }}
log.level = "trace"
`
DefaultClientConfig = `
serverAddr = "127.0.0.1"
serverPort = {{ .%s }}
log.level = "trace"
`
LegacyDefaultServerConfig = `
[common] [common]
bind_port = {{ .%s }} bind_port = {{ .%s }}
log_level = trace log_level = trace
` `
DefaultClientConfig = ` LegacyDefaultClientConfig = `
[common] [common]
server_addr = 127.0.0.1 server_addr = 127.0.0.1
server_port = {{ .%s }} server_port = {{ .%s }}
@ -34,6 +45,9 @@ var (
func init() { func init() {
PortServerName = port.GenName("Server") PortServerName = port.GenName("Server")
PortClientAdmin = port.GenName("ClientAdmin") PortClientAdmin = port.GenName("ClientAdmin")
LegacyDefaultServerConfig = fmt.Sprintf(LegacyDefaultServerConfig, port.GenName("Server"))
LegacyDefaultClientConfig = fmt.Sprintf(LegacyDefaultClientConfig, port.GenName("Server"))
DefaultServerConfig = fmt.Sprintf(DefaultServerConfig, port.GenName("Server")) DefaultServerConfig = fmt.Sprintf(DefaultServerConfig, port.GenName("Server"))
DefaultClientConfig = fmt.Sprintf(DefaultClientConfig, port.GenName("Server")) DefaultClientConfig = fmt.Sprintf(DefaultClientConfig, port.GenName("Server"))
} }

View File

@ -29,7 +29,10 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i)) path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i))
err = os.WriteFile(path, []byte(outs[i]), 0o666) err = os.WriteFile(path, []byte(outs[i]), 0o666)
ExpectNoError(err) ExpectNoError(err)
flog.Trace("[%s] %s", path, outs[i])
if TestContext.Debug {
flog.Debug("[%s] %s", path, outs[i])
}
p := process.NewWithEnvs(TestContext.FRPServerPath, []string{"-c", path}, f.osEnvs) p := process.NewWithEnvs(TestContext.FRPServerPath, []string{"-c", path}, f.osEnvs)
f.serverConfPaths = append(f.serverConfPaths, path) f.serverConfPaths = append(f.serverConfPaths, path)
@ -46,7 +49,10 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i)) path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i))
err = os.WriteFile(path, []byte(outs[index]), 0o666) err = os.WriteFile(path, []byte(outs[index]), 0o666)
ExpectNoError(err) ExpectNoError(err)
flog.Trace("[%s] %s", path, outs[index])
if TestContext.Debug {
flog.Debug("[%s] %s", path, outs[index])
}
p := process.NewWithEnvs(TestContext.FRPClientPath, []string{"-c", path}, f.osEnvs) p := process.NewWithEnvs(TestContext.FRPClientPath, []string{"-c", path}, f.osEnvs)
f.clientConfPaths = append(f.clientConfPaths, path) f.clientConfPaths = append(f.clientConfPaths, path)

View File

@ -25,8 +25,8 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
for _, t := range types { for _, t := range types {
proxyType := t proxyType := t
ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() { ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
localPortName := "" localPortName := ""
protocol := "tcp" protocol := "tcp"
@ -96,13 +96,13 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
ginkgo.Describe("HTTP", func() { ginkgo.Describe("HTTP", func() {
ginkgo.It("proxy to HTTP server", func() { ginkgo.It("proxy to HTTP server", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
vhost_http_port = %d vhost_http_port = %d
`, vhostHTTPPort) `, vhostHTTPPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
getProxyConf := func(proxyName string, customDomains string, extra string) string { getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
@ -178,14 +178,14 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
ginkgo.Describe("HTTPS", func() { ginkgo.Describe("HTTPS", func() {
ginkgo.It("proxy to HTTPS server", func() { ginkgo.It("proxy to HTTPS server", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
vhostHTTPSPort := f.AllocPort() vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
vhost_https_port = %d vhost_https_port = %d
`, vhostHTTPSPort) `, vhostHTTPSPort)
localPort := f.AllocPort() localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
getProxyConf := func(proxyName string, customDomains string, extra string) string { getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
[%s] [%s]
@ -281,10 +281,10 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
for _, t := range types { for _, t := range types {
proxyType := t proxyType := t
ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() { ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientServerConf := consts.DefaultClientConfig + "\nuser = user1" clientServerConf := consts.LegacyDefaultClientConfig + "\nuser = user1"
clientVisitorConf := consts.DefaultClientConfig + "\nuser = user1" clientVisitorConf := consts.LegacyDefaultClientConfig + "\nuser = user1"
clientUser2VisitorConf := consts.DefaultClientConfig + "\nuser = user2" clientUser2VisitorConf := consts.LegacyDefaultClientConfig + "\nuser = user2"
localPortName := "" localPortName := ""
protocol := "tcp" protocol := "tcp"
@ -439,8 +439,8 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
ginkgo.Describe("TCPMUX", func() { ginkgo.Describe("TCPMUX", func() {
ginkgo.It("Type tcpmux", func() { ginkgo.It("Type tcpmux", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
tcpmuxHTTPConnectPortName := port.GenName("TCPMUX") tcpmuxHTTPConnectPortName := port.GenName("TCPMUX")
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`

View File

@ -18,7 +18,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
f := framework.NewDefaultFramework() f := framework.NewDefaultFramework()
ginkgo.It("Update && Reload API", func() { ginkgo.It("Update && Reload API", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
adminPort := f.AllocPort() adminPort := f.AllocPort()
@ -26,7 +26,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
p2Port := f.AllocPort() p2Port := f.AllocPort()
p3Port := f.AllocPort() p3Port := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
admin_port = %d admin_port = %d
[p1] [p1]
@ -80,10 +80,10 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
}) })
ginkgo.It("healthz", func() { ginkgo.It("healthz", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
dashboardPort := f.AllocPort() dashboardPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
admin_addr = 0.0.0.0 admin_addr = 0.0.0.0
admin_port = %d admin_port = %d
admin_user = admin admin_user = admin
@ -103,11 +103,11 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
}) })
ginkgo.It("stop", func() { ginkgo.It("stop", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
adminPort := f.AllocPort() adminPort := f.AllocPort()
testPort := f.AllocPort() testPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
admin_port = %d admin_port = %d
[test] [test]

View File

@ -33,8 +33,8 @@ func renderBindPortConfig(protocol string) string {
} }
func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) { func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
if configures.clientPrefix != "" { if configures.clientPrefix != "" {
clientConf = configures.clientPrefix clientConf = configures.clientPrefix
} }
@ -64,7 +64,7 @@ func runClientServerTest(f *framework.Framework, configures *generalTestConfigur
clientConfs := []string{clientConf} clientConfs := []string{clientConf}
if configures.client2 != "" { if configures.client2 != "" {
client2Conf := consts.DefaultClientConfig client2Conf := consts.LegacyDefaultClientConfig
if configures.client2Prefix != "" { if configures.client2Prefix != "" {
client2Conf = configures.client2Prefix client2Conf = configures.client2Prefix
} }

View File

@ -15,8 +15,8 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
ginkgo.Describe("Template", func() { ginkgo.Describe("Template", func() {
ginkgo.It("render by env", func() { ginkgo.It("render by env", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
portName := port.GenName("TCP") portName := port.GenName("TCP")
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`

View File

@ -19,7 +19,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
f := framework.NewDefaultFramework() f := framework.NewDefaultFramework()
getDefaultServerConf := func(vhostHTTPPort int) string { getDefaultServerConf := func(vhostHTTPPort int) string {
conf := consts.DefaultServerConfig + ` conf := consts.LegacyDefaultServerConfig + `
vhost_http_port = %d vhost_http_port = %d
` `
return fmt.Sprintf(conf, vhostHTTPPort) return fmt.Sprintf(conf, vhostHTTPPort)
@ -41,7 +41,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
barPort := f.AllocPort() barPort := f.AllocPort()
f.RunServer("", newHTTPServer(barPort, "bar")) f.RunServer("", newHTTPServer(barPort, "bar"))
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[foo] [foo]
type = http type = http
@ -91,7 +91,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
otherPort := f.AllocPort() otherPort := f.AllocPort()
f.RunServer("", newHTTPServer(otherPort, "other")) f.RunServer("", newHTTPServer(otherPort, "other"))
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[foo] [foo]
type = http type = http
@ -142,7 +142,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort) serverConf := getDefaultServerConf(vhostHTTPPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = http type = http
@ -180,7 +180,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort) serverConf := getDefaultServerConf(vhostHTTPPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = http type = http
@ -225,7 +225,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
barPort := f.AllocPort() barPort := f.AllocPort()
f.RunServer("", newHTTPServer(barPort, "bar")) f.RunServer("", newHTTPServer(barPort, "bar"))
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[foo] [foo]
type = http type = http
@ -270,7 +270,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
) )
f.RunServer("", localServer) f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = http type = http
@ -303,7 +303,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
) )
f.RunServer("", localServer) f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = http type = http
@ -352,7 +352,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
f.RunServer("", localServer) f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = http type = http

View File

@ -18,8 +18,8 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
f := framework.NewDefaultFramework() f := framework.NewDefaultFramework()
ginkgo.It("Ports Whitelist", func() { ginkgo.It("Ports Whitelist", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
serverConf += ` serverConf += `
allow_ports = 20000-25000,25002,30000-50000 allow_ports = 20000-25000,25002,30000-50000
@ -81,8 +81,8 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
}) })
ginkgo.It("Alloc Random Port", func() { ginkgo.It("Alloc Random Port", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
adminPort := f.AllocPort() adminPort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
@ -125,13 +125,13 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
}) })
ginkgo.It("Port Reuse", func() { ginkgo.It("Port Reuse", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
// Use same port as PortServer // Use same port as PortServer
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
vhost_http_port = {{ .%s }} vhost_http_port = {{ .%s }}
`, consts.PortServerName) `, consts.PortServerName)
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
[http] [http]
type = http type = http
local_port = {{ .%s }} local_port = {{ .%s }}
@ -146,7 +146,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
}) })
ginkgo.It("healthz", func() { ginkgo.It("healthz", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
dashboardPort := f.AllocPort() dashboardPort := f.AllocPort()
// Use same port as PortServer // Use same port as PortServer
@ -158,7 +158,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
dashboard_pwd = admin dashboard_pwd = admin
`, consts.PortServerName, dashboardPort) `, consts.PortServerName, dashboardPort)
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
[http] [http]
type = http type = http
local_port = {{ .%s }} local_port = {{ .%s }}

View File

@ -20,7 +20,7 @@ var _ = ginkgo.Describe("[Feature: TCPMUX httpconnect]", func() {
f := framework.NewDefaultFramework() f := framework.NewDefaultFramework()
getDefaultServerConf := func(httpconnectPort int) string { getDefaultServerConf := func(httpconnectPort int) string {
conf := consts.DefaultServerConfig + ` conf := consts.LegacyDefaultServerConfig + `
tcpmux_httpconnect_port = %d tcpmux_httpconnect_port = %d
` `
return fmt.Sprintf(conf, httpconnectPort) return fmt.Sprintf(conf, httpconnectPort)
@ -53,7 +53,7 @@ var _ = ginkgo.Describe("[Feature: TCPMUX httpconnect]", func() {
otherPort := f.AllocPort() otherPort := f.AllocPort()
f.RunServer("", newServer(otherPort, "other")) f.RunServer("", newServer(otherPort, "other"))
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[foo] [foo]
type = tcpmux type = tcpmux
@ -110,7 +110,7 @@ var _ = ginkgo.Describe("[Feature: TCPMUX httpconnect]", func() {
fooPort := f.AllocPort() fooPort := f.AllocPort()
f.RunServer("", newServer(fooPort, "foo")) f.RunServer("", newServer(fooPort, "foo"))
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = tcpmux type = tcpmux
@ -195,7 +195,7 @@ var _ = ginkgo.Describe("[Feature: TCPMUX httpconnect]", func() {
localPort := f.AllocPort() localPort := f.AllocPort()
f.RunServer("", newServer(localPort)) f.RunServer("", newServer(localPort))
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = tcpmux type = tcpmux

View File

@ -16,8 +16,8 @@ var _ = ginkgo.Describe("[Feature: XTCP]", func() {
f := framework.NewDefaultFramework() f := framework.NewDefaultFramework()
ginkgo.It("Fallback To STCP", func() { ginkgo.It("Fallback To STCP", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
bindPortName := port.GenName("XTCP") bindPortName := port.GenName("XTCP")
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`

View File

@ -10,17 +10,17 @@ import (
plugin "github.com/fatedier/frp/pkg/plugin/server" plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/fatedier/frp/test/e2e/framework" "github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts" "github.com/fatedier/frp/test/e2e/framework/consts"
plugintest "github.com/fatedier/frp/test/e2e/legacy/plugin"
"github.com/fatedier/frp/test/e2e/mock/server/streamserver" "github.com/fatedier/frp/test/e2e/mock/server/streamserver"
"github.com/fatedier/frp/test/e2e/pkg/request" "github.com/fatedier/frp/test/e2e/pkg/request"
plugintest "github.com/fatedier/frp/test/e2e/plugin"
) )
var _ = ginkgo.Describe("[Feature: Bandwidth Limit]", func() { var _ = ginkgo.Describe("[Feature: Bandwidth Limit]", func() {
f := framework.NewDefaultFramework() f := framework.NewDefaultFramework()
ginkgo.It("Proxy Bandwidth Limit by Client", func() { ginkgo.It("Proxy Bandwidth Limit by Client", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
localPort := f.AllocPort() localPort := f.AllocPort()
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort)) localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort))
@ -69,13 +69,13 @@ var _ = ginkgo.Describe("[Feature: Bandwidth Limit]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
ops = NewProxy ops = NewProxy
`, pluginPort) `, pluginPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
localPort := f.AllocPort() localPort := f.AllocPort()
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort)) localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort))

View File

@ -62,8 +62,8 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
ginkgo.Describe("Load Balancing", func() { ginkgo.Describe("Load Balancing", func() {
ginkgo.It("TCP", func() { ginkgo.It("TCP", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
fooPort := f.AllocPort() fooPort := f.AllocPort()
fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo"))) fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo")))
@ -114,8 +114,8 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
ginkgo.Describe("Health Check", func() { ginkgo.Describe("Health Check", func() {
ginkgo.It("TCP", func() { ginkgo.It("TCP", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
fooPort := f.AllocPort() fooPort := f.AllocPort()
fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo"))) fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo")))
@ -180,10 +180,10 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
ginkgo.It("HTTP", func() { ginkgo.It("HTTP", func() {
vhostPort := f.AllocPort() vhostPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
vhost_http_port = %d vhost_http_port = %d
`, vhostPort) `, vhostPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
fooPort := f.AllocPort() fooPort := f.AllocPort()
fooServer := newHTTPServer(fooPort, "foo") fooServer := newHTTPServer(fooPort, "foo")

View File

@ -18,13 +18,13 @@ var _ = ginkgo.Describe("[Feature: Monitor]", func() {
ginkgo.It("Prometheus metrics", func() { ginkgo.It("Prometheus metrics", func() {
dashboardPort := f.AllocPort() dashboardPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
enable_prometheus = true enable_prometheus = true
dashboard_addr = 0.0.0.0 dashboard_addr = 0.0.0.0
dashboard_port = %d dashboard_port = %d
`, dashboardPort) `, dashboardPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp] [tcp]

View File

@ -23,7 +23,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
ginkgo.It("HTTP X-Forwarded-For", func() { ginkgo.It("HTTP X-Forwarded-For", func() {
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
vhost_http_port = %d vhost_http_port = %d
`, vhostHTTPPort) `, vhostHTTPPort)
@ -36,7 +36,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
) )
f.RunServer("", localServer) f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[test] [test]
type = http type = http
@ -56,8 +56,8 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
ginkgo.Describe("Proxy Protocol", func() { ginkgo.Describe("Proxy Protocol", func() {
ginkgo.It("TCP", func() { ginkgo.It("TCP", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
localPort := f.AllocPort() localPort := f.AllocPort()
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort), localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort),
@ -107,11 +107,11 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
ginkgo.It("HTTP", func() { ginkgo.It("HTTP", func() {
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
vhost_http_port = %d vhost_http_port = %d
`, vhostHTTPPort) `, vhostHTTPPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
localPort := f.AllocPort() localPort := f.AllocPort()
var srcAddrRecord string var srcAddrRecord string

View File

@ -21,8 +21,8 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
ginkgo.Describe("UnixDomainSocket", func() { ginkgo.Describe("UnixDomainSocket", func() {
ginkgo.It("Expose a unix domain socket echo server", func() { ginkgo.It("Expose a unix domain socket echo server", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
getProxyConf := func(proxyName string, portName string, extra string) string { getProxyConf := func(proxyName string, portName string, extra string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
@ -77,8 +77,8 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
}) })
ginkgo.It("http_proxy", func() { ginkgo.It("http_proxy", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
@ -109,8 +109,8 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
}) })
ginkgo.It("socks5 proxy", func() { ginkgo.It("socks5 proxy", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
@ -137,10 +137,10 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
ginkgo.It("static_file", func() { ginkgo.It("static_file", func() {
vhostPort := f.AllocPort() vhostPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
vhost_http_port = %d vhost_http_port = %d
`, vhostPort) `, vhostPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
f.WriteTempFile("test_static_file", "foo") f.WriteTempFile("test_static_file", "foo")
@ -185,14 +185,14 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
}) })
ginkgo.It("http2https", func() { ginkgo.It("http2https", func() {
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
vhost_http_port = %d vhost_http_port = %d
`, vhostHTTPPort) `, vhostHTTPPort)
localPort := f.AllocPort() localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
[http2https] [http2https]
type = http type = http
custom_domains = example.com custom_domains = example.com
@ -227,14 +227,14 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert)) crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert))
keyPath := f.WriteTempFile("server.key", string(artifacts.Key)) keyPath := f.WriteTempFile("server.key", string(artifacts.Key))
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
vhostHTTPSPort := f.AllocPort() vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
vhost_https_port = %d vhost_https_port = %d
`, vhostHTTPSPort) `, vhostHTTPSPort)
localPort := f.AllocPort() localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
[https2http] [https2http]
type = https type = https
custom_domains = example.com custom_domains = example.com
@ -271,14 +271,14 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert)) crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert))
keyPath := f.WriteTempFile("server.key", string(artifacts.Key)) keyPath := f.WriteTempFile("server.key", string(artifacts.Key))
serverConf := consts.DefaultServerConfig serverConf := consts.LegacyDefaultServerConfig
vhostHTTPSPort := f.AllocPort() vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(` serverConf += fmt.Sprintf(`
vhost_https_port = %d vhost_https_port = %d
`, vhostHTTPSPort) `, vhostHTTPSPort)
localPort := f.AllocPort() localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(` clientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
[https2https] [https2https]
type = https type = https
custom_domains = example.com custom_domains = example.com

View File

@ -44,13 +44,13 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.user-manager] [plugin.user-manager]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
ops = Login ops = Login
`, localPort) `, localPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
@ -63,7 +63,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
`, framework.TCPEchoServerPort, remotePort) `, framework.TCPEchoServerPort, remotePort)
remotePort2 := f.AllocPort() remotePort2 := f.AllocPort()
invalidTokenClientConf := consts.DefaultClientConfig + fmt.Sprintf(` invalidTokenClientConf := consts.LegacyDefaultClientConfig + fmt.Sprintf(`
[tcp2] [tcp2]
type = tcp type = tcp
local_port = {{ .%s }} local_port = {{ .%s }}
@ -102,13 +102,13 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
ops = NewProxy ops = NewProxy
`, localPort) `, localPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
@ -137,13 +137,13 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
ops = NewProxy ops = NewProxy
`, localPort) `, localPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp] [tcp]
@ -178,13 +178,13 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
ops = CloseProxy ops = CloseProxy
`, localPort) `, localPort)
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
@ -230,7 +230,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
@ -238,7 +238,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
`, localPort) `, localPort)
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
heartbeat_interval = 1 heartbeat_interval = 1
authenticate_heartbeats = true authenticate_heartbeats = true
@ -280,7 +280,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
@ -288,7 +288,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
`, localPort) `, localPort)
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp] [tcp]
type = tcp type = tcp
@ -325,7 +325,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = 127.0.0.1:%d addr = 127.0.0.1:%d
path = /handler path = /handler
@ -333,7 +333,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
`, localPort) `, localPort)
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp] [tcp]
type = tcp type = tcp
@ -372,7 +372,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f.RunServer("", pluginServer) f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(` serverConf := consts.LegacyDefaultServerConfig + fmt.Sprintf(`
[plugin.test] [plugin.test]
addr = https://127.0.0.1:%d addr = https://127.0.0.1:%d
path = /handler path = /handler
@ -380,7 +380,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
`, localPort) `, localPort)
remotePort := f.AllocPort() remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp] [tcp]
type = tcp type = tcp

524
test/e2e/v1/basic/basic.go Normal file
View File

@ -0,0 +1,524 @@
package basic
import (
"crypto/tls"
"fmt"
"strings"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
"github.com/fatedier/frp/test/e2e/pkg/port"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: Basic]", func() {
f := framework.NewDefaultFramework()
ginkgo.Describe("TCP && UDP", func() {
types := []string{"tcp", "udp"}
for _, t := range types {
proxyType := t
ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
localPortName := ""
protocol := "tcp"
switch proxyType {
case "tcp":
localPortName = framework.TCPEchoServerPort
protocol = "tcp"
case "udp":
localPortName = framework.UDPEchoServerPort
protocol = "udp"
}
getProxyConf := func(proxyName string, portName string, extra string) string {
return fmt.Sprintf(`
[[proxies]]
name = "%s"
type = "%s"
localPort = {{ .%s }}
remotePort = {{ .%s }}
`+extra, proxyName, proxyType, localPortName, portName)
}
tests := []struct {
proxyName string
portName string
extraConfig string
}{
{
proxyName: "normal",
portName: port.GenName("Normal"),
},
{
proxyName: "with-encryption",
portName: port.GenName("WithEncryption"),
extraConfig: "transport.useEncryption = true",
},
{
proxyName: "with-compression",
portName: port.GenName("WithCompression"),
extraConfig: "transport.useCompression = true",
},
{
proxyName: "with-encryption-and-compression",
portName: port.GenName("WithEncryptionAndCompression"),
extraConfig: `
transport.useEncryption = true
transport.useCompression = true
`,
},
}
// build all client config
for _, test := range tests {
clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
}
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests {
framework.NewRequestExpect(f).
Protocol(protocol).
PortName(test.portName).
Explain(test.proxyName).
Ensure()
}
})
}
})
ginkgo.Describe("HTTP", func() {
ginkgo.It("proxy to HTTP server", func() {
serverConf := consts.DefaultServerConfig
vhostHTTPPort := f.AllocPort()
serverConf += fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostHTTPPort)
clientConf := consts.DefaultClientConfig
getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(`
[[proxies]]
name = "%s"
type = "http"
localPort = {{ .%s }}
customDomains = %s
`+extra, proxyName, framework.HTTPSimpleServerPort, customDomains)
}
tests := []struct {
proxyName string
customDomains string
extraConfig string
}{
{
proxyName: "normal",
},
{
proxyName: "with-encryption",
extraConfig: "transport.useEncryption = true",
},
{
proxyName: "with-compression",
extraConfig: "transport.useCompression = true",
},
{
proxyName: "with-encryption-and-compression",
extraConfig: `
transport.useEncryption = true
transport.useCompression = true
`,
},
{
proxyName: "multiple-custom-domains",
customDomains: `["a.example.com", "b.example.com"]`,
},
}
// build all client config
for i, test := range tests {
if tests[i].customDomains == "" {
tests[i].customDomains = fmt.Sprintf(`["%s"]`, test.proxyName+".example.com")
}
clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
}
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests {
for _, domain := range strings.Split(test.customDomains, ",") {
domain = strings.TrimSpace(domain)
domain = strings.TrimLeft(domain, "[\"")
domain = strings.TrimRight(domain, "]\"")
framework.NewRequestExpect(f).
Explain(test.proxyName + "-" + domain).
Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost(domain)
}).
Ensure()
}
}
// not exist host
framework.NewRequestExpect(f).
Explain("not exist host").
Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("not-exist.example.com")
}).
Ensure(framework.ExpectResponseCode(404))
})
})
ginkgo.Describe("HTTPS", func() {
ginkgo.It("proxy to HTTPS server", func() {
serverConf := consts.DefaultServerConfig
vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(`
vhostHTTPSPort = %d
`, vhostHTTPSPort)
localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig
getProxyConf := func(proxyName string, customDomains string, extra string) string {
return fmt.Sprintf(`
[[proxies]]
name = "%s"
type = "https"
localPort = %d
customDomains = %s
`+extra, proxyName, localPort, customDomains)
}
tests := []struct {
proxyName string
customDomains string
extraConfig string
}{
{
proxyName: "normal",
},
{
proxyName: "with-encryption",
extraConfig: "transport.useEncryption = true",
},
{
proxyName: "with-compression",
extraConfig: "transport.useCompression = true",
},
{
proxyName: "with-encryption-and-compression",
extraConfig: `
transport.useEncryption = true
transport.useCompression = true
`,
},
{
proxyName: "multiple-custom-domains",
customDomains: `["a.example.com", "b.example.com"]`,
},
}
// build all client config
for i, test := range tests {
if tests[i].customDomains == "" {
tests[i].customDomains = fmt.Sprintf(`["%s"]`, test.proxyName+".example.com")
}
clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
}
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf})
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
framework.ExpectNoError(err)
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithTLSConfig(tlsConfig),
httpserver.WithResponse([]byte("test")),
)
f.RunServer("", localServer)
for _, test := range tests {
for _, domain := range strings.Split(test.customDomains, ",") {
domain = strings.TrimSpace(domain)
domain = strings.TrimLeft(domain, "[\"")
domain = strings.TrimRight(domain, "]\"")
framework.NewRequestExpect(f).
Explain(test.proxyName + "-" + domain).
Port(vhostHTTPSPort).
RequestModify(func(r *request.Request) {
r.HTTPS().HTTPHost(domain).TLSConfig(&tls.Config{
ServerName: domain,
InsecureSkipVerify: true,
})
}).
ExpectResp([]byte("test")).
Ensure()
}
}
// not exist host
notExistDomain := "not-exist.example.com"
framework.NewRequestExpect(f).
Explain("not exist host").
Port(vhostHTTPSPort).
RequestModify(func(r *request.Request) {
r.HTTPS().HTTPHost(notExistDomain).TLSConfig(&tls.Config{
ServerName: notExistDomain,
InsecureSkipVerify: true,
})
}).
ExpectError(true).
Ensure()
})
})
ginkgo.Describe("STCP && SUDP && XTCP", func() {
types := []string{"stcp", "sudp", "xtcp"}
for _, t := range types {
proxyType := t
ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() {
serverConf := consts.DefaultServerConfig
clientServerConf := consts.DefaultClientConfig + "\nuser = \"user1\""
clientVisitorConf := consts.DefaultClientConfig + "\nuser = \"user1\""
clientUser2VisitorConf := consts.DefaultClientConfig + "\nuser = \"user2\""
localPortName := ""
protocol := "tcp"
switch proxyType {
case "stcp":
localPortName = framework.TCPEchoServerPort
protocol = "tcp"
case "sudp":
localPortName = framework.UDPEchoServerPort
protocol = "udp"
case "xtcp":
localPortName = framework.TCPEchoServerPort
protocol = "tcp"
ginkgo.Skip("stun server is not stable")
}
correctSK := "abc"
wrongSK := "123"
getProxyServerConf := func(proxyName string, extra string) string {
return fmt.Sprintf(`
[[proxies]]
name = "%s"
type = "%s"
secretKey = "%s"
localPort = {{ .%s }}
`+extra, proxyName, proxyType, correctSK, localPortName)
}
getProxyVisitorConf := func(proxyName string, portName, visitorSK, extra string) string {
return fmt.Sprintf(`
[[visitors]]
name = "%s"
type = "%s"
serverName = "%s"
secretKey = "%s"
bindPort = {{ .%s }}
`+extra, proxyName, proxyType, proxyName, visitorSK, portName)
}
tests := []struct {
proxyName string
bindPortName string
visitorSK string
commonExtraConfig string
proxyExtraConfig string
visitorExtraConfig string
expectError bool
deployUser2Client bool
// skipXTCP is used to skip xtcp test case
skipXTCP bool
}{
{
proxyName: "normal",
bindPortName: port.GenName("Normal"),
visitorSK: correctSK,
skipXTCP: true,
},
{
proxyName: "with-encryption",
bindPortName: port.GenName("WithEncryption"),
visitorSK: correctSK,
commonExtraConfig: "transport.useEncryption = true",
skipXTCP: true,
},
{
proxyName: "with-compression",
bindPortName: port.GenName("WithCompression"),
visitorSK: correctSK,
commonExtraConfig: "transport.useCompression = true",
skipXTCP: true,
},
{
proxyName: "with-encryption-and-compression",
bindPortName: port.GenName("WithEncryptionAndCompression"),
visitorSK: correctSK,
commonExtraConfig: `
transport.useEncryption = true
transport.useCompression = true
`,
skipXTCP: true,
},
{
proxyName: "with-error-sk",
bindPortName: port.GenName("WithErrorSK"),
visitorSK: wrongSK,
expectError: true,
},
{
proxyName: "allowed-user",
bindPortName: port.GenName("AllowedUser"),
visitorSK: correctSK,
proxyExtraConfig: `allowUsers = ["another", "user2"]`,
visitorExtraConfig: `serverUser = "user1"`,
deployUser2Client: true,
},
{
proxyName: "not-allowed-user",
bindPortName: port.GenName("NotAllowedUser"),
visitorSK: correctSK,
proxyExtraConfig: `allowUsers = ["invalid"]`,
visitorExtraConfig: `serverUser = "user1"`,
expectError: true,
},
{
proxyName: "allow-all",
bindPortName: port.GenName("AllowAll"),
visitorSK: correctSK,
proxyExtraConfig: `allowUsers = ["*"]`,
visitorExtraConfig: `serverUser = "user1"`,
deployUser2Client: true,
},
}
// build all client config
for _, test := range tests {
clientServerConf += getProxyServerConf(test.proxyName, test.commonExtraConfig+"\n"+test.proxyExtraConfig) + "\n"
}
for _, test := range tests {
config := getProxyVisitorConf(
test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig,
) + "\n"
if test.deployUser2Client {
clientUser2VisitorConf += config
} else {
clientVisitorConf += config
}
}
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientServerConf, clientVisitorConf, clientUser2VisitorConf})
for _, test := range tests {
timeout := time.Second
if t == "xtcp" {
if test.skipXTCP {
continue
}
timeout = 10 * time.Second
}
framework.NewRequestExpect(f).
RequestModify(func(r *request.Request) {
r.Timeout(timeout)
}).
Protocol(protocol).
PortName(test.bindPortName).
Explain(test.proxyName).
ExpectError(test.expectError).
Ensure()
}
})
}
})
ginkgo.Describe("TCPMUX", func() {
ginkgo.It("Type tcpmux", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
tcpmuxHTTPConnectPortName := port.GenName("TCPMUX")
serverConf += fmt.Sprintf(`
tcpmuxHTTPConnectPort = {{ .%s }}
`, tcpmuxHTTPConnectPortName)
getProxyConf := func(proxyName string, extra string) string {
return fmt.Sprintf(`
[[proxies]]
name = "%s"
type = "tcpmux"
multiplexer = "httpconnect"
localPort = {{ .%s }}
customDomains = ["%s"]
`+extra, proxyName, port.GenName(proxyName), proxyName)
}
tests := []struct {
proxyName string
extraConfig string
}{
{
proxyName: "normal",
},
{
proxyName: "with-encryption",
extraConfig: "transport.useEncryption = true",
},
{
proxyName: "with-compression",
extraConfig: "transport.useCompression = true",
},
{
proxyName: "with-encryption-and-compression",
extraConfig: `
transport.useEncryption = true
transport.useCompression = true
`,
},
}
// build all client config
for _, test := range tests {
clientConf += getProxyConf(test.proxyName, test.extraConfig) + "\n"
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName)))
f.RunServer(port.GenName(test.proxyName), localServer)
}
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf})
// Request without HTTP connect should get error
framework.NewRequestExpect(f).
PortName(tcpmuxHTTPConnectPortName).
ExpectError(true).
Explain("request without HTTP connect expect error").
Ensure()
proxyURL := fmt.Sprintf("http://127.0.0.1:%d", f.PortByName(tcpmuxHTTPConnectPortName))
// Request with incorrect connect hostname
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.Addr("invalid").Proxy(proxyURL)
}).ExpectError(true).Explain("request without HTTP connect expect error").Ensure()
// Request with correct connect hostname
for _, test := range tests {
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.Addr(test.proxyName).Proxy(proxyURL)
}).ExpectResp([]byte(test.proxyName)).Explain(test.proxyName).Ensure()
}
})
})
})

136
test/e2e/v1/basic/client.go Normal file
View File

@ -0,0 +1,136 @@
package basic
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/request"
clientsdk "github.com/fatedier/frp/test/e2e/pkg/sdk/client"
)
var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("Update && Reload API", func() {
serverConf := consts.DefaultServerConfig
adminPort := f.AllocPort()
p1Port := f.AllocPort()
p2Port := f.AllocPort()
p3Port := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
webServer.port = %d
[[proxies]]
name = "p1"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
[[proxies]]
name = "p2"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
[[proxies]]
name = "p3"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, adminPort,
framework.TCPEchoServerPort, p1Port,
framework.TCPEchoServerPort, p2Port,
framework.TCPEchoServerPort, p3Port)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(p1Port).Ensure()
framework.NewRequestExpect(f).Port(p2Port).Ensure()
framework.NewRequestExpect(f).Port(p3Port).Ensure()
client := clientsdk.New("127.0.0.1", adminPort)
conf, err := client.GetConfig()
framework.ExpectNoError(err)
newP2Port := f.AllocPort()
// change p2 port and remove p3 proxy
newClientConf := strings.ReplaceAll(conf, strconv.Itoa(p2Port), strconv.Itoa(newP2Port))
p3Index := strings.LastIndex(newClientConf, "[[proxies]]")
if p3Index >= 0 {
newClientConf = newClientConf[:p3Index]
}
err = client.UpdateConfig(newClientConf)
framework.ExpectNoError(err)
err = client.Reload()
framework.ExpectNoError(err)
time.Sleep(time.Second)
framework.NewRequestExpect(f).Port(p1Port).Explain("p1 port").Ensure()
framework.NewRequestExpect(f).Port(p2Port).Explain("original p2 port").ExpectError(true).Ensure()
framework.NewRequestExpect(f).Port(newP2Port).Explain("new p2 port").Ensure()
framework.NewRequestExpect(f).Port(p3Port).Explain("p3 port").ExpectError(true).Ensure()
})
ginkgo.It("healthz", func() {
serverConf := consts.DefaultServerConfig
dashboardPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
webServer.addr = "0.0.0.0"
webServer.port = %d
webServer.user = "admin"
webServer.password = "admin"
`, dashboardPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().HTTPPath("/healthz")
}).Port(dashboardPort).ExpectResp([]byte("")).Ensure()
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().HTTPPath("/")
}).Port(dashboardPort).
Ensure(framework.ExpectResponseCode(401))
})
ginkgo.It("stop", func() {
serverConf := consts.DefaultServerConfig
adminPort := f.AllocPort()
testPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
webServer.port = %d
[[proxies]]
name = "test"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, adminPort, framework.TCPEchoServerPort, testPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(testPort).Ensure()
client := clientsdk.New("127.0.0.1", adminPort)
err := client.Stop()
framework.ExpectNoError(err)
time.Sleep(3 * time.Second)
// frpc stopped so the port is not listened, expect error
framework.NewRequestExpect(f).Port(testPort).ExpectError(true).Ensure()
})
})

View File

@ -0,0 +1,325 @@
package basic
import (
"fmt"
"strings"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/cert"
"github.com/fatedier/frp/test/e2e/pkg/port"
)
type generalTestConfigures struct {
server string
client string
clientPrefix string
client2 string
client2Prefix string
testDelay time.Duration
expectError bool
}
func renderBindPortConfig(protocol string) string {
if protocol == "kcp" {
return fmt.Sprintf(`kcpBindPort = {{ .%s }}`, consts.PortServerName)
} else if protocol == "quic" {
return fmt.Sprintf(`quicBindPort = {{ .%s }}`, consts.PortServerName)
}
return ""
}
func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
if configures.clientPrefix != "" {
clientConf = configures.clientPrefix
}
serverConf += fmt.Sprintf(`
%s
`, configures.server)
tcpPortName := port.GenName("TCP")
udpPortName := port.GenName("UDP")
clientConf += fmt.Sprintf(`
%s
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = {{ .%s }}
[[proxies]]
name = "udp"
type = "udp"
localPort = {{ .%s }}
remotePort = {{ .%s }}
`, configures.client,
framework.TCPEchoServerPort, tcpPortName,
framework.UDPEchoServerPort, udpPortName,
)
clientConfs := []string{clientConf}
if configures.client2 != "" {
client2Conf := consts.DefaultClientConfig
if configures.client2Prefix != "" {
client2Conf = configures.client2Prefix
}
client2Conf += fmt.Sprintf(`
%s
`, configures.client2)
clientConfs = append(clientConfs, client2Conf)
}
f.RunProcesses([]string{serverConf}, clientConfs)
if configures.testDelay > 0 {
time.Sleep(configures.testDelay)
}
framework.NewRequestExpect(f).PortName(tcpPortName).ExpectError(configures.expectError).Explain("tcp proxy").Ensure()
framework.NewRequestExpect(f).Protocol("udp").
PortName(udpPortName).ExpectError(configures.expectError).Explain("udp proxy").Ensure()
}
// defineClientServerTest test a normal tcp and udp proxy with specified TestConfigures.
func defineClientServerTest(desc string, f *framework.Framework, configures *generalTestConfigures) {
ginkgo.It(desc, func() {
runClientServerTest(f, configures)
})
}
var _ = ginkgo.Describe("[Feature: Client-Server]", func() {
f := framework.NewDefaultFramework()
ginkgo.Describe("Protocol", func() {
supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
for _, protocol := range supportProtocols {
configures := &generalTestConfigures{
server: fmt.Sprintf(`
%s
`, renderBindPortConfig(protocol)),
client: fmt.Sprintf(`transport.protocol = "%s"`, protocol),
}
defineClientServerTest(protocol, f, configures)
}
})
// wss is special, it needs to be tested separately.
// frps only supports ws, so there should be a proxy to terminate TLS before frps.
ginkgo.Describe("Protocol wss", func() {
wssPort := f.AllocPort()
configures := &generalTestConfigures{
clientPrefix: fmt.Sprintf(`
serverAddr = "127.0.0.1"
serverPort = %d
loginFailExit = false
transport.protocol = "wss"
log.level = "trace"
`, wssPort),
// Due to the fact that frps cannot directly accept wss connections, we use the https2http plugin of another frpc to terminate TLS.
client2: fmt.Sprintf(`
[[proxies]]
name = "wss2ws"
type = "tcp"
remotePort = %d
[proxies.plugin]
type = "https2http"
localAddr = "127.0.0.1:{{ .%s }}"
`, wssPort, consts.PortServerName),
testDelay: 10 * time.Second,
}
defineClientServerTest("wss", f, configures)
})
ginkgo.Describe("Authentication", func() {
defineClientServerTest("Token Correct", f, &generalTestConfigures{
server: `auth.token = "123456"`,
client: `auth.token = "123456"`,
})
defineClientServerTest("Token Incorrect", f, &generalTestConfigures{
server: `auth.token = "123456"`,
client: `auth.token = "invalid"`,
expectError: true,
})
})
ginkgo.Describe("TLS", func() {
supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
for _, protocol := range supportProtocols {
tmp := protocol
// Since v0.50.0, the default value of tls_enable has been changed to true.
// Therefore, here it needs to be set as false to test the scenario of turning it off.
defineClientServerTest("Disable TLS over "+strings.ToUpper(tmp), f, &generalTestConfigures{
server: fmt.Sprintf(`
%s
`, renderBindPortConfig(protocol)),
client: fmt.Sprintf(`transport.tls.enable = false
transport.protocol = "%s"
`, protocol),
})
}
defineClientServerTest("enable tls force, client with TLS", f, &generalTestConfigures{
server: "transport.tls.force = true",
})
defineClientServerTest("enable tls force, client without TLS", f, &generalTestConfigures{
server: "transport.tls.force = true",
client: "transport.tls.enable = false",
expectError: true,
})
})
ginkgo.Describe("TLS with custom certificate", func() {
supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
var (
caCrtPath string
serverCrtPath, serverKeyPath string
clientCrtPath, clientKeyPath string
)
ginkgo.JustBeforeEach(func() {
generator := &cert.SelfSignedCertGenerator{}
artifacts, err := generator.Generate("127.0.0.1")
framework.ExpectNoError(err)
caCrtPath = f.WriteTempFile("ca.crt", string(artifacts.CACert))
serverCrtPath = f.WriteTempFile("server.crt", string(artifacts.Cert))
serverKeyPath = f.WriteTempFile("server.key", string(artifacts.Key))
generator.SetCA(artifacts.CACert, artifacts.CAKey)
_, err = generator.Generate("127.0.0.1")
framework.ExpectNoError(err)
clientCrtPath = f.WriteTempFile("client.crt", string(artifacts.Cert))
clientKeyPath = f.WriteTempFile("client.key", string(artifacts.Key))
})
for _, protocol := range supportProtocols {
tmp := protocol
ginkgo.It("one-way authentication: "+tmp, func() {
runClientServerTest(f, &generalTestConfigures{
server: fmt.Sprintf(`
%s
transport.tls.trustedCaFile = "%s"
`, renderBindPortConfig(tmp), caCrtPath),
client: fmt.Sprintf(`
transport.protocol = "%s"
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
`, tmp, clientCrtPath, clientKeyPath),
})
})
ginkgo.It("mutual authentication: "+tmp, func() {
runClientServerTest(f, &generalTestConfigures{
server: fmt.Sprintf(`
%s
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
transport.tls.trustedCaFile = "%s"
`, renderBindPortConfig(tmp), serverCrtPath, serverKeyPath, caCrtPath),
client: fmt.Sprintf(`
transport.protocol = "%s"
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
transport.tls.trustedCaFile = "%s"
`, tmp, clientCrtPath, clientKeyPath, caCrtPath),
})
})
}
})
ginkgo.Describe("TLS with custom certificate and specified server name", func() {
var (
caCrtPath string
serverCrtPath, serverKeyPath string
clientCrtPath, clientKeyPath string
)
ginkgo.JustBeforeEach(func() {
generator := &cert.SelfSignedCertGenerator{}
artifacts, err := generator.Generate("example.com")
framework.ExpectNoError(err)
caCrtPath = f.WriteTempFile("ca.crt", string(artifacts.CACert))
serverCrtPath = f.WriteTempFile("server.crt", string(artifacts.Cert))
serverKeyPath = f.WriteTempFile("server.key", string(artifacts.Key))
generator.SetCA(artifacts.CACert, artifacts.CAKey)
_, err = generator.Generate("example.com")
framework.ExpectNoError(err)
clientCrtPath = f.WriteTempFile("client.crt", string(artifacts.Cert))
clientKeyPath = f.WriteTempFile("client.key", string(artifacts.Key))
})
ginkgo.It("mutual authentication", func() {
runClientServerTest(f, &generalTestConfigures{
server: fmt.Sprintf(`
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
transport.tls.trustedCaFile = "%s"
`, serverCrtPath, serverKeyPath, caCrtPath),
client: fmt.Sprintf(`
transport.tls.serverName = "example.com"
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
transport.tls.trustedCaFile = "%s"
`, clientCrtPath, clientKeyPath, caCrtPath),
})
})
ginkgo.It("mutual authentication with incorrect server name", func() {
runClientServerTest(f, &generalTestConfigures{
server: fmt.Sprintf(`
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
transport.tls.trustedCaFile = "%s"
`, serverCrtPath, serverKeyPath, caCrtPath),
client: fmt.Sprintf(`
transport.tls.serverName = "invalid.com"
transport.tls.certFile = "%s"
transport.tls.keyFile = "%s"
transport.tls.trustedCaFile = "%s"
`, clientCrtPath, clientKeyPath, caCrtPath),
expectError: true,
})
})
})
ginkgo.Describe("TLS with disable_custom_tls_first_byte set to false", func() {
supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
for _, protocol := range supportProtocols {
tmp := protocol
defineClientServerTest("TLS over "+strings.ToUpper(tmp), f, &generalTestConfigures{
server: fmt.Sprintf(`
%s
`, renderBindPortConfig(protocol)),
client: fmt.Sprintf(`
transport.protocol = "%s"
transport.tls.disableCustomTLSFirstByte = false
`, protocol),
})
}
})
ginkgo.Describe("IPv6 bind address", func() {
supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
for _, protocol := range supportProtocols {
tmp := protocol
defineClientServerTest("IPv6 bind address: "+strings.ToUpper(tmp), f, &generalTestConfigures{
server: fmt.Sprintf(`
bindAddr = "::"
%s
`, renderBindPortConfig(protocol)),
client: fmt.Sprintf(`
transport.protocol = "%s"
`, protocol),
})
}
})
})

109
test/e2e/v1/basic/cmd.go Normal file
View File

@ -0,0 +1,109 @@
package basic
import (
"fmt"
"strconv"
"strings"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
const (
ConfigValidStr = "syntax is ok"
)
var _ = ginkgo.Describe("[Feature: Cmd]", func() {
f := framework.NewDefaultFramework()
ginkgo.Describe("Verify", func() {
ginkgo.It("frps valid", func() {
path := f.GenerateConfigFile(`
bindAddr = "0.0.0.0"
bindPort = 7000
`)
_, output, err := f.RunFrps("verify", "-c", path)
framework.ExpectNoError(err)
framework.ExpectTrue(strings.Contains(output, ConfigValidStr), "output: %s", output)
})
ginkgo.It("frps invalid", func() {
path := f.GenerateConfigFile(`
bindAddr = "0.0.0.0"
bindPort = 70000
`)
_, output, err := f.RunFrps("verify", "-c", path)
framework.ExpectNoError(err)
framework.ExpectTrue(!strings.Contains(output, ConfigValidStr), "output: %s", output)
})
ginkgo.It("frpc valid", func() {
path := f.GenerateConfigFile(`
serverAddr = "0.0.0.0"
serverPort = 7000
`)
_, output, err := f.RunFrpc("verify", "-c", path)
framework.ExpectNoError(err)
framework.ExpectTrue(strings.Contains(output, ConfigValidStr), "output: %s", output)
})
ginkgo.It("frpc invalid", func() {
path := f.GenerateConfigFile(`
serverAddr = "0.0.0.0"
serverPort = 7000
transport.protocol = "invalid"
`)
_, output, err := f.RunFrpc("verify", "-c", path)
framework.ExpectNoError(err)
framework.ExpectTrue(!strings.Contains(output, ConfigValidStr), "output: %s", output)
})
})
ginkgo.Describe("Single proxy", func() {
ginkgo.It("TCP", func() {
serverPort := f.AllocPort()
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort))
framework.ExpectNoError(err)
localPort := f.PortByName(framework.TCPEchoServerPort)
remotePort := f.AllocPort()
_, _, err = f.RunFrpc("tcp", "-s", fmt.Sprintf("127.0.0.1:%d", serverPort), "-t", "123", "-u", "test",
"-l", strconv.Itoa(localPort), "-r", strconv.Itoa(remotePort), "-n", "tcp_test")
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Port(remotePort).Ensure()
})
ginkgo.It("UDP", func() {
serverPort := f.AllocPort()
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort))
framework.ExpectNoError(err)
localPort := f.PortByName(framework.UDPEchoServerPort)
remotePort := f.AllocPort()
_, _, err = f.RunFrpc("udp", "-s", fmt.Sprintf("127.0.0.1:%d", serverPort), "-t", "123", "-u", "test",
"-l", strconv.Itoa(localPort), "-r", strconv.Itoa(remotePort), "-n", "udp_test")
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Protocol("udp").
Port(remotePort).Ensure()
})
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))
framework.ExpectNoError(err)
_, _, err = f.RunFrpc("http", "-s", "127.0.0.1:"+strconv.Itoa(serverPort), "-t", "123", "-u", "test",
"-n", "udp_test", "-l", strconv.Itoa(f.PortByName(framework.HTTPSimpleServerPort)),
"--custom_domain", "test.example.com")
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("test.example.com")
}).
Ensure()
})
})
})

View File

@ -0,0 +1,84 @@
package basic
import (
"fmt"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port"
)
var _ = ginkgo.Describe("[Feature: Config]", func() {
f := framework.NewDefaultFramework()
ginkgo.Describe("Template", func() {
ginkgo.It("render by env", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
portName := port.GenName("TCP")
serverConf += fmt.Sprintf(`
auth.token = "{{ %s{{ .Envs.FRP_TOKEN }}%s }}"
`, "`", "`")
clientConf += fmt.Sprintf(`
auth.token = "{{ %s{{ .Envs.FRP_TOKEN }}%s }}"
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = {{ .%s }}
`, "`", "`", framework.TCPEchoServerPort, portName)
f.SetEnvs([]string{"FRP_TOKEN=123"})
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).PortName(portName).Ensure()
})
})
ginkgo.Describe("Includes", func() {
ginkgo.It("split tcp proxies into different files", func() {
serverPort := f.AllocPort()
serverConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
bindAddr = "0.0.0.0"
bindPort = %d
`, serverPort))
remotePort := f.AllocPort()
proxyConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = %d
remotePort = %d
`, f.PortByName(framework.TCPEchoServerPort), remotePort))
remotePort2 := f.AllocPort()
proxyConfigPath2 := f.GenerateConfigFile(fmt.Sprintf(`
[[proxies]]
name = "tcp2"
type = "tcp"
localPort = %d
remotePort = %d
`, f.PortByName(framework.TCPEchoServerPort), remotePort2))
clientConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
serverPort = %d
includes = ["%s","%s"]
`, serverPort, proxyConfigPath, proxyConfigPath2))
_, _, err := f.RunFrps("-c", serverConfigPath)
framework.ExpectNoError(err)
_, _, err = f.RunFrpc("-c", clientConfigPath)
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Port(remotePort).Ensure()
framework.NewRequestExpect(f).Port(remotePort2).Ensure()
})
})
})

388
test/e2e/v1/basic/http.go Normal file
View File

@ -0,0 +1,388 @@
package basic
import (
"fmt"
"net/http"
"net/url"
"strconv"
"github.com/gorilla/websocket"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: HTTP]", func() {
f := framework.NewDefaultFramework()
getDefaultServerConf := func(vhostHTTPPort int) string {
conf := consts.DefaultServerConfig + `
vhostHTTPPort = %d
`
return fmt.Sprintf(conf, vhostHTTPPort)
}
newHTTPServer := func(port int, respContent string) *httpserver.Server {
return httpserver.New(
httpserver.WithBindPort(port),
httpserver.WithHandler(framework.SpecifiedHTTPBodyHandler([]byte(respContent))),
)
}
ginkgo.It("HTTP route by locations", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
fooPort := f.AllocPort()
f.RunServer("", newHTTPServer(fooPort, "foo"))
barPort := f.AllocPort()
f.RunServer("", newHTTPServer(barPort, "bar"))
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
locations = ["/","/foo"]
[[proxies]]
name = "bar"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
locations = ["/bar"]
`, fooPort, barPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
tests := []struct {
path string
expectResp string
desc string
}{
{path: "/foo", expectResp: "foo", desc: "foo path"},
{path: "/bar", expectResp: "bar", desc: "bar path"},
{path: "/other", expectResp: "foo", desc: "other path"},
}
for _, test := range tests {
framework.NewRequestExpect(f).Explain(test.desc).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com").HTTPPath(test.path)
}).
ExpectResp([]byte(test.expectResp)).
Ensure()
}
})
ginkgo.It("HTTP route by HTTP user", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
fooPort := f.AllocPort()
f.RunServer("", newHTTPServer(fooPort, "foo"))
barPort := f.AllocPort()
f.RunServer("", newHTTPServer(barPort, "bar"))
otherPort := f.AllocPort()
f.RunServer("", newHTTPServer(otherPort, "other"))
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
routeByHTTPUser = "user1"
[[proxies]]
name = "bar"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
routeByHTTPUser = "user2"
[[proxies]]
name = "catchAll"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
`, fooPort, barPort, otherPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// user1
framework.NewRequestExpect(f).Explain("user1").Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user1", "")
}).
ExpectResp([]byte("foo")).
Ensure()
// user2
framework.NewRequestExpect(f).Explain("user2").Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user2", "")
}).
ExpectResp([]byte("bar")).
Ensure()
// other user
framework.NewRequestExpect(f).Explain("other user").Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user3", "")
}).
ExpectResp([]byte("other")).
Ensure()
})
ginkgo.It("HTTP Basic Auth", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = {{ .%s }}
customDomains = ["normal.example.com"]
httpUser = "test"
httpPassword = "test"
`, framework.HTTPSimpleServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// not set auth header
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com")
}).
Ensure(framework.ExpectResponseCode(401))
// set incorrect auth header
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("test", "invalid")
}).
Ensure(framework.ExpectResponseCode(401))
// set correct auth header
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("test", "test")
}).
Ensure()
})
ginkgo.It("Wildcard domain", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = {{ .%s }}
customDomains = ["*.example.com"]
`, framework.HTTPSimpleServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// not match host
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("not-match.test.com")
}).
Ensure(framework.ExpectResponseCode(404))
// test.example.com match *.example.com
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("test.example.com")
}).
Ensure()
// sub.test.example.com match *.example.com
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("sub.test.example.com")
}).
Ensure()
})
ginkgo.It("Subdomain", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
serverConf += `
subdomainHost = "example.com"
`
fooPort := f.AllocPort()
f.RunServer("", newHTTPServer(fooPort, "foo"))
barPort := f.AllocPort()
f.RunServer("", newHTTPServer(barPort, "bar"))
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "http"
localPort = %d
subdomain = "foo"
[[proxies]]
name = "bar"
type = "http"
localPort = %d
subdomain = "bar"
`, fooPort, barPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// foo
framework.NewRequestExpect(f).Explain("foo subdomain").Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("foo.example.com")
}).
ExpectResp([]byte("foo")).
Ensure()
// bar
framework.NewRequestExpect(f).Explain("bar subdomain").Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("bar.example.com")
}).
ExpectResp([]byte("bar")).
Ensure()
})
ginkgo.It("Modify headers", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
localPort := f.AllocPort()
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte(req.Header.Get("X-From-Where")))
})),
)
f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
requestHeaders.set.x-from-where = "frp"
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// not set auth header
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com")
}).
ExpectResp([]byte("frp")). // local http server will write this X-From-Where header to response body
Ensure()
})
ginkgo.It("Host Header Rewrite", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
localPort := f.AllocPort()
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte(req.Host))
})),
)
f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
hostHeaderRewrite = "rewrite.example.com"
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com")
}).
ExpectResp([]byte("rewrite.example.com")). // local http server will write host header to response body
Ensure()
})
ginkgo.It("Websocket protocol", func() {
vhostHTTPPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostHTTPPort)
upgrader := websocket.Upgrader{}
localPort := f.AllocPort()
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
c, err := upgrader.Upgrade(w, req, nil)
if err != nil {
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
if err != nil {
break
}
err = c.WriteMessage(mt, message)
if err != nil {
break
}
}
})),
)
f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = %d
customDomains = ["127.0.0.1"]
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
u := url.URL{Scheme: "ws", Host: "127.0.0.1:" + strconv.Itoa(vhostHTTPPort)}
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
framework.ExpectNoError(err)
err = c.WriteMessage(websocket.TextMessage, []byte(consts.TestString))
framework.ExpectNoError(err)
_, msg, err := c.ReadMessage()
framework.ExpectNoError(err)
framework.ExpectEqualValues(consts.TestString, string(msg))
})
})

192
test/e2e/v1/basic/server.go Normal file
View File

@ -0,0 +1,192 @@
package basic
import (
"fmt"
"net"
"strconv"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port"
"github.com/fatedier/frp/test/e2e/pkg/request"
clientsdk "github.com/fatedier/frp/test/e2e/pkg/sdk/client"
)
var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("Ports Whitelist", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
serverConf += `
allowPorts = [
{ start = 20000, end = 25000 },
{ single = 25002 },
{ start = 30000, end = 50000 },
]
`
tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000))
udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000))
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp-allowded-in-range"
type = "tcp"
localPort = {{ .%s }}
remotePort = {{ .%s }}
`, framework.TCPEchoServerPort, tcpPortName)
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp-port-not-allowed"
type = "tcp"
localPort = {{ .%s }}
remotePort = 25001
`, framework.TCPEchoServerPort)
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp-port-unavailable"
type = "tcp"
localPort = {{ .%s }}
remotePort = {{ .%s }}
`, framework.TCPEchoServerPort, consts.PortServerName)
clientConf += fmt.Sprintf(`
[[proxies]]
name = "udp-allowed-in-range"
type = "udp"
localPort = {{ .%s }}
remotePort = {{ .%s }}
`, framework.UDPEchoServerPort, udpPortName)
clientConf += fmt.Sprintf(`
[[proxies]]
name = "udp-port-not-allowed"
type = "udp"
localPort = {{ .%s }}
remotePort = 25003
`, framework.UDPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// TCP
// Allowed in range
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
// Not Allowed
framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure()
// Unavailable, already bind by frps
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
// UDP
// Allowed in range
framework.NewRequestExpect(f).Protocol("udp").PortName(udpPortName).Ensure()
// Not Allowed
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.UDP().Port(25003)
}).ExpectError(true).Ensure()
})
ginkgo.It("Alloc Random Port", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
adminPort := f.AllocPort()
clientConf += fmt.Sprintf(`
webServer.port = %d
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
[[proxies]]
name = "udp"
type = "udp"
localPort = {{ .%s }}
`, adminPort, framework.TCPEchoServerPort, framework.UDPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
client := clientsdk.New("127.0.0.1", adminPort)
// tcp random port
status, err := client.GetProxyStatus("tcp")
framework.ExpectNoError(err)
_, portStr, err := net.SplitHostPort(status.RemoteAddr)
framework.ExpectNoError(err)
port, err := strconv.Atoi(portStr)
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Port(port).Ensure()
// udp random port
status, err = client.GetProxyStatus("udp")
framework.ExpectNoError(err)
_, portStr, err = net.SplitHostPort(status.RemoteAddr)
framework.ExpectNoError(err)
port, err = strconv.Atoi(portStr)
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Protocol("udp").Port(port).Ensure()
})
ginkgo.It("Port Reuse", func() {
serverConf := consts.DefaultServerConfig
// Use same port as PortServer
serverConf += fmt.Sprintf(`
vhostHTTPPort = {{ .%s }}
`, consts.PortServerName)
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "http"
type = "http"
localPort = {{ .%s }}
customDomains = ["example.com"]
`, framework.HTTPSimpleServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("example.com")
}).PortName(consts.PortServerName).Ensure()
})
ginkgo.It("healthz", func() {
serverConf := consts.DefaultServerConfig
dashboardPort := f.AllocPort()
// Use same port as PortServer
serverConf += fmt.Sprintf(`
vhostHTTPPort = {{ .%s }}
webServer.addr = "0.0.0.0"
webServer.port = %d
webServer.user = "admin"
webServer.password = "admin"
`, consts.PortServerName, dashboardPort)
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "http"
type = "http"
localPort = {{ .%s }}
customDomains = ["example.com"]
`, framework.HTTPSimpleServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().HTTPPath("/healthz")
}).Port(dashboardPort).ExpectResp([]byte("")).Ensure()
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().HTTPPath("/")
}).Port(dashboardPort).
Ensure(framework.ExpectResponseCode(401))
})
})

223
test/e2e/v1/basic/tcpmux.go Normal file
View File

@ -0,0 +1,223 @@
package basic
import (
"bufio"
"fmt"
"net"
"net/http"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
"github.com/fatedier/frp/test/e2e/pkg/request"
"github.com/fatedier/frp/test/e2e/pkg/rpc"
)
var _ = ginkgo.Describe("[Feature: TCPMUX httpconnect]", func() {
f := framework.NewDefaultFramework()
getDefaultServerConf := func(httpconnectPort int) string {
conf := consts.DefaultServerConfig + `
tcpmuxHTTPConnectPort = %d
`
return fmt.Sprintf(conf, httpconnectPort)
}
newServer := func(port int, respContent string) *streamserver.Server {
return streamserver.New(
streamserver.TCP,
streamserver.WithBindPort(port),
streamserver.WithRespContent([]byte(respContent)),
)
}
proxyURLWithAuth := func(username, password string, port int) string {
if username == "" {
return fmt.Sprintf("http://127.0.0.1:%d", port)
}
return fmt.Sprintf("http://%s:%s@127.0.0.1:%d", username, password, port)
}
ginkgo.It("Route by HTTP user", func() {
vhostPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostPort)
fooPort := f.AllocPort()
f.RunServer("", newServer(fooPort, "foo"))
barPort := f.AllocPort()
f.RunServer("", newServer(barPort, "bar"))
otherPort := f.AllocPort()
f.RunServer("", newServer(otherPort, "other"))
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "tcpmux"
multiplexer = "httpconnect"
localPort = %d
customDomains = ["normal.example.com"]
routeByHTTPUser = "user1"
[[proxies]]
name = "bar"
type = "tcpmux"
multiplexer = "httpconnect"
localPort = %d
customDomains = ["normal.example.com"]
routeByHTTPUser = "user2"
[[proxies]]
name = "catchAll"
type = "tcpmux"
multiplexer = "httpconnect"
localPort = %d
customDomains = ["normal.example.com"]
`, fooPort, barPort, otherPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// user1
framework.NewRequestExpect(f).Explain("user1").
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("user1", "", vhostPort))
}).
ExpectResp([]byte("foo")).
Ensure()
// user2
framework.NewRequestExpect(f).Explain("user2").
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("user2", "", vhostPort))
}).
ExpectResp([]byte("bar")).
Ensure()
// other user
framework.NewRequestExpect(f).Explain("other user").
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("user3", "", vhostPort))
}).
ExpectResp([]byte("other")).
Ensure()
})
ginkgo.It("Proxy auth", func() {
vhostPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostPort)
fooPort := f.AllocPort()
f.RunServer("", newServer(fooPort, "foo"))
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "tcpmux"
multiplexer = "httpconnect"
localPort = %d
customDomains = ["normal.example.com"]
httpUser = "test"
httpPassword = "test"
`, fooPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// not set auth header
framework.NewRequestExpect(f).Explain("no auth").
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("", "", vhostPort))
}).
ExpectError(true).
Ensure()
// set incorrect auth header
framework.NewRequestExpect(f).Explain("incorrect auth").
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("test", "invalid", vhostPort))
}).
ExpectError(true).
Ensure()
// set correct auth header
framework.NewRequestExpect(f).Explain("correct auth").
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("test", "test", vhostPort))
}).
ExpectResp([]byte("foo")).
Ensure()
})
ginkgo.It("TCPMux Passthrough", func() {
vhostPort := f.AllocPort()
serverConf := getDefaultServerConf(vhostPort)
serverConf += `
tcpmuxPassthrough = true
`
var (
respErr error
connectRequestHost string
)
newServer := func(port int) *streamserver.Server {
return streamserver.New(
streamserver.TCP,
streamserver.WithBindPort(port),
streamserver.WithCustomHandler(func(conn net.Conn) {
defer conn.Close()
// read HTTP CONNECT request
bufioReader := bufio.NewReader(conn)
req, err := http.ReadRequest(bufioReader)
if err != nil {
respErr = err
return
}
connectRequestHost = req.Host
// return ok response
res := util.OkResponse()
if res.Body != nil {
defer res.Body.Close()
}
_ = res.Write(conn)
buf, err := rpc.ReadBytes(conn)
if err != nil {
respErr = err
return
}
_, _ = rpc.WriteBytes(conn, buf)
}),
)
}
localPort := f.AllocPort()
f.RunServer("", newServer(localPort))
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "tcpmux"
multiplexer = "httpconnect"
localPort = %d
customDomains = ["normal.example.com"]
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).
RequestModify(func(r *request.Request) {
r.Addr("normal.example.com").Proxy(proxyURLWithAuth("", "", vhostPort)).Body([]byte("frp"))
}).
ExpectResp([]byte("frp")).
Ensure()
framework.ExpectNoError(respErr)
framework.ExpectEqualValues(connectRequestHost, "normal.example.com")
})
})

53
test/e2e/v1/basic/xtcp.go Normal file
View File

@ -0,0 +1,53 @@
package basic
import (
"fmt"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: XTCP]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("Fallback To STCP", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
bindPortName := port.GenName("XTCP")
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "stcp"
localPort = {{ .%s }}
[[visitors]]
name = "foo-visitor"
type = "stcp"
serverName = "foo"
bindPort = -1
[[visitors]]
name = "bar-visitor"
type = "xtcp"
serverName = "bar"
bindPort = {{ .%s }}
keepTunnelOpen = true
fallbackTo = "foo-visitor"
fallbackTimeoutMs = 200
`, framework.TCPEchoServerPort, bindPortName)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).
RequestModify(func(r *request.Request) {
r.Timeout(time.Second)
}).
PortName(bindPortName).
Ensure()
})
})

View File

@ -0,0 +1,108 @@
package features
import (
"fmt"
"strings"
"time"
"github.com/onsi/ginkgo/v2"
plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
plugintest "github.com/fatedier/frp/test/e2e/legacy/plugin"
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: Bandwidth Limit]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("Proxy Bandwidth Limit by Client", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
localPort := f.AllocPort()
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort))
f.RunServer("", localServer)
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = %d
remotePort = %d
transport.bandwidthLimit = "10KB"
`, localPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
content := strings.Repeat("a", 50*1024) // 5KB
start := time.Now()
framework.NewRequestExpect(f).Port(remotePort).RequestModify(func(r *request.Request) {
r.Body([]byte(content)).Timeout(30 * time.Second)
}).ExpectResp([]byte(content)).Ensure()
duration := time.Since(start)
framework.Logf("request duration: %s", duration.String())
framework.ExpectTrue(duration.Seconds() > 8, "100Kb with 10KB limit, want > 8 seconds, but got %s", duration.String())
})
ginkgo.It("Proxy Bandwidth Limit by Server", func() {
// new test plugin server
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.NewProxyContent{}
return &r
}
pluginPort := f.AllocPort()
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.NewProxyContent)
content.BandwidthLimit = "10KB"
content.BandwidthLimitMode = "server"
ret.Content = content
return &ret
}
pluginServer := plugintest.NewHTTPPluginServer(pluginPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["NewProxy"]
`, pluginPort)
clientConf := consts.DefaultClientConfig
localPort := f.AllocPort()
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort))
f.RunServer("", localServer)
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = %d
remotePort = %d
`, localPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
content := strings.Repeat("a", 50*1024) // 5KB
start := time.Now()
framework.NewRequestExpect(f).Port(remotePort).RequestModify(func(r *request.Request) {
r.Body([]byte(content)).Timeout(30 * time.Second)
}).ExpectResp([]byte(content)).Ensure()
duration := time.Since(start)
framework.Logf("request duration: %s", duration.String())
framework.ExpectTrue(duration.Seconds() > 8, "100Kb with 10KB limit, want > 8 seconds, but got %s", duration.String())
})
})

View File

@ -0,0 +1,64 @@
package features
import (
"fmt"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
)
var _ = ginkgo.Describe("[Feature: Chaos]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("reconnect after frps restart", func() {
serverPort := f.AllocPort()
serverConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
bindAddr = "0.0.0.0"
bindPort = %d
`, serverPort))
remotePort := f.AllocPort()
clientConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
serverPort = %d
log.level = "trace"
[[proxies]]
name = "tcp"
type = "tcp"
localPort = %d
remotePort = %d
`, serverPort, f.PortByName(framework.TCPEchoServerPort), remotePort))
// 1. start frps and frpc, expect request success
ps, _, err := f.RunFrps("-c", serverConfigPath)
framework.ExpectNoError(err)
pc, _, err := f.RunFrpc("-c", clientConfigPath)
framework.ExpectNoError(err)
framework.NewRequestExpect(f).Port(remotePort).Ensure()
// 2. stop frps, expect request failed
_ = ps.Stop()
time.Sleep(200 * time.Millisecond)
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
// 3. restart frps, expect request success
_, _, err = f.RunFrps("-c", serverConfigPath)
framework.ExpectNoError(err)
time.Sleep(2 * time.Second)
framework.NewRequestExpect(f).Port(remotePort).Ensure()
// 4. stop frpc, expect request failed
_ = pc.Stop()
time.Sleep(200 * time.Millisecond)
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
// 5. restart frpc, expect request success
_, _, err = f.RunFrpc("-c", clientConfigPath)
framework.ExpectNoError(err)
time.Sleep(time.Second)
framework.NewRequestExpect(f).Port(remotePort).Ensure()
})
})

View File

@ -0,0 +1,267 @@
package features
import (
"fmt"
"strconv"
"sync"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: Group]", func() {
f := framework.NewDefaultFramework()
newHTTPServer := func(port int, respContent string) *httpserver.Server {
return httpserver.New(
httpserver.WithBindPort(port),
httpserver.WithHandler(framework.SpecifiedHTTPBodyHandler([]byte(respContent))),
)
}
validateFooBarResponse := func(resp *request.Response) bool {
if string(resp.Content) == "foo" || string(resp.Content) == "bar" {
return true
}
return false
}
doFooBarHTTPRequest := func(vhostPort int, host string) []string {
results := []string{}
var wait sync.WaitGroup
var mu sync.Mutex
expectFn := func() {
framework.NewRequestExpect(f).Port(vhostPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost(host)
}).
Ensure(validateFooBarResponse, func(resp *request.Response) bool {
mu.Lock()
defer mu.Unlock()
results = append(results, string(resp.Content))
return true
})
}
for i := 0; i < 10; i++ {
wait.Add(1)
go func() {
defer wait.Done()
expectFn()
}()
}
wait.Wait()
return results
}
ginkgo.Describe("Load Balancing", func() {
ginkgo.It("TCP", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
fooPort := f.AllocPort()
fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo")))
f.RunServer("", fooServer)
barPort := f.AllocPort()
barServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(barPort), streamserver.WithRespContent([]byte("bar")))
f.RunServer("", barServer)
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "tcp"
localPort = %d
remotePort = %d
loadBalancer.group = "test"
loadBalancer.groupKey = "123"
[[proxies]]
name = "bar"
type = "tcp"
localPort = %d
remotePort = %d
loadBalancer.group = "test"
loadBalancer.groupKey = "123"
`, fooPort, remotePort, barPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
fooCount := 0
barCount := 0
for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Explain("times " + strconv.Itoa(i)).Port(remotePort).Ensure(func(resp *request.Response) bool {
switch string(resp.Content) {
case "foo":
fooCount++
case "bar":
barCount++
default:
return false
}
return true
})
}
framework.ExpectTrue(fooCount > 1 && barCount > 1, "fooCount: %d, barCount: %d", fooCount, barCount)
})
})
ginkgo.Describe("Health Check", func() {
ginkgo.It("TCP", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
fooPort := f.AllocPort()
fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo")))
f.RunServer("", fooServer)
barPort := f.AllocPort()
barServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(barPort), streamserver.WithRespContent([]byte("bar")))
f.RunServer("", barServer)
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "tcp"
localPort = %d
remotePort = %d
loadBalancer.group = "test"
loadBalancer.groupKey = "123"
healthCheck.type = "tcp"
healthCheck.intervalSeconds = 1
[[proxies]]
name = "bar"
type = "tcp"
localPort = %d
remotePort = %d
loadBalancer.group = "test"
loadBalancer.groupKey = "123"
healthCheck.type = "tcp"
healthCheck.intervalSeconds = 1
`, fooPort, remotePort, barPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// check foo and bar is ok
results := []string{}
for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
results = append(results, string(resp.Content))
return true
})
}
framework.ExpectContainElements(results, []string{"foo", "bar"})
// close bar server, check foo is ok
barServer.Close()
time.Sleep(2 * time.Second)
for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure()
}
// resume bar server, check foo and bar is ok
f.RunServer("", barServer)
time.Sleep(2 * time.Second)
results = []string{}
for i := 0; i < 10; i++ {
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
results = append(results, string(resp.Content))
return true
})
}
framework.ExpectContainElements(results, []string{"foo", "bar"})
})
ginkgo.It("HTTP", func() {
vhostPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostPort)
clientConf := consts.DefaultClientConfig
fooPort := f.AllocPort()
fooServer := newHTTPServer(fooPort, "foo")
f.RunServer("", fooServer)
barPort := f.AllocPort()
barServer := newHTTPServer(barPort, "bar")
f.RunServer("", barServer)
clientConf += fmt.Sprintf(`
[[proxies]]
name = "foo"
type = "http"
localPort = %d
customDomains = ["example.com"]
loadBalancer.group = "test"
loadBalancer.groupKey = "123"
healthCheck.type = "http"
healthCheck.intervalSeconds = 1
healthCheck.path = "/healthz"
[[proxies]]
name = "bar"
type = "http"
localPort = %d
customDomains = ["example.com"]
loadBalancer.group = "test"
loadBalancer.groupKey = "123"
healthCheck.type = "http"
healthCheck.intervalSeconds = 1
healthCheck.path = "/healthz"
`, fooPort, barPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// send first HTTP request
var contents []string
framework.NewRequestExpect(f).Port(vhostPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("example.com")
}).
Ensure(func(resp *request.Response) bool {
contents = append(contents, string(resp.Content))
return true
})
// send second HTTP request, should be forwarded to another service
framework.NewRequestExpect(f).Port(vhostPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("example.com")
}).
Ensure(func(resp *request.Response) bool {
contents = append(contents, string(resp.Content))
return true
})
framework.ExpectContainElements(contents, []string{"foo", "bar"})
// check foo and bar is ok
results := doFooBarHTTPRequest(vhostPort, "example.com")
framework.ExpectContainElements(results, []string{"foo", "bar"})
// close bar server, check foo is ok
barServer.Close()
time.Sleep(2 * time.Second)
results = doFooBarHTTPRequest(vhostPort, "example.com")
framework.ExpectContainElements(results, []string{"foo"})
framework.ExpectNotContainElements(results, []string{"bar"})
// resume bar server, check foo and bar is ok
f.RunServer("", barServer)
time.Sleep(2 * time.Second)
results = doFooBarHTTPRequest(vhostPort, "example.com")
framework.ExpectContainElements(results, []string{"foo", "bar"})
})
})
})

View File

@ -0,0 +1,47 @@
package features
import (
"fmt"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/test/e2e/framework"
)
var _ = ginkgo.Describe("[Feature: Heartbeat]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("disable application layer heartbeat", func() {
serverPort := f.AllocPort()
serverConf := fmt.Sprintf(`
bindAddr = "0.0.0.0"
bindPort = %d
transport.heartbeatTimeout = -1
transport.tcpMuxKeepaliveInterval = 2
`, serverPort)
remotePort := f.AllocPort()
clientConf := fmt.Sprintf(`
serverPort = %d
log.level = "trace"
transport.heartbeatInterval = -1
transport.heartbeatTimeout = -1
transport.tcpMuxKeepaliveInterval = 2
[[proxies]]
name = "tcp"
type = "tcp"
localPort = %d
remotePort = %d
`, serverPort, f.PortByName(framework.TCPEchoServerPort), remotePort)
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Protocol("tcp").Port(remotePort).Ensure()
time.Sleep(5 * time.Second)
framework.NewRequestExpect(f).Protocol("tcp").Port(remotePort).Ensure()
})
})

View File

@ -0,0 +1,55 @@
package features
import (
"fmt"
"strings"
"time"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: Monitor]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("Prometheus metrics", func() {
dashboardPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
enablePrometheus = true
webServer.addr = "0.0.0.0"
webServer.port = %d
`, dashboardPort)
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
time.Sleep(500 * time.Millisecond)
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().Port(dashboardPort).HTTPPath("/metrics")
}).Ensure(func(resp *request.Response) bool {
log.Trace("prometheus metrics response: \n%s", resp.Content)
if resp.Code != 200 {
return false
}
if !strings.Contains(string(resp.Content), "traffic_in") {
return false
}
return true
})
})
})

View File

@ -0,0 +1,154 @@
package features
import (
"bufio"
"fmt"
"net"
"net/http"
"github.com/onsi/ginkgo/v2"
pp "github.com/pires/go-proxyproto"
"github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
"github.com/fatedier/frp/test/e2e/pkg/request"
"github.com/fatedier/frp/test/e2e/pkg/rpc"
)
var _ = ginkgo.Describe("[Feature: Real IP]", func() {
f := framework.NewDefaultFramework()
ginkgo.It("HTTP X-Forwarded-For", func() {
vhostHTTPPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostHTTPPort)
localPort := f.AllocPort()
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
})),
)
f.RunServer("", localServer)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com")
}).
ExpectResp([]byte("127.0.0.1")).
Ensure()
})
ginkgo.Describe("Proxy Protocol", func() {
ginkgo.It("TCP", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
localPort := f.AllocPort()
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort),
streamserver.WithCustomHandler(func(c net.Conn) {
defer c.Close()
rd := bufio.NewReader(c)
ppHeader, err := pp.Read(rd)
if err != nil {
log.Error("read proxy protocol error: %v", err)
return
}
for {
if _, err := rpc.ReadBytes(rd); err != nil {
return
}
buf := []byte(ppHeader.SourceAddr.String())
_, _ = rpc.WriteBytes(c, buf)
}
}))
f.RunServer("", localServer)
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = %d
remotePort = %d
transport.proxyProtocolVersion = "v2"
`, localPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure(func(resp *request.Response) bool {
log.Trace("ProxyProtocol get SourceAddr: %s", string(resp.Content))
addr, err := net.ResolveTCPAddr("tcp", string(resp.Content))
if err != nil {
return false
}
if addr.IP.String() != "127.0.0.1" {
return false
}
return true
})
})
ginkgo.It("HTTP", func() {
vhostHTTPPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostHTTPPort)
clientConf := consts.DefaultClientConfig
localPort := f.AllocPort()
var srcAddrRecord string
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort),
streamserver.WithCustomHandler(func(c net.Conn) {
defer c.Close()
rd := bufio.NewReader(c)
ppHeader, err := pp.Read(rd)
if err != nil {
log.Error("read proxy protocol error: %v", err)
return
}
srcAddrRecord = ppHeader.SourceAddr.String()
}))
f.RunServer("", localServer)
clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
localPort = %d
customDomains = ["normal.example.com"]
transport.proxyProtocolVersion = "v2"
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(vhostHTTPPort).RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("normal.example.com")
}).Ensure(framework.ExpectResponseCode(404))
log.Trace("ProxyProtocol get SourceAddr: %s", srcAddrRecord)
addr, err := net.ResolveTCPAddr("tcp", srcAddrRecord)
framework.ExpectNoError(err, srcAddrRecord)
framework.ExpectEqualValues("127.0.0.1", addr.IP.String())
})
})
})

View File

@ -0,0 +1,331 @@
package plugin
import (
"crypto/tls"
"fmt"
"strconv"
"github.com/onsi/ginkgo/v2"
"github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
"github.com/fatedier/frp/test/e2e/pkg/cert"
"github.com/fatedier/frp/test/e2e/pkg/port"
"github.com/fatedier/frp/test/e2e/pkg/request"
)
var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
f := framework.NewDefaultFramework()
ginkgo.Describe("UnixDomainSocket", func() {
ginkgo.It("Expose a unix domain socket echo server", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
getProxyConf := func(proxyName string, portName string, extra string) string {
return fmt.Sprintf(`
[[proxies]]
name = "%s"
type = "tcp"
remotePort = {{ .%s }}
[proxies.plugin]
type = "unix_domain_socket"
unixPath = "{{ .%s }}"
`+extra, proxyName, portName, framework.UDSEchoServerAddr)
}
tests := []struct {
proxyName string
portName string
extraConfig string
}{
{
proxyName: "normal",
portName: port.GenName("Normal"),
},
{
proxyName: "with-encryption",
portName: port.GenName("WithEncryption"),
extraConfig: "transport.useEncryption = true",
},
{
proxyName: "with-compression",
portName: port.GenName("WithCompression"),
extraConfig: "transport.useCompression = true",
},
{
proxyName: "with-encryption-and-compression",
portName: port.GenName("WithEncryptionAndCompression"),
extraConfig: `
transport.useEncryption = true
transport.useCompression = true
`,
},
}
// build all client config
for _, test := range tests {
clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
}
// run frps and frpc
f.RunProcesses([]string{serverConf}, []string{clientConf})
for _, test := range tests {
framework.NewRequestExpect(f).Port(f.PortByName(test.portName)).Ensure()
}
})
})
ginkgo.It("http_proxy", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
remotePort = %d
[proxies.plugin]
type = "http_proxy"
httpUser = "abc"
httpPassword = "123"
`, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// http proxy, no auth info
framework.NewRequestExpect(f).PortName(framework.HTTPSimpleServerPort).RequestModify(func(r *request.Request) {
r.HTTP().Proxy("http://127.0.0.1:" + strconv.Itoa(remotePort))
}).Ensure(framework.ExpectResponseCode(407))
// http proxy, correct auth
framework.NewRequestExpect(f).PortName(framework.HTTPSimpleServerPort).RequestModify(func(r *request.Request) {
r.HTTP().Proxy("http://abc:123@127.0.0.1:" + strconv.Itoa(remotePort))
}).Ensure()
// connect TCP server by CONNECT method
framework.NewRequestExpect(f).PortName(framework.TCPEchoServerPort).RequestModify(func(r *request.Request) {
r.TCP().Proxy("http://abc:123@127.0.0.1:" + strconv.Itoa(remotePort))
})
})
ginkgo.It("socks5 proxy", func() {
serverConf := consts.DefaultServerConfig
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
remotePort = %d
[proxies.plugin]
type = "socks5"
username = "abc"
password = "123"
`, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// http proxy, no auth info
framework.NewRequestExpect(f).PortName(framework.TCPEchoServerPort).RequestModify(func(r *request.Request) {
r.TCP().Proxy("socks5://127.0.0.1:" + strconv.Itoa(remotePort))
}).ExpectError(true).Ensure()
// http proxy, correct auth
framework.NewRequestExpect(f).PortName(framework.TCPEchoServerPort).RequestModify(func(r *request.Request) {
r.TCP().Proxy("socks5://abc:123@127.0.0.1:" + strconv.Itoa(remotePort))
}).Ensure()
})
ginkgo.It("static_file", func() {
vhostPort := f.AllocPort()
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostPort)
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
f.WriteTempFile("test_static_file", "foo")
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
remotePort = %d
[proxies.plugin]
type = "static_file"
localPath = "%s"
[[proxies]]
name = "http"
type = "http"
customDomains = ["example.com"]
[proxies.plugin]
type = "static_file"
localPath = "%s"
[[proxies]]
name = "http-with-auth"
type = "http"
customDomains = ["other.example.com"]
[proxies.plugin]
type = "static_file"
localPath = "%s"
httpUser = "abc"
httpPassword = "123"
`, remotePort, f.TempDirectory, f.TempDirectory, f.TempDirectory)
f.RunProcesses([]string{serverConf}, []string{clientConf})
// from tcp proxy
framework.NewRequestExpect(f).Request(
framework.NewHTTPRequest().HTTPPath("/test_static_file").Port(remotePort),
).ExpectResp([]byte("foo")).Ensure()
// from http proxy without auth
framework.NewRequestExpect(f).Request(
framework.NewHTTPRequest().HTTPHost("example.com").HTTPPath("/test_static_file").Port(vhostPort),
).ExpectResp([]byte("foo")).Ensure()
// from http proxy with auth
framework.NewRequestExpect(f).Request(
framework.NewHTTPRequest().HTTPHost("other.example.com").HTTPPath("/test_static_file").Port(vhostPort).HTTPAuth("abc", "123"),
).ExpectResp([]byte("foo")).Ensure()
})
ginkgo.It("http2https", func() {
serverConf := consts.DefaultServerConfig
vhostHTTPPort := f.AllocPort()
serverConf += fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostHTTPPort)
localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "http2https"
type = "http"
customDomains = ["example.com"]
[proxies.plugin]
type = "http2https"
localAddr = "127.0.0.1:%d"
`, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
framework.ExpectNoError(err)
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithTLSConfig(tlsConfig),
httpserver.WithResponse([]byte("test")),
)
f.RunServer("", localServer)
framework.NewRequestExpect(f).
Port(vhostHTTPPort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("example.com")
}).
ExpectResp([]byte("test")).
Ensure()
})
ginkgo.It("https2http", func() {
generator := &cert.SelfSignedCertGenerator{}
artifacts, err := generator.Generate("example.com")
framework.ExpectNoError(err)
crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert))
keyPath := f.WriteTempFile("server.key", string(artifacts.Key))
serverConf := consts.DefaultServerConfig
vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(`
vhostHTTPSPort = %d
`, vhostHTTPSPort)
localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "https2http"
type = "https"
customDomains = ["example.com"]
[proxies.plugin]
type = "https2http"
localAddr = "127.0.0.1:%d"
crtPath = "%s"
keyPath = "%s"
`, localPort, crtPath, keyPath)
f.RunProcesses([]string{serverConf}, []string{clientConf})
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithResponse([]byte("test")),
)
f.RunServer("", localServer)
framework.NewRequestExpect(f).
Port(vhostHTTPSPort).
RequestModify(func(r *request.Request) {
r.HTTPS().HTTPHost("example.com").TLSConfig(&tls.Config{
ServerName: "example.com",
InsecureSkipVerify: true,
})
}).
ExpectResp([]byte("test")).
Ensure()
})
ginkgo.It("https2https", func() {
generator := &cert.SelfSignedCertGenerator{}
artifacts, err := generator.Generate("example.com")
framework.ExpectNoError(err)
crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert))
keyPath := f.WriteTempFile("server.key", string(artifacts.Key))
serverConf := consts.DefaultServerConfig
vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(`
vhostHTTPSPort = %d
`, vhostHTTPSPort)
localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "https2https"
type = "https"
customDomains = ["example.com"]
[proxies.plugin]
type = "https2https"
localAddr = "127.0.0.1:%d"
crtPath = "%s"
keyPath = "%s"
`, localPort, crtPath, keyPath)
f.RunProcesses([]string{serverConf}, []string{clientConf})
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
framework.ExpectNoError(err)
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithResponse([]byte("test")),
httpserver.WithTLSConfig(tlsConfig),
)
f.RunServer("", localServer)
framework.NewRequestExpect(f).
Port(vhostHTTPSPort).
RequestModify(func(r *request.Request) {
r.HTTPS().HTTPHost("example.com").TLSConfig(&tls.Config{
ServerName: "example.com",
InsecureSkipVerify: true,
})
}).
ExpectResp([]byte("test")).
Ensure()
})
})

View File

@ -0,0 +1,415 @@
package plugin
import (
"fmt"
"time"
"github.com/onsi/ginkgo/v2"
plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
)
var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() {
f := framework.NewDefaultFramework()
ginkgo.Describe("Login", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.LoginContent{}
return &r
}
ginkgo.It("Auth for custom meta token", func() {
localPort := f.AllocPort()
clientAddressGot := false
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.LoginContent)
if content.ClientAddress != "" {
clientAddressGot = true
}
if content.Metas["token"] == "123" {
ret.Unchange = true
} else {
ret.Reject = true
ret.RejectReason = "invalid token"
}
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "user-manager"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["Login"]
`, localPort)
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
metadatas.token = "123"
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
remotePort2 := f.AllocPort()
invalidTokenClientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "tcp2"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort2)
f.RunProcesses([]string{serverConf}, []string{clientConf, invalidTokenClientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
framework.NewRequestExpect(f).Port(remotePort2).ExpectError(true).Ensure()
framework.ExpectTrue(clientAddressGot)
})
})
ginkgo.Describe("NewProxy", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.NewProxyContent{}
return &r
}
ginkgo.It("Validate Info", func() {
localPort := f.AllocPort()
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.NewProxyContent)
if content.ProxyName == "tcp" {
ret.Unchange = true
} else {
ret.Reject = true
}
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["NewProxy"]
`, localPort)
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
})
ginkgo.It("Mofify RemotePort", func() {
localPort := f.AllocPort()
remotePort := f.AllocPort()
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.NewProxyContent)
content.RemotePort = remotePort
ret.Content = content
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["NewProxy"]
`, localPort)
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = 0
`, framework.TCPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
})
})
ginkgo.Describe("CloseProxy", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.CloseProxyContent{}
return &r
}
ginkgo.It("Validate Info", func() {
localPort := f.AllocPort()
var recordProxyName string
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.CloseProxyContent)
recordProxyName = content.ProxyName
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["CloseProxy"]
`, localPort)
clientConf := consts.DefaultClientConfig
remotePort := f.AllocPort()
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
_, clients := f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
for _, c := range clients {
_ = c.Stop()
}
time.Sleep(1 * time.Second)
framework.ExpectEqual(recordProxyName, "tcp")
})
})
ginkgo.Describe("Ping", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.PingContent{}
return &r
}
ginkgo.It("Validate Info", func() {
localPort := f.AllocPort()
var record string
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.PingContent)
record = content.Ping.PrivilegeKey
ret.Unchange = true
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["Ping"]
`, localPort)
remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
transport.heartbeatInterval = 1
auth.additionalScopes = ["HeartBeats"]
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
time.Sleep(3 * time.Second)
framework.ExpectNotEqual("", record)
})
})
ginkgo.Describe("NewWorkConn", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.NewWorkConnContent{}
return &r
}
ginkgo.It("Validate Info", func() {
localPort := f.AllocPort()
var record string
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.NewWorkConnContent)
record = content.NewWorkConn.RunID
ret.Unchange = true
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["NewWorkConn"]
`, localPort)
remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
framework.ExpectNotEqual("", record)
})
})
ginkgo.Describe("NewUserConn", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.NewUserConnContent{}
return &r
}
ginkgo.It("Validate Info", func() {
localPort := f.AllocPort()
var record string
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.NewUserConnContent)
record = content.RemoteAddr
ret.Unchange = true
return &ret
}
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "127.0.0.1:%d"
path = "/handler"
ops = ["NewUserConn"]
`, localPort)
remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
framework.ExpectNotEqual("", record)
})
})
ginkgo.Describe("HTTPS Protocol", func() {
newFunc := func() *plugin.Request {
var r plugin.Request
r.Content = &plugin.NewUserConnContent{}
return &r
}
ginkgo.It("Validate Login Info, disable tls verify", func() {
localPort := f.AllocPort()
var record string
handler := func(req *plugin.Request) *plugin.Response {
var ret plugin.Response
content := req.Content.(*plugin.NewUserConnContent)
record = content.RemoteAddr
ret.Unchange = true
return &ret
}
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
framework.ExpectNoError(err)
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, tlsConfig)
f.RunServer("", pluginServer)
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
[[httpPlugins]]
name = "test"
addr = "https://127.0.0.1:%d"
path = "/handler"
ops = ["NewUserConn"]
`, localPort)
remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig
clientConf += fmt.Sprintf(`
[[proxies]]
name = "tcp"
type = "tcp"
localPort = {{ .%s }}
remotePort = %d
`, framework.TCPEchoServerPort, remotePort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure()
framework.ExpectNotEqual("", record)
})
})
})

View File

@ -0,0 +1,41 @@
package plugin
import (
"crypto/tls"
"encoding/json"
"io"
"net/http"
plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
)
type Handler func(req *plugin.Request) *plugin.Response
type NewPluginRequest func() *plugin.Request
func NewHTTPPluginServer(port int, newFunc NewPluginRequest, handler Handler, tlsConfig *tls.Config) *httpserver.Server {
return httpserver.New(
httpserver.WithBindPort(port),
httpserver.WithTLSConfig(tlsConfig),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r := newFunc()
buf, err := io.ReadAll(req.Body)
if err != nil {
w.WriteHeader(500)
return
}
log.Trace("plugin request: %s", string(buf))
err = json.Unmarshal(buf, &r)
if err != nil {
w.WriteHeader(500)
return
}
resp := handler(r)
buf, _ = json.Marshal(resp)
log.Trace("plugin response: %s", string(buf))
_, _ = w.Write(buf)
})),
)
}