From 896add33dbc773802d2d57cd31ada79c34365a0f Mon Sep 17 00:00:00 2001 From: yuyulei Date: Mon, 28 Dec 2020 14:01:21 +0800 Subject: [PATCH] Update by comments --- client/admin_api.go | 2 +- cmd/frpc/sub/http.go | 2 +- cmd/frpc/sub/https.go | 2 +- cmd/frpc/sub/root.go | 4 +- cmd/frpc/sub/stcp.go | 2 +- cmd/frpc/sub/sudp.go | 2 +- cmd/frpc/sub/tcp.go | 2 +- cmd/frpc/sub/tcpmux.go | 2 +- cmd/frpc/sub/udp.go | 2 +- cmd/frpc/sub/xtcp.go | 2 +- cmd/frps/root.go | 4 +- pkg/auth/auth.go | 18 +- pkg/auth/oidc.go | 16 +- pkg/auth/token.go | 2 +- pkg/config/README.md | 12 + pkg/config/bandwidth.go | 121 ---- pkg/config/client.go | 125 +++- pkg/config/client_test.go | 118 ++-- pkg/config/proxy.go | 320 +++++++--- pkg/config/proxy_test.go | 96 ++- pkg/config/server.go | 140 +++++ pkg/config/types.go | 577 +++--------------- .../{bandwidth_test.go => types_test.go} | 0 pkg/config/visitor.go | 102 +++- pkg/config/visitor_test.go | 9 +- pkg/util/util/http.go | 2 +- 26 files changed, 784 insertions(+), 900 deletions(-) create mode 100644 pkg/config/README.md delete mode 100644 pkg/config/bandwidth.go rename pkg/config/{bandwidth_test.go => types_test.go} (100%) diff --git a/client/admin_api.go b/client/admin_api.go index 9b23b1a7..c5548fb5 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -62,7 +62,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { return } - pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(svr.cfg.User, content, newCommonCfg.Start) + pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(svr.cfg.User, content, newCommonCfg.Start) if err != nil { res.Code = 400 res.Msg = err.Error() diff --git a/cmd/frpc/sub/http.go b/cmd/frpc/sub/http.go index d1286b2e..03593f1a 100644 --- a/cmd/frpc/sub/http.go +++ b/cmd/frpc/sub/http.go @@ -47,7 +47,7 @@ var httpCmd = &cobra.Command{ Use: "http", Short: "Run frpc with a single http proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/https.go b/cmd/frpc/sub/https.go index 99a22953..d636f426 100644 --- a/cmd/frpc/sub/https.go +++ b/cmd/frpc/sub/https.go @@ -43,7 +43,7 @@ var httpsCmd = &cobra.Command{ Use: "https", Short: "Run frpc with a single https proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index b318f3b7..bf867932 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -129,7 +129,7 @@ func handleSignal(svr *client.Service) { close(kcpDoneCh) } -func parseClientCommonCfg(fileType int, source interface{}) (cfg config.ClientCommonConf, err error) { +func parseClientCommonCfg(fileType int, source []byte) (cfg config.ClientCommonConf, err error) { if fileType == CfgFileTypeIni { cfg, err = config.UnmarshalClientConfFromIni(source) } else if fileType == CfgFileTypeCmd { @@ -194,7 +194,7 @@ func runClient(cfgFilePath string) (err error) { return } - pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(cfg.User, content, cfg.Start) + pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(cfg.User, content, cfg.Start) if err != nil { return } diff --git a/cmd/frpc/sub/stcp.go b/cmd/frpc/sub/stcp.go index 1b4ac0f1..673a268e 100644 --- a/cmd/frpc/sub/stcp.go +++ b/cmd/frpc/sub/stcp.go @@ -45,7 +45,7 @@ var stcpCmd = &cobra.Command{ Use: "stcp", Short: "Run frpc with a single stcp proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/sudp.go b/cmd/frpc/sub/sudp.go index d7306771..3c3d5a8b 100644 --- a/cmd/frpc/sub/sudp.go +++ b/cmd/frpc/sub/sudp.go @@ -45,7 +45,7 @@ var sudpCmd = &cobra.Command{ Use: "sudp", Short: "Run frpc with a single sudp proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/tcp.go b/cmd/frpc/sub/tcp.go index e6f310fd..b62cb74a 100644 --- a/cmd/frpc/sub/tcp.go +++ b/cmd/frpc/sub/tcp.go @@ -41,7 +41,7 @@ var tcpCmd = &cobra.Command{ Use: "tcp", Short: "Run frpc with a single tcp proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/tcpmux.go b/cmd/frpc/sub/tcpmux.go index 065bf9c1..6f46cf76 100644 --- a/cmd/frpc/sub/tcpmux.go +++ b/cmd/frpc/sub/tcpmux.go @@ -44,7 +44,7 @@ var tcpMuxCmd = &cobra.Command{ Use: "tcpmux", Short: "Run frpc with a single tcpmux proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/udp.go b/cmd/frpc/sub/udp.go index c5219960..7f6dd3f0 100644 --- a/cmd/frpc/sub/udp.go +++ b/cmd/frpc/sub/udp.go @@ -41,7 +41,7 @@ var udpCmd = &cobra.Command{ Use: "udp", Short: "Run frpc with a single udp proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/xtcp.go b/cmd/frpc/sub/xtcp.go index 6e66f953..1eb096f7 100644 --- a/cmd/frpc/sub/xtcp.go +++ b/cmd/frpc/sub/xtcp.go @@ -45,7 +45,7 @@ var xtcpCmd = &cobra.Command{ Use: "xtcp", Short: "Run frpc with a single xtcp proxy", RunE: func(cmd *cobra.Command, args []string) error { - clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "") + clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frps/root.go b/cmd/frps/root.go index 6c638b1e..b76817bc 100644 --- a/cmd/frps/root.go +++ b/cmd/frps/root.go @@ -114,7 +114,7 @@ var rootCmd = &cobra.Command{ cfg, err = parseServerCommonCfg(CfgFileTypeIni, content) } else { log.Info("frps uses command line arguments for config") - cfg, err = parseServerCommonCfg(CfgFileTypeCmd, "") + cfg, err = parseServerCommonCfg(CfgFileTypeCmd, nil) } if err != nil { return err @@ -135,7 +135,7 @@ func Execute() { } } -func parseServerCommonCfg(fileType int, source interface{}) (cfg config.ServerCommonConf, err error) { +func parseServerCommonCfg(fileType int, source []byte) (cfg config.ServerCommonConf, err error) { if fileType == CfgFileTypeIni { cfg, err = config.UnmarshalServerConfFromIni(source) } else if fileType == CfgFileTypeCmd { diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 161c2d18..894da247 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -26,13 +26,13 @@ type BaseConfig struct { // authenticate frpc with frps. If "token" is specified - token will be // read into login message. If "oidc" is specified - OIDC (Open ID Connect) // token will be issued using OIDC settings. By default, this value is "token". - AuthenticationMethod string `ini:"authentication_method",json:"authentication_method"` + AuthenticationMethod string `ini:"authentication_method" json:"authentication_method"` // AuthenticateHeartBeats specifies whether to include authentication token in // heartbeats sent to frps. By default, this value is false. - AuthenticateHeartBeats bool `ini:"authenticate_heartbeats",json:"authenticate_heartbeats"` + AuthenticateHeartBeats bool `ini:"authenticate_heartbeats" json:"authenticate_heartbeats"` // AuthenticateNewWorkConns specifies whether to include authentication token in // new work connections sent to frps. By default, this value is false. - AuthenticateNewWorkConns bool `ini:"authenticate_new_work_conns",json:"authenticate_new_work_conns"` + AuthenticateNewWorkConns bool `ini:"authenticate_new_work_conns" json:"authenticate_new_work_conns"` } func getDefaultBaseConf() BaseConfig { @@ -44,9 +44,9 @@ func getDefaultBaseConf() BaseConfig { } type ClientConfig struct { - BaseConfig `ini:",,,,extends"` - OidcClientConfig `ini:",,,,extends"` - TokenConfig `ini:",,,,extends"` + BaseConfig `ini:",extends"` + OidcClientConfig `ini:",extends"` + TokenConfig `ini:",extends"` } func GetDefaultClientConf() ClientConfig { @@ -58,9 +58,9 @@ func GetDefaultClientConf() ClientConfig { } type ServerConfig struct { - BaseConfig `ini:",,,,extends"` - OidcServerConfig `ini:",,,,extends"` - TokenConfig `ini:",,,,extends"` + BaseConfig `ini:",extends"` + OidcServerConfig `ini:",extends"` + TokenConfig `ini:",extends"` } func GetDefaultServerConf() ServerConfig { diff --git a/pkg/auth/oidc.go b/pkg/auth/oidc.go index 1309b73a..981f7589 100644 --- a/pkg/auth/oidc.go +++ b/pkg/auth/oidc.go @@ -28,18 +28,18 @@ type OidcClientConfig struct { // OidcClientID specifies the client ID to use to get a token in OIDC // authentication if AuthenticationMethod == "oidc". By default, this value // is "". - OidcClientID string `ini:"oidc_client_id",json:"oidc_client_id"` + OidcClientID string `ini:"oidc_client_id" json:"oidc_client_id"` // OidcClientSecret specifies the client secret to use to get a token in OIDC // authentication if AuthenticationMethod == "oidc". By default, this value // is "". - OidcClientSecret string `ini:"oidc_client_secret",json:"oidc_client_secret"` + OidcClientSecret string `ini:"oidc_client_secret" json:"oidc_client_secret"` // OidcAudience specifies the audience of the token in OIDC authentication //if AuthenticationMethod == "oidc". By default, this value is "". - OidcAudience string `ini:"oidc_audience",json:"oidc_audience"` + OidcAudience string `ini:"oidc_audience" json:"oidc_audience"` // OidcTokenEndpointURL specifies the URL which implements OIDC Token Endpoint. // It will be used to get an OIDC token if AuthenticationMethod == "oidc". // By default, this value is "". - OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url",json:"oidc_token_endpoint_url"` + OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"` } func getDefaultOidcClientConf() OidcClientConfig { @@ -56,20 +56,20 @@ type OidcServerConfig struct { // will be used to load public keys to verify signature and will be compared // with the issuer claim in the OIDC token. It will be used if // AuthenticationMethod == "oidc". By default, this value is "". - OidcIssuer string `ini:"oidc_issuer",json:"oidc_issuer"` + OidcIssuer string `ini:"oidc_issuer" json:"oidc_issuer"` // OidcAudience specifies the audience OIDC tokens should contain when validated. // If this value is empty, audience ("client ID") verification will be skipped. // It will be used when AuthenticationMethod == "oidc". By default, this // value is "". - OidcAudience string `ini:"oidc_audience",json:"oidc_audience"` + OidcAudience string `ini:"oidc_audience" json:"oidc_audience"` // OidcSkipExpiryCheck specifies whether to skip checking if the OIDC token is // expired. It will be used when AuthenticationMethod == "oidc". By default, this // value is false. - OidcSkipExpiryCheck bool `ini:"oidc_skip_expiry_check",json:"oidc_skip_expiry_check"` + OidcSkipExpiryCheck bool `ini:"oidc_skip_expiry_check" json:"oidc_skip_expiry_check"` // OidcSkipIssuerCheck specifies whether to skip checking if the OIDC token's // issuer claim matches the issuer specified in OidcIssuer. It will be used when // AuthenticationMethod == "oidc". By default, this value is false. - OidcSkipIssuerCheck bool `ini:"oidc_skip_issuer_check",json:"oidc_skip_issuer_check"` + OidcSkipIssuerCheck bool `ini:"oidc_skip_issuer_check" json:"oidc_skip_issuer_check"` } func getDefaultOidcServerConf() OidcServerConfig { diff --git a/pkg/auth/token.go b/pkg/auth/token.go index bf1c8487..1049174d 100644 --- a/pkg/auth/token.go +++ b/pkg/auth/token.go @@ -26,7 +26,7 @@ type TokenConfig struct { // 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 succeed. By default, this value is "". - Token string `ini:"token",json:"token"` + Token string `ini:"token" json:"token"` } func getDefaultTokenConf() TokenConfig { diff --git a/pkg/config/README.md b/pkg/config/README.md new file mode 100644 index 00000000..d74f756e --- /dev/null +++ b/pkg/config/README.md @@ -0,0 +1,12 @@ +So far, there is no mature Go project that does well in parsing `*.ini` files. + +By comparison, we have selected an open source project: `https://github.com/go-ini/ini`. + +This library helped us solve most of the key-value matching, but there are still some problems, such as not supporting parsing `map`. + +We add our own logic on the basis of this library. In the current situationwhich, we need to complete the entire `Unmarshal` in two steps: + +* Step#1, use `go-ini` to complete the basic parameter matching; +* Step#2, parse our custom parameters to realize parsing special structure, like `map`, `array`. + +Some of the keywords in `tag`(like inline, extends, etc.) may be different from standard libraries such as `json` and `protobuf` in Go. For details, please refer to the library documentation: https://ini.unknwon.io/docs/intro. \ No newline at end of file diff --git a/pkg/config/bandwidth.go b/pkg/config/bandwidth.go deleted file mode 100644 index 062cc746..00000000 --- a/pkg/config/bandwidth.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019 fatedier, fatedier@gmail.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "encoding/json" - "errors" - "strconv" - "strings" -) - -const ( - MB = 1024 * 1024 - KB = 1024 -) - -type BandwidthQuantity struct { - s string // MB or KB - - i int64 // bytes -} - -func NewBandwidthQuantity(s string) (BandwidthQuantity, error) { - q := BandwidthQuantity{} - err := q.UnmarshalString(s) - if err != nil { - return q, err - } - return q, nil -} - -func MustBandwidthQuantity(s string) BandwidthQuantity { - q := BandwidthQuantity{} - err := q.UnmarshalString(s) - if err != nil { - panic(err) - } - return q -} - -func (q *BandwidthQuantity) Equal(u *BandwidthQuantity) bool { - if q == nil && u == nil { - return true - } - if q != nil && u != nil { - return q.i == u.i - } - return false -} - -func (q *BandwidthQuantity) String() string { - return q.s -} - -func (q *BandwidthQuantity) UnmarshalString(s string) error { - s = strings.TrimSpace(s) - if s == "" { - return nil - } - - var ( - base int64 - f float64 - err error - ) - if strings.HasSuffix(s, "MB") { - base = MB - fstr := strings.TrimSuffix(s, "MB") - f, err = strconv.ParseFloat(fstr, 64) - if err != nil { - return err - } - } else if strings.HasSuffix(s, "KB") { - base = KB - fstr := strings.TrimSuffix(s, "KB") - f, err = strconv.ParseFloat(fstr, 64) - if err != nil { - return err - } - } else { - return errors.New("unit not support") - } - - q.s = s - q.i = int64(f * float64(base)) - return nil -} - -func (q *BandwidthQuantity) UnmarshalJSON(b []byte) error { - if len(b) == 4 && string(b) == "null" { - return nil - } - - var str string - err := json.Unmarshal(b, &str) - if err != nil { - return err - } - - return q.UnmarshalString(str) -} - -func (q *BandwidthQuantity) MarshalJSON() ([]byte, error) { - return []byte("\"" + q.s + "\""), nil -} - -func (q *BandwidthQuantity) Bytes() int64 { - return q.i -} diff --git a/pkg/config/client.go b/pkg/config/client.go index 96353ea2..4db46206 100644 --- a/pkg/config/client.go +++ b/pkg/config/client.go @@ -25,6 +25,116 @@ import ( "gopkg.in/ini.v1" ) +// ClientCommonConf contains information for a client service. It is +// recommended to use GetDefaultClientConf instead of creating this object +// directly, so that all unspecified fields have reasonable default values. +type ClientCommonConf struct { + auth.ClientConfig `ini:",extends" json:"inline"` + + // ServerAddr specifies the address of the server to connect to. By + // default, this value is "0.0.0.0". + ServerAddr string `ini:"server_addr" josn:"server_addr"` + // ServerPort specifies the port to connect to the server on. By default, + // this value is 7000. + ServerPort int `ini:"server_port" json:"server_port"` + // HTTPProxy specifies a proxy address to connect to the server through. If + // this value is "", the server will be connected to directly. By default, + // this value is read from the "http_proxy" environment variable. + HTTPProxy string `ini:"http_proxy" json:"http_proxy"` + // LogFile specifies a file where logs will be written to. This value will + // only be used if LogWay is set appropriately. By default, this value is + // "console". + LogFile string `ini:"log_file" json:"log_file"` + // LogWay specifies the way logging is managed. Valid values are "console" + // or "file". If "console" is used, logs will be printed to stdout. If + // "file" is used, logs will be printed to LogFile. By default, this value + // is "console". + LogWay string `ini:"log_way" json:"log_way"` + // LogLevel specifies the minimum log level. Valid values are "trace", + // "debug", "info", "warn", and "error". By default, this value is "info". + LogLevel string `ini:"log_level" json:"log_level"` + // LogMaxDays specifies the maximum number of days to store log information + // before deletion. This is only used if LogWay == "file". By default, this + // value is 0. + LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"` + // DisableLogColor disables log colors when LogWay == "console" when set to + // true. By default, this value is false. + DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"` + // AdminAddr specifies the address that the admin server binds to. By + // default, this value is "127.0.0.1". + AdminAddr string `ini:"admin_addr" json:"admin_addr"` + // AdminPort specifies the port for the admin server to listen on. If this + // value is 0, the admin server will not be started. By default, this value + // is 0. + AdminPort int `ini:"admin_port" json:"admin_port"` + // AdminUser specifies the username that the admin server will use for + // login. By default, this value is "admin". + AdminUser string `ini:"admin_user" json:"admin_user"` + // AdminPwd specifies the password that the admin server will use for + // login. By default, this value is "admin". + AdminPwd string `ini:"admin_pwd" json:"admin_pwd"` + // AssetsDir specifies the local directory that the admin server will load + // resources from. If this value is "", assets will be loaded from the + // bundled executable using statik. By default, this value is "". + AssetsDir string `ini:"assets_dir" json:"assets_dir"` + // PoolCount specifies the number of connections the client will make to + // the server in advance. By default, this value is 0. + PoolCount int `ini:"pool_count" json:"pool_count"` + // TCPMux toggles TCP stream multiplexing. This allows multiple requests + // from a client to share a single TCP connection. If this value is true, + // the server must have TCP multiplexing enabled as well. By default, this + // value is true. + TCPMux bool `ini:"tcp_mux" json:"tcp_mux"` + // User specifies a prefix for proxy names to distinguish them from other + // clients. If this value is not "", proxy names will automatically be + // changed to "{user}.{proxy_name}". By default, this value is "". + User string `ini:"user" json:"user"` + // DNSServer specifies a DNS server address for FRPC to use. If this value + // is "", the default DNS will be used. By default, this value is "". + DNSServer string `ini:"dns_server" json:"dns_server"` + // LoginFailExit controls whether or not the client should exit after a + // failed login attempt. If false, the client will retry until a login + // attempt succeeds. By default, this value is true. + LoginFailExit bool `ini:"login_fail_exit" json:"login_fail_exit"` + // Start specifies a set of enabled proxies by name. If this set is empty, + // all supplied proxies are enabled. By default, this value is an empty + // set. + Start []string `ini:"start" json:"start"` + //Start map[string]struct{} `json:"start"` + // Protocol specifies the protocol to use when interacting with the server. + // Valid values are "tcp", "kcp" and "websocket". By default, this value + // is "tcp". + Protocol string `ini:"protocol" json:"protocol"` + // TLSEnable specifies whether or not TLS should be used when communicating + // with the server. If "tls_cert_file" and "tls_key_file" are valid, + // client will load the supplied tls configuration. + TLSEnable bool `ini:"tls_enable" json:"tls_enable"` + // ClientTLSCertPath specifies the path of the cert file that client will + // load. It only works when "tls_enable" is true and "tls_key_file" is valid. + TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"` + // ClientTLSKeyPath specifies the path of the secret key file that client + // will load. It only works when "tls_enable" is true and "tls_cert_file" + // are valid. + TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"` + // TrustedCaFile specifies the path of the trusted ca file that will load. + // It only works when "tls_enable" is valid and tls configuration of server + // has been specified. + TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"` + // HeartBeatInterval specifies at what interval heartbeats are sent to the + // server, in seconds. It is not recommended to change this value. By + // default, this value is 30. + HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"` + // HeartBeatTimeout specifies the maximum allowed heartbeat response delay + // before the connection is terminated, in seconds. It is not recommended + // to change this value. By default, this value is 90. + HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"` + // Client meta info + Metas map[string]string `ini:"-" json:"metas"` + // UDPPacketSize specifies the udp packet size + // By default, this value is 1500 + UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"` +} + // GetDefaultClientConf returns a client configuration with default values. func GetDefaultClientConf() ClientCommonConf { return ClientCommonConf{ @@ -101,8 +211,7 @@ func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) { s, err := f.GetSection("common") if err != nil { - // TODO: add error info - return ClientCommonConf{}, err + return ClientCommonConf{}, fmt.Errorf("invalid configuration file, not found [common] section") } common := GetDefaultClientConf() @@ -118,7 +227,7 @@ func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) { // if len(startProxy) is 0, start all // otherwise just start proxies in startProxy map -func LoadAllConfFromIni( +func LoadAllProxyConfsFromIni( prefix string, source interface{}, start []string, @@ -164,9 +273,9 @@ func LoadAllConfFromIni( } for _, section := range rangeSections { - err = appendTemplates(f, section) + err = renderRangeProxyTemplates(f, section) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("fail to render range-section[%s] with error: %v", section.Name(), err) } } @@ -207,7 +316,7 @@ func LoadAllConfFromIni( return proxyConfs, visitorConfs, nil } -func appendTemplates(f *ini.File, section *ini.Section) error { +func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error { // Validation localPortStr := section.Key("local_port").String() @@ -227,11 +336,11 @@ func appendTemplates(f *ini.File, section *ini.Section) error { } if len(localPorts) != len(remotePorts) { - return fmt.Errorf("range section [%s] local ports number should be same with remote ports number", section.Name()) + return fmt.Errorf("local ports number should be same with remote ports number") } if len(localPorts) == 0 { - return fmt.Errorf("range section [%s] local_port and remote_port is necessary", section.Name()) + return fmt.Errorf("local_port and remote_port is necessary") } // Templates diff --git a/pkg/config/client_test.go b/pkg/config/client_test.go index 6f581445..9ad85e38 100644 --- a/pkg/config/client_test.go +++ b/pkg/config/client_test.go @@ -325,9 +325,7 @@ func Test_LoadClientBasicConf(t *testing.T) { HealthCheckAddr: "127.0.0.9:29", }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6009, - }, + RemotePort: 6009, }, testUser + ".ssh_random": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -338,9 +336,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 29, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 9, - }, + RemotePort: 9, }, testUser + ".tcp_port_0": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -351,9 +347,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 6010, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6010, - }, + RemotePort: 6010, }, testUser + ".tcp_port_1": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -364,9 +358,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 6011, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6011, - }, + RemotePort: 6011, }, testUser + ".tcp_port_2": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -377,9 +369,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 6019, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6019, - }, + RemotePort: 6019, }, testUser + ".dns": &UDPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -392,9 +382,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 59, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6009, - }, + RemotePort: 6009, }, testUser + ".udp_port_0": &UDPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -407,9 +395,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 6000, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6000, - }, + RemotePort: 6000, }, testUser + ".udp_port_1": &UDPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -422,9 +408,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 6010, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6010, - }, + RemotePort: 6010, }, testUser + ".udp_port_2": &UDPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -437,9 +421,7 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 6011, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6011, - }, + RemotePort: 6011, }, testUser + ".web01": &HTTPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -459,18 +441,16 @@ func Test_LoadClientBasicConf(t *testing.T) { HealthCheckURL: "http://127.0.0.9:89/status", }, }, - HTTPProxySpec: HTTPProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"web02.yourdomain.com"}, - SubDomain: "web01", - }, - Locations: []string{"/", "/pic"}, - HTTPUser: "admin", - HTTPPwd: "admin", - HostHeaderRewrite: "example.com", - Headers: map[string]string{ - "X-From-Where": "frp", - }, + DomainConf: DomainConf{ + CustomDomains: []string{"web02.yourdomain.com"}, + SubDomain: "web01", + }, + Locations: []string{"/", "/pic"}, + HTTPUser: "admin", + HTTPPwd: "admin", + HostHeaderRewrite: "example.com", + Headers: map[string]string{ + "X-From-Where": "frp", }, }, testUser + ".web02": &HTTPSProxyConf{ @@ -485,11 +465,9 @@ func Test_LoadClientBasicConf(t *testing.T) { }, ProxyProtocolVersion: "v2", }, - HTTPSProxySpec: HTTPSProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"web02.yourdomain.com"}, - SubDomain: "web01", - }, + DomainConf: DomainConf{ + CustomDomains: []string{"web02.yourdomain.com"}, + SubDomain: "web01", }, }, testUser + ".secret_tcp": &STCPProxyConf{ @@ -501,10 +479,8 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 22, }, }, - STCPProxySpec: STCPProxySpec{ - Role: "server", - Sk: "abcdefg", - }, + Role: "server", + Sk: "abcdefg", }, testUser + ".p2p_tcp": &XTCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -515,10 +491,8 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 22, }, }, - XTCPProxySpec: XTCPProxySpec{ - Role: "server", - Sk: "abcdefg", - }, + Role: "server", + Sk: "abcdefg", }, testUser + ".tcpmuxhttpconnect": &TCPMuxProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -529,13 +503,11 @@ func Test_LoadClientBasicConf(t *testing.T) { LocalPort: 10701, }, }, - TCPMuxProxySpec: TCPMuxProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"tunnel1"}, - SubDomain: "", - }, - Multiplexer: "httpconnect", + DomainConf: DomainConf{ + CustomDomains: []string{"tunnel1"}, + SubDomain: "", }, + Multiplexer: "httpconnect", }, testUser + ".plugin_unix_domain_socket": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -549,9 +521,7 @@ func Test_LoadClientBasicConf(t *testing.T) { }, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6003, - }, + RemotePort: 6003, }, testUser + ".plugin_http_proxy": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -566,9 +536,7 @@ func Test_LoadClientBasicConf(t *testing.T) { }, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6004, - }, + RemotePort: 6004, }, testUser + ".plugin_socks5": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -583,9 +551,7 @@ func Test_LoadClientBasicConf(t *testing.T) { }, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6005, - }, + RemotePort: 6005, }, testUser + ".plugin_static_file": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -602,9 +568,7 @@ func Test_LoadClientBasicConf(t *testing.T) { }, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6006, - }, + RemotePort: 6006, }, testUser + ".plugin_https2http": &HTTPSProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -622,10 +586,8 @@ func Test_LoadClientBasicConf(t *testing.T) { }, }, }, - HTTPSProxySpec: HTTPSProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"test.yourdomain.com"}, - }, + DomainConf: DomainConf{ + CustomDomains: []string{"test.yourdomain.com"}, }, }, testUser + ".plugin_http2https": &HTTPProxyConf{ @@ -642,10 +604,8 @@ func Test_LoadClientBasicConf(t *testing.T) { }, }, }, - HTTPProxySpec: HTTPProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"test.yourdomain.com"}, - }, + DomainConf: DomainConf{ + CustomDomains: []string{"test.yourdomain.com"}, }, }, } @@ -675,7 +635,7 @@ func Test_LoadClientBasicConf(t *testing.T) { }, } - proxyActual, visitorActual, err := LoadAllConfFromIni(testUser, testClientBytesWithFull, nil) + proxyActual, visitorActual, err := LoadAllProxyConfsFromIni(testUser, testClientBytesWithFull, nil) assert.NoError(err) assert.Equal(proxyExpected, proxyActual) assert.Equal(visitorExpected, visitorActual) diff --git a/pkg/config/proxy.go b/pkg/config/proxy.go index 16fcff15..96a4face 100644 --- a/pkg/config/proxy.go +++ b/pkg/config/proxy.go @@ -25,6 +25,197 @@ import ( "gopkg.in/ini.v1" ) +// Proxy +var ( + proxyConfTypeMap = map[string]reflect.Type{ + consts.TCPProxy: reflect.TypeOf(TCPProxyConf{}), + consts.TCPMuxProxy: reflect.TypeOf(TCPMuxProxyConf{}), + consts.UDPProxy: reflect.TypeOf(UDPProxyConf{}), + consts.HTTPProxy: reflect.TypeOf(HTTPProxyConf{}), + consts.HTTPSProxy: reflect.TypeOf(HTTPSProxyConf{}), + consts.STCPProxy: reflect.TypeOf(STCPProxyConf{}), + consts.XTCPProxy: reflect.TypeOf(XTCPProxyConf{}), + consts.SUDPProxy: reflect.TypeOf(SUDPProxyConf{}), + } +) + +func NewConfByType(proxyType string) ProxyConf { + v, ok := proxyConfTypeMap[proxyType] + if !ok { + return nil + } + cfg := reflect.New(v).Interface().(ProxyConf) + return cfg +} + +type ProxyConf interface { + GetBaseInfo() *BaseProxyConf + UnmarshalFromMsg(*msg.NewProxy) + UnmarshalFromIni(string, string, *ini.Section) error + MarshalToMsg(*msg.NewProxy) + CheckForCli() error + CheckForSvr(ServerCommonConf) error + Compare(ProxyConf) bool +} + +// LocalSvrConf configures what location the client will to, or what +// plugin will be used. +type LocalSvrConf struct { + // LocalIP specifies the IP address or host name to to. + LocalIP string `ini:"local_ip" json:"local_ip"` + // LocalPort specifies the port to to. + LocalPort int `ini:"local_port" json:"local_port"` + + // Plugin specifies what plugin should be used for ng. If this value + // is set, the LocalIp and LocalPort values will be ignored. By default, + // this value is "". + Plugin string `ini:"plugin" json:"plugin"` + // PluginParams specify parameters to be passed to the plugin, if one is + // being used. By default, this value is an empty map. + PluginParams map[string]string `ini:"-"` +} + +// HealthCheckConf configures health checking. This can be useful for load +// balancing purposes to detect and remove proxies to failing services. +type HealthCheckConf struct { + // HealthCheckType specifies what protocol to use for health checking. + // Valid values include "tcp", "http", and "". If this value is "", health + // checking will not be performed. By default, this value is "". + // + // If the type is "tcp", a connection will be attempted to the target + // server. If a connection cannot be established, the health check fails. + // + // If the type is "http", a GET request will be made to the endpoint + // specified by HealthCheckURL. If the response is not a 200, the health + // check fails. + HealthCheckType string `ini:"health_check_type" json:"health_check_type"` // tcp | http + // HealthCheckTimeoutS specifies the number of seconds to wait for a health + // check attempt to connect. If the timeout is reached, this counts as a + // health check failure. By default, this value is 3. + HealthCheckTimeoutS int `ini:"health_check_timeout_s" json:"health_check_timeout_s"` + // HealthCheckMaxFailed specifies the number of allowed failures before the + // is stopped. By default, this value is 1. + HealthCheckMaxFailed int `ini:"health_check_max_failed" json:"health_check_max_failed"` + // HealthCheckIntervalS specifies the time in seconds between health + // checks. By default, this value is 10. + HealthCheckIntervalS int `ini:"health_check_interval_s" json:"health_check_interval_s"` + // HealthCheckURL specifies the address to send health checks to if the + // health check type is "http". + HealthCheckURL string `ini:"health_check_url" json:"health_check_url"` + // HealthCheckAddr specifies the address to connect to if the health check + // type is "tcp". + HealthCheckAddr string `ini:"-"` +} + +// BaseProxyConf provides configuration info that is common to all types. +type BaseProxyConf struct { + // ProxyName is the name of this + ProxyName string `ini:"name" json:"name"` + // ProxyType specifies the type of this Valid values include "tcp", + // "udp", "http", "https", "stcp", and "xtcp". By default, this value is + // "tcp". + ProxyType string `ini:"type" json:"type"` + + // UseEncryption controls whether or not communication with the server will + // be encrypted. Encryption is done using the tokens supplied in the server + // and client configuration. By default, this value is false. + UseEncryption bool `ini:"use_encryption" json:"use_encryption"` + // UseCompression controls whether or not communication with the server + // will be compressed. By default, this value is false. + UseCompression bool `ini:"use_compression" json:"use_compression"` + // Group specifies which group the is a part of. The server will use + // this information to load balance proxies in the same group. If the value + // is "", this will not be in a group. By default, this value is "". + Group string `ini:"group" json:"group"` + // GroupKey specifies a group key, which should be the same among proxies + // of the same group. By default, this value is "". + GroupKey string `ini:"group_key" json:"group_key"` + + // ProxyProtocolVersion specifies which protocol version to use. Valid + // values include "v1", "v2", and "". If the value is "", a protocol + // version will be automatically selected. By default, this value is "". + ProxyProtocolVersion string `ini:"proxy_protocol_version" json:"proxy_protocol_version"` + + // BandwidthLimit limit the bandwidth + // 0 means no limit + BandwidthLimit BandwidthQuantity `ini:"bandwidth_limit" json:"bandwidth_limit"` + + // meta info for each proxy + Metas map[string]string `ini:"-" json:"metas"` + + // TODO: LocalSvrConf => LocalAppConf + LocalSvrConf `ini:",extends" json:"inline"` + HealthCheckConf `ini:",extends" json:"inline"` +} + +type DomainConf struct { + CustomDomains []string `ini:"custom_domains" json:"custom_domains"` + SubDomain string `ini:"subdomain" json:"subdomain"` +} + +// HTTP +type HTTPProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + DomainConf `ini:",extends" json:"inline"` + + Locations []string `ini:"locations" json:"locations"` + HTTPUser string `ini:"http_user" json:"http_user"` + HTTPPwd string `ini:"http_pwd" json:"http_pwd"` + HostHeaderRewrite string `ini:"host_header_rewrite" json:"host_header_rewrite"` + Headers map[string]string `ini:"-" json:"headers"` +} + +// HTTPS +type HTTPSProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + DomainConf `ini:",extends" json:"inline"` +} + +// TCP +type TCPProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + RemotePort int `ini:"remote_port" json:"remote_port"` +} + +// TCPMux +type TCPMuxProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + DomainConf `ini:",extends" json:"inline"` + + Multiplexer string `ini:"multiplexer"` +} + +// STCP +type STCPProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + + Role string `ini:"role" json:"role"` + Sk string `ini:"sk" json:"sk"` +} + +// XTCP +type XTCPProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + + Role string `ini:"role" json:"role"` + Sk string `ini:"sk" json:"sk"` +} + +// UDP +type UDPProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + + RemotePort int `ini:"remote_port" json:"remote_port"` +} + +// SUDP +type SUDPProxyConf struct { + BaseProxyConf `ini:",extends" json:"inline"` + + Role string `ini:"role" json:"role"` + Sk string `ini:"sk" json:"sk"` +} + // Proxy Conf Loader // DefaultProxyConf creates a empty ProxyConf object by proxyType. // If proxyType doesn't exist, return nil. @@ -54,23 +245,17 @@ func DefaultProxyConf(proxyType string) ProxyConf { case consts.STCPProxy: conf = &STCPProxyConf{ BaseProxyConf: defaultBaseProxyConf(proxyType), - STCPProxySpec: STCPProxySpec{ - Role: "server", - }, + Role: "server", } case consts.XTCPProxy: conf = &XTCPProxyConf{ BaseProxyConf: defaultBaseProxyConf(proxyType), - XTCPProxySpec: XTCPProxySpec{ - Role: "server", - }, + Role: "server", } case consts.SUDPProxy: conf = &SUDPProxyConf{ BaseProxyConf: defaultBaseProxyConf(proxyType), - SUDPProxySpec: SUDPProxySpec{ - Role: "server", - }, + Role: "server", } default: return nil @@ -304,9 +489,21 @@ func (cfg *HealthCheckConf) checkForCli() error { return nil } -// TCP -var _ ProxyConf = &TCPProxyConf{} +func preUnmarshalFromIni(cfg ProxyConf, prefix string, name string, section *ini.Section) error { + err := section.MapTo(cfg) + if err != nil { + return err + } + err = cfg.GetBaseInfo().decorate(prefix, name, section) + if err != nil { + return err + } + + return nil +} + +// TCP func (cfg *TCPProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*TCPProxyConf) if !ok { @@ -318,7 +515,7 @@ func (cfg *TCPProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.TCPProxySpec, cmpConf.TCPProxySpec) { + if cfg.RemotePort != cmpConf.RemotePort { return false } @@ -333,12 +530,7 @@ func (cfg *TCPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { } func (cfg *TCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -370,8 +562,6 @@ func (cfg *TCPProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { } // TCPMux -var _ ProxyConf = &TCPMuxProxyConf{} - func (cfg *TCPMuxProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*TCPMuxProxyConf) if !ok { @@ -383,7 +573,11 @@ func (cfg *TCPMuxProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.TCPMuxProxySpec, cmpConf.TCPMuxProxySpec) { + if !reflect.DeepEqual(cfg.DomainConf, cmpConf.DomainConf) { + return false + } + + if cfg.Multiplexer != cmpConf.Multiplexer { return false } @@ -391,12 +585,7 @@ func (cfg *TCPMuxProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *TCPMuxProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -459,8 +648,6 @@ func (cfg *TCPMuxProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) } // UDP -var _ ProxyConf = &UDPProxyConf{} - func (cfg *UDPProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*UDPProxyConf) if !ok { @@ -472,7 +659,7 @@ func (cfg *UDPProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.UDPProxySpec, cmpConf.UDPProxySpec) { + if cfg.RemotePort != cmpConf.RemotePort { return false } @@ -480,12 +667,7 @@ func (cfg *UDPProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *UDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -524,8 +706,6 @@ func (cfg *UDPProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { } // HTTP -var _ ProxyConf = &HTTPProxyConf{} - func (cfg *HTTPProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*HTTPProxyConf) if !ok { @@ -537,7 +717,15 @@ func (cfg *HTTPProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.HTTPProxySpec, cmpConf.HTTPProxySpec) { + if !reflect.DeepEqual(cfg.DomainConf, cmpConf.DomainConf) { + return false + } + + if !reflect.DeepEqual(cfg.Locations, cmpConf.Locations) || + cfg.HTTPUser != cmpConf.HTTPUser || + cfg.HTTPPwd != cmpConf.HTTPPwd || + cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite || + !reflect.DeepEqual(cfg.Headers, cmpConf.Headers) { return false } @@ -545,12 +733,7 @@ func (cfg *HTTPProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *HTTPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -614,8 +797,6 @@ func (cfg *HTTPProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { } // HTTPS -var _ ProxyConf = &HTTPSProxyConf{} - func (cfg *HTTPSProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*HTTPSProxyConf) if !ok { @@ -627,7 +808,7 @@ func (cfg *HTTPSProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.HTTPSProxySpec, cmpConf.HTTPSProxySpec) { + if !reflect.DeepEqual(cfg.DomainConf, cmpConf.DomainConf) { return false } @@ -635,12 +816,7 @@ func (cfg *HTTPSProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *HTTPSProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -693,8 +869,6 @@ func (cfg *HTTPSProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { } // SUDP -var _ ProxyConf = &SUDPProxyConf{} - func (cfg *SUDPProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*SUDPProxyConf) if !ok { @@ -706,7 +880,8 @@ func (cfg *SUDPProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.SUDPProxySpec, cmpConf.SUDPProxySpec) { + if cfg.Role != cmpConf.Role || + cfg.Sk != cmpConf.Sk { return false } @@ -714,12 +889,7 @@ func (cfg *SUDPProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *SUDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -762,8 +932,6 @@ func (cfg *SUDPProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { } // STCP -var _ ProxyConf = &STCPProxyConf{} - func (cfg *STCPProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*STCPProxyConf) if !ok { @@ -775,7 +943,8 @@ func (cfg *STCPProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.STCPProxySpec, cmpConf.STCPProxySpec) { + if cfg.Role != cmpConf.Role || + cfg.Sk != cmpConf.Sk { return false } @@ -783,12 +952,7 @@ func (cfg *STCPProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *STCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } @@ -834,8 +998,6 @@ func (cfg *STCPProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { } // XTCP -var _ ProxyConf = &XTCPProxyConf{} - func (cfg *XTCPProxyConf) Compare(cmp ProxyConf) bool { cmpConf, ok := cmp.(*XTCPProxyConf) if !ok { @@ -847,7 +1009,8 @@ func (cfg *XTCPProxyConf) Compare(cmp ProxyConf) bool { } // Add custom logic equal if exists. - if !reflect.DeepEqual(cfg.XTCPProxySpec, cmpConf.XTCPProxySpec) { + if cfg.Role != cmpConf.Role || + cfg.Sk != cmpConf.Sk { return false } @@ -855,12 +1018,7 @@ func (cfg *XTCPProxyConf) Compare(cmp ProxyConf) bool { } func (cfg *XTCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) - if err != nil { - return err - } - - err = cfg.BaseProxyConf.decorate(prefix, name, section) + err := preUnmarshalFromIni(cfg, prefix, name, section) if err != nil { return err } diff --git a/pkg/config/proxy_test.go b/pkg/config/proxy_test.go index fef6a0cc..68c87b95 100644 --- a/pkg/config/proxy_test.go +++ b/pkg/config/proxy_test.go @@ -35,6 +35,12 @@ var ( testProxyPrefix = "test." ) +func Test_Proxy_Interface(t *testing.T) { + for name := range proxyConfTypeMap { + NewConfByType(name) + } +} + func Test_Proxy_UnmarshalFromIni(t *testing.T) { assert := assert.New(t) @@ -89,9 +95,7 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { HealthCheckAddr: "127.0.0.9:29", }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6009, - }, + RemotePort: 6009, }, }, { @@ -112,9 +116,7 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { LocalPort: 29, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 9, - }, + RemotePort: 9, }, }, { @@ -139,9 +141,7 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { LocalPort: 59, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6009, - }, + RemotePort: 6009, }, }, { @@ -184,18 +184,16 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { HealthCheckURL: "http://127.0.0.9:89/status", }, }, - HTTPProxySpec: HTTPProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"web02.yourdomain.com"}, - SubDomain: "web01", - }, - Locations: []string{"/", "/pic"}, - HTTPUser: "admin", - HTTPPwd: "admin", - HostHeaderRewrite: "example.com", - Headers: map[string]string{ - "X-From-Where": "frp", - }, + DomainConf: DomainConf{ + CustomDomains: []string{"web02.yourdomain.com"}, + SubDomain: "web01", + }, + Locations: []string{"/", "/pic"}, + HTTPUser: "admin", + HTTPPwd: "admin", + HostHeaderRewrite: "example.com", + Headers: map[string]string{ + "X-From-Where": "frp", }, }, }, @@ -224,11 +222,9 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { }, ProxyProtocolVersion: "v2", }, - HTTPSProxySpec: HTTPSProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"web02.yourdomain.com"}, - SubDomain: "web01", - }, + DomainConf: DomainConf{ + CustomDomains: []string{"web02.yourdomain.com"}, + SubDomain: "web01", }, }, }, @@ -252,10 +248,8 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { LocalPort: 22, }, }, - STCPProxySpec: STCPProxySpec{ - Role: "server", - Sk: "abcdefg", - }, + Role: "server", + Sk: "abcdefg", }, }, { @@ -278,10 +272,8 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { LocalPort: 22, }, }, - XTCPProxySpec: XTCPProxySpec{ - Role: "server", - Sk: "abcdefg", - }, + Role: "server", + Sk: "abcdefg", }, }, { @@ -303,13 +295,11 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) { LocalPort: 10701, }, }, - TCPMuxProxySpec: TCPMuxProxySpec{ - DomainConf: DomainConf{ - CustomDomains: []string{"tunnel1"}, - SubDomain: "", - }, - Multiplexer: "httpconnect", + DomainConf: DomainConf{ + CustomDomains: []string{"tunnel1"}, + SubDomain: "", }, + Multiplexer: "httpconnect", }, }, } @@ -359,9 +349,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { LocalPort: 6010, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6010, - }, + RemotePort: 6010, }, "tcp_port_1": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -372,9 +360,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { LocalPort: 6011, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6011, - }, + RemotePort: 6011, }, "tcp_port_2": &TCPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -385,9 +371,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { LocalPort: 6019, }, }, - TCPProxySpec: TCPProxySpec{ - RemotePort: 6019, - }, + RemotePort: 6019, }, }, }, @@ -414,9 +398,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { LocalPort: 6000, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6000, - }, + RemotePort: 6000, }, "udp_port_1": &UDPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -429,9 +411,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { LocalPort: 6010, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6010, - }, + RemotePort: 6010, }, "udp_port_2": &UDPProxyConf{ BaseProxyConf: BaseProxyConf{ @@ -444,9 +424,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { LocalPort: 6011, }, }, - UDPProxySpec: UDPProxySpec{ - RemotePort: 6011, - }, + RemotePort: 6011, }, }, }, @@ -460,7 +438,7 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) { actual := make(map[string]ProxyConf) s := f.Section(c.sname) - err = appendTemplates(f, s) + err = renderRangeProxyTemplates(f, s) assert.NoError(err) f.DeleteSection(ini.DefaultSection) diff --git a/pkg/config/server.go b/pkg/config/server.go index 888acba3..10d5b33d 100644 --- a/pkg/config/server.go +++ b/pkg/config/server.go @@ -25,6 +25,146 @@ import ( "gopkg.in/ini.v1" ) +// ServerCommonConf contains information for a server service. It is +// recommended to use GetDefaultServerConf instead of creating this object +// directly, so that all unspecified fields have reasonable default values. +type ServerCommonConf struct { + auth.ServerConfig `ini:",extends" json:"inline"` + + // BindAddr specifies the address that the server binds to. By default, + // this value is "0.0.0.0". + BindAddr string `ini:"bind_addr" json:"bind_addr"` + // BindPort specifies the port that the server listens on. By default, this + // value is 7000. + BindPort int `ini:"bind_port" json:"bind_port"` + // BindUDPPort specifies the UDP port that the server listens on. If this + // value is 0, the server will not listen for UDP connections. By default, + // this value is 0 + BindUDPPort int `ini:"bind_udp_port" json:"bind_udp_port"` + // KCPBindPort specifies the KCP port that the server listens on. If this + // value is 0, the server will not listen for KCP connections. By default, + // this value is 0. + KCPBindPort int `ini:"kcp_bind_port" json:"kcp_bind_port"` + // ProxyBindAddr specifies the address that the proxy binds to. This value + // may be the same as BindAddr. By default, this value is "0.0.0.0". + ProxyBindAddr string `ini:"proxy_bind_addr" json:"proxy_bind_addr"` + // VhostHTTPPort specifies the port that the server listens for HTTP Vhost + // requests. If this value is 0, the server will not listen for HTTP + // requests. By default, this value is 0. + VhostHTTPPort int `ini:"vhost_http_port" json:"vhost_http_port"` + // VhostHTTPSPort specifies the port that the server listens for HTTPS + // Vhost requests. If this value is 0, the server will not listen for HTTPS + // requests. By default, this value is 0. + VhostHTTPSPort int `ini:"vhost_https_port" json:"vhost_https_port"` + // TCPMuxHTTPConnectPort specifies the port that the server listens for TCP + // HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP + // requests on one single port. If it's not - it will listen on this value for + // HTTP CONNECT requests. By default, this value is 0. + TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port"` + // VhostHTTPTimeout specifies the response header timeout for the Vhost + // HTTP server, in seconds. By default, this value is 60. + VhostHTTPTimeout int64 `ini:"vhost_http_timeout" json:"vhost_http_timeout"` + // DashboardAddr specifies the address that the dashboard binds to. By + // default, this value is "0.0.0.0". + DashboardAddr string `ini:"dashboard_addr" json:"dashboard_addr"` + // DashboardPort specifies the port that the dashboard listens on. If this + // value is 0, the dashboard will not be started. By default, this value is + // 0. + DashboardPort int `ini:"dashboard_port" json:"dashboard_port"` + // DashboardUser specifies the username that the dashboard will use for + // login. By default, this value is "admin". + DashboardUser string `ini:"dashboard_user" json:"dashboard_user"` + // DashboardUser specifies the password that the dashboard will use for + // login. By default, this value is "admin". + DashboardPwd string `ini:"dashboard_pwd" json:"dashboard_pwd"` + // EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} + // in /metrics api. + EnablePrometheus bool `ini:"enable_prometheus" json:"enable_prometheus"` + // AssetsDir specifies the local directory that the dashboard will load + // resources from. If this value is "", assets will be loaded from the + // bundled executable using statik. By default, this value is "". + AssetsDir string `ini:"assets_dir" json:"assets_dir"` + // LogFile specifies a file where logs will be written to. This value will + // only be used if LogWay is set appropriately. By default, this value is + // "console". + LogFile string `ini:"log_file" json:"log_file"` + // LogWay specifies the way logging is managed. Valid values are "console" + // or "file". If "console" is used, logs will be printed to stdout. If + // "file" is used, logs will be printed to LogFile. By default, this value + // is "console". + LogWay string `ini:"log_way" json:"log_way"` + // LogLevel specifies the minimum log level. Valid values are "trace", + // "debug", "info", "warn", and "error". By default, this value is "info". + LogLevel string `ini:"log_level" json:"log_level"` + // LogMaxDays specifies the maximum number of days to store log information + // before deletion. This is only used if LogWay == "file". By default, this + // value is 0. + LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"` + // DisableLogColor disables log colors when LogWay == "console" when set to + // true. By default, this value is false. + DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"` + // DetailedErrorsToClient defines whether to send the specific error (with + // debug info) to frpc. By default, this value is true. + DetailedErrorsToClient bool `ini:"detailed_errors_to_client" json:"detailed_errors_to_client"` + + // SubDomainHost specifies the domain that will be attached to sub-domains + // requested by the client when using Vhost proxying. For example, if this + // value is set to "frps.com" and the client requested the subdomain + // "test", the resulting URL would be "test.frps.com". By default, this + // value is "". + SubDomainHost string `ini:"subdomain_host" json:"subdomain_host"` + // TCPMux toggles TCP stream multiplexing. This allows multiple requests + // from a client to share a single TCP connection. By default, this value + // is true. + TCPMux bool `ini:"tcp_mux" json:"tcp_mux"` + // Custom404Page specifies a path to a custom 404 page to display. If this + // value is "", a default page will be displayed. By default, this value is + // "". + Custom404Page string `ini:"custom_404_page" json:"custom_404_page"` + + // AllowPorts specifies a set of ports that clients are able to proxy to. + // If the length of this value is 0, all ports are allowed. By default, + // this value is an empty set. + AllowPorts map[int]struct{} `ini:"-" json:"-"` + // MaxPoolCount specifies the maximum pool size for each proxy. By default, + // this value is 5. + MaxPoolCount int64 `ini:"max_pool_count" json:"max_pool_count"` + // MaxPortsPerClient specifies the maximum number of ports a single client + // may proxy to. If this value is 0, no limit will be applied. By default, + // this value is 0. + MaxPortsPerClient int64 `ini:"max_ports_per_client" json:"max_ports_per_client"` + // TLSOnly specifies whether to only accept TLS-encrypted connections. + // By default, the value is false. + TLSOnly bool `ini:"tls_only" json:"tls_only"` + // TLSCertFile specifies the path of the cert file that the server will + // load. If "tls_cert_file", "tls_key_file" are valid, the server will use this + // supplied tls configuration. Otherwise, the server will use the tls + // configuration generated by itself. + TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"` + // TLSKeyFile specifies the path of the secret key that the server will + // load. If "tls_cert_file", "tls_key_file" are valid, the server will use this + // supplied tls configuration. Otherwise, the server will use the tls + // configuration generated by itself. + TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"` + // TLSTrustedCaFile specifies the paths of the client cert files that the + // server will load. It only works when "tls_only" is true. If + // "tls_trusted_ca_file" is valid, the server will verify each client's + // certificate. + TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"` + // HeartBeatTimeout specifies the maximum time to wait for a heartbeat + // before terminating the connection. It is not recommended to change this + // value. By default, this value is 90. + HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"` + // UserConnTimeout specifies the maximum time to wait for a work + // connection. By default, this value is 10. + UserConnTimeout int64 `ini:"user_conn_timeout" json:"user_conn_timeout"` + // HTTPPlugins specify the server plugins support HTTP protocol. + HTTPPlugins map[string]plugin.HTTPPluginOptions `ini:"-" json:"http_plugins"` + // UDPPacketSize specifies the UDP packet size + // By default, this value is 1500 + UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"` +} + // GetDefaultServerConf returns a server configuration with reasonable // defaults. func GetDefaultServerConf() ServerCommonConf { diff --git a/pkg/config/types.go b/pkg/config/types.go index 1def458f..062cc746 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -1,4 +1,4 @@ -// Copyright 2020 The frp Authors +// Copyright 2019 fatedier, fatedier@gmail.com // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,508 +15,107 @@ package config import ( - "reflect" - - "github.com/fatedier/frp/pkg/auth" - "github.com/fatedier/frp/pkg/consts" - "github.com/fatedier/frp/pkg/msg" - plugin "github.com/fatedier/frp/pkg/plugin/server" - - "gopkg.in/ini.v1" + "encoding/json" + "errors" + "strconv" + "strings" ) -// ClientCommonConf contains information for a client service. It is -// recommended to use GetDefaultClientConf instead of creating this object -// directly, so that all unspecified fields have reasonable default values. -type ClientCommonConf struct { - auth.ClientConfig `ini:",,,,extends",json:"inline"` - // ServerAddr specifies the address of the server to connect to. By - // default, this value is "0.0.0.0". - ServerAddr string `ini:"server_addr",josn:"server_addr"` - // ServerPort specifies the port to connect to the server on. By default, - // this value is 7000. - ServerPort int `ini:"server_port",json:"server_port"` - // HTTPProxy specifies a proxy address to connect to the server through. If - // this value is "", the server will be connected to directly. By default, - // this value is read from the "http_proxy" environment variable. - HTTPProxy string `ini:"http_proxy",json:"http_proxy"` - // LogFile specifies a file where logs will be written to. This value will - // only be used if LogWay is set appropriately. By default, this value is - // "console". - LogFile string `ini:"log_file",json:"log_file"` - // LogWay specifies the way logging is managed. Valid values are "console" - // or "file". If "console" is used, logs will be printed to stdout. If - // "file" is used, logs will be printed to LogFile. By default, this value - // is "console". - LogWay string `ini:"log_way",json:"log_way"` - // LogLevel specifies the minimum log level. Valid values are "trace", - // "debug", "info", "warn", and "error". By default, this value is "info". - LogLevel string `ini:"log_level",json:"log_level"` - // LogMaxDays specifies the maximum number of days to store log information - // before deletion. This is only used if LogWay == "file". By default, this - // value is 0. - LogMaxDays int64 `ini:"log_max_days",json:"log_max_days"` - // DisableLogColor disables log colors when LogWay == "console" when set to - // true. By default, this value is false. - DisableLogColor bool `ini:"disable_log_color",json:"disable_log_color"` - // AdminAddr specifies the address that the admin server binds to. By - // default, this value is "127.0.0.1". - AdminAddr string `ini:"admin_addr",json:"admin_addr"` - // AdminPort specifies the port for the admin server to listen on. If this - // value is 0, the admin server will not be started. By default, this value - // is 0. - AdminPort int `ini:"admin_port",json:"admin_port"` - // AdminUser specifies the username that the admin server will use for - // login. By default, this value is "admin". - AdminUser string `ini:"admin_user",json:"admin_user"` - // AdminPwd specifies the password that the admin server will use for - // login. By default, this value is "admin". - AdminPwd string `ini:"admin_pwd",json:"admin_pwd"` - // AssetsDir specifies the local directory that the admin server will load - // resources from. If this value is "", assets will be loaded from the - // bundled executable using statik. By default, this value is "". - AssetsDir string `ini:"assets_dir",json:"assets_dir"` - // PoolCount specifies the number of connections the client will make to - // the server in advance. By default, this value is 0. - PoolCount int `ini:"pool_count",json:"pool_count"` - // TCPMux toggles TCP stream multiplexing. This allows multiple requests - // from a client to share a single TCP connection. If this value is true, - // the server must have TCP multiplexing enabled as well. By default, this - // value is true. - TCPMux bool `ini:"tcp_mux",json:"tcp_mux"` - // User specifies a prefix for proxy names to distinguish them from other - // clients. If this value is not "", proxy names will automatically be - // changed to "{user}.{proxy_name}". By default, this value is "". - User string `ini:"user",json:"user"` - // DNSServer specifies a DNS server address for FRPC to use. If this value - // is "", the default DNS will be used. By default, this value is "". - DNSServer string `ini:"dns_server",json:"dns_server"` - // LoginFailExit controls whether or not the client should exit after a - // failed login attempt. If false, the client will retry until a login - // attempt succeeds. By default, this value is true. - LoginFailExit bool `ini:"login_fail_exit",json:"login_fail_exit"` - // Start specifies a set of enabled proxies by name. If this set is empty, - // all supplied proxies are enabled. By default, this value is an empty - // set. - Start []string `ini:"start",json:"start"` - //Start map[string]struct{} `json:"start"` - // Protocol specifies the protocol to use when interacting with the server. - // Valid values are "tcp", "kcp" and "websocket". By default, this value - // is "tcp". - Protocol string `ini:"protocol",json:"protocol"` - // TLSEnable specifies whether or not TLS should be used when communicating - // with the server. If "tls_cert_file" and "tls_key_file" are valid, - // client will load the supplied tls configuration. - TLSEnable bool `ini:"tls_enable",json:"tls_enable"` - // ClientTLSCertPath specifies the path of the cert file that client will - // load. It only works when "tls_enable" is true and "tls_key_file" is valid. - TLSCertFile string `ini:"tls_cert_file",json:"tls_cert_file"` - // ClientTLSKeyPath specifies the path of the secret key file that client - // will load. It only works when "tls_enable" is true and "tls_cert_file" - // are valid. - TLSKeyFile string `ini:"tls_key_file",json:"tls_key_file"` - // TrustedCaFile specifies the path of the trusted ca file that will load. - // It only works when "tls_enable" is valid and tls configuration of server - // has been specified. - TLSTrustedCaFile string `ini:"tls_trusted_ca_file",json:"tls_trusted_ca_file"` - // HeartBeatInterval specifies at what interval heartbeats are sent to the - // server, in seconds. It is not recommended to change this value. By - // default, this value is 30. - HeartbeatInterval int64 `ini:"heartbeat_interval",json:"heartbeat_interval"` - // HeartBeatTimeout specifies the maximum allowed heartbeat response delay - // before the connection is terminated, in seconds. It is not recommended - // to change this value. By default, this value is 90. - HeartbeatTimeout int64 `ini:"heartbeat_timeout",json:"heartbeat_timeout"` - // Client meta info - Metas map[string]string `ini:"-",json:"metas"` - // UDPPacketSize specifies the udp packet size - // By default, this value is 1500 - UDPPacketSize int64 `ini:"udp_packet_size",json:"udp_packet_size"` +const ( + MB = 1024 * 1024 + KB = 1024 +) + +type BandwidthQuantity struct { + s string // MB or KB + + i int64 // bytes } -// ServerCommonConf contains information for a server service. It is -// recommended to use GetDefaultServerConf instead of creating this object -// directly, so that all unspecified fields have reasonable default values. -type ServerCommonConf struct { - auth.ServerConfig `ini:",,,,extends",json:"inline"` - // BindAddr specifies the address that the server binds to. By default, - // this value is "0.0.0.0". - BindAddr string `ini:"bind_addr",json:"bind_addr"` - // BindPort specifies the port that the server listens on. By default, this - // value is 7000. - BindPort int `ini:"bind_port",json:"bind_port"` - // BindUDPPort specifies the UDP port that the server listens on. If this - // value is 0, the server will not listen for UDP connections. By default, - // this value is 0 - BindUDPPort int `ini:"bind_udp_port",json:"bind_udp_port"` - // KCPBindPort specifies the KCP port that the server listens on. If this - // value is 0, the server will not listen for KCP connections. By default, - // this value is 0. - KCPBindPort int `ini:"kcp_bind_port",json:"kcp_bind_port"` - // ProxyBindAddr specifies the address that the proxy binds to. This value - // may be the same as BindAddr. By default, this value is "0.0.0.0". - ProxyBindAddr string `ini:"proxy_bind_addr",json:"proxy_bind_addr"` - // VhostHTTPPort specifies the port that the server listens for HTTP Vhost - // requests. If this value is 0, the server will not listen for HTTP - // requests. By default, this value is 0. - VhostHTTPPort int `ini:"vhost_http_port",json:"vhost_http_port"` - // VhostHTTPSPort specifies the port that the server listens for HTTPS - // Vhost requests. If this value is 0, the server will not listen for HTTPS - // requests. By default, this value is 0. - VhostHTTPSPort int `ini:"vhost_https_port",json:"vhost_https_port"` - // TCPMuxHTTPConnectPort specifies the port that the server listens for TCP - // HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP - // requests on one single port. If it's not - it will listen on this value for - // HTTP CONNECT requests. By default, this value is 0. - TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port",json:"tcpmux_httpconnect_port"` - // VhostHTTPTimeout specifies the response header timeout for the Vhost - // HTTP server, in seconds. By default, this value is 60. - VhostHTTPTimeout int64 `ini:"vhost_http_timeout",json:"vhost_http_timeout"` - // DashboardAddr specifies the address that the dashboard binds to. By - // default, this value is "0.0.0.0". - DashboardAddr string `ini:"dashboard_addr",json:"dashboard_addr"` - // DashboardPort specifies the port that the dashboard listens on. If this - // value is 0, the dashboard will not be started. By default, this value is - // 0. - DashboardPort int `ini:"dashboard_port",json:"dashboard_port"` - // DashboardUser specifies the username that the dashboard will use for - // login. By default, this value is "admin". - DashboardUser string `ini:"dashboard_user",json:"dashboard_user"` - // DashboardUser specifies the password that the dashboard will use for - // login. By default, this value is "admin". - DashboardPwd string `ini:"dashboard_pwd",json:"dashboard_pwd"` - // EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} - // in /metrics api. - EnablePrometheus bool `ini:"enable_prometheus",json:"enable_prometheus"` - // AssetsDir specifies the local directory that the dashboard will load - // resources from. If this value is "", assets will be loaded from the - // bundled executable using statik. By default, this value is "". - AssetsDir string `ini:"assets_dir",json:"assets_dir"` - // LogFile specifies a file where logs will be written to. This value will - // only be used if LogWay is set appropriately. By default, this value is - // "console". - LogFile string `ini:"log_file",json:"log_file"` - // LogWay specifies the way logging is managed. Valid values are "console" - // or "file". If "console" is used, logs will be printed to stdout. If - // "file" is used, logs will be printed to LogFile. By default, this value - // is "console". - LogWay string `ini:"log_way",json:"log_way"` - // LogLevel specifies the minimum log level. Valid values are "trace", - // "debug", "info", "warn", and "error". By default, this value is "info". - LogLevel string `ini:"log_level",json:"log_level"` - // LogMaxDays specifies the maximum number of days to store log information - // before deletion. This is only used if LogWay == "file". By default, this - // value is 0. - LogMaxDays int64 `ini:"log_max_days",json:"log_max_days"` - // DisableLogColor disables log colors when LogWay == "console" when set to - // true. By default, this value is false. - DisableLogColor bool `ini:"disable_log_color",json:"disable_log_color"` - // DetailedErrorsToClient defines whether to send the specific error (with - // debug info) to frpc. By default, this value is true. - DetailedErrorsToClient bool `ini:"detailed_errors_to_client",json:"detailed_errors_to_client"` - - // SubDomainHost specifies the domain that will be attached to sub-domains - // requested by the client when using Vhost proxying. For example, if this - // value is set to "frps.com" and the client requested the subdomain - // "test", the resulting URL would be "test.frps.com". By default, this - // value is "". - SubDomainHost string `ini:"subdomain_host",json:"subdomain_host"` - // TCPMux toggles TCP stream multiplexing. This allows multiple requests - // from a client to share a single TCP connection. By default, this value - // is true. - TCPMux bool `ini:"tcp_mux",json:"tcp_mux"` - // Custom404Page specifies a path to a custom 404 page to display. If this - // value is "", a default page will be displayed. By default, this value is - // "". - Custom404Page string `ini:"custom_404_page",json:"custom_404_page"` - - // AllowPorts specifies a set of ports that clients are able to proxy to. - // If the length of this value is 0, all ports are allowed. By default, - // this value is an empty set. - AllowPorts map[int]struct{} `ini:"-",json:"-"` - // MaxPoolCount specifies the maximum pool size for each proxy. By default, - // this value is 5. - MaxPoolCount int64 `ini:"max_pool_count",json:"max_pool_count"` - // MaxPortsPerClient specifies the maximum number of ports a single client - // may proxy to. If this value is 0, no limit will be applied. By default, - // this value is 0. - MaxPortsPerClient int64 `ini:"max_ports_per_client",json:"max_ports_per_client"` - // TLSOnly specifies whether to only accept TLS-encrypted connections. - // By default, the value is false. - TLSOnly bool `ini:"tls_only",json:"tls_only"` - // TLSCertFile specifies the path of the cert file that the server will - // load. If "tls_cert_file", "tls_key_file" are valid, the server will use this - // supplied tls configuration. Otherwise, the server will use the tls - // configuration generated by itself. - TLSCertFile string `ini:"tls_cert_file",json:"tls_cert_file"` - // TLSKeyFile specifies the path of the secret key that the server will - // load. If "tls_cert_file", "tls_key_file" are valid, the server will use this - // supplied tls configuration. Otherwise, the server will use the tls - // configuration generated by itself. - TLSKeyFile string `ini:"tls_key_file",json:"tls_key_file"` - // TLSTrustedCaFile specifies the paths of the client cert files that the - // server will load. It only works when "tls_only" is true. If - // "tls_trusted_ca_file" is valid, the server will verify each client's - // certificate. - TLSTrustedCaFile string `ini:"tls_trusted_ca_file",json:"tls_trusted_ca_file"` - // HeartBeatTimeout specifies the maximum time to wait for a heartbeat - // before terminating the connection. It is not recommended to change this - // value. By default, this value is 90. - HeartbeatTimeout int64 `ini:"heartbeat_timeout",json:"heartbeat_timeout"` - // UserConnTimeout specifies the maximum time to wait for a work - // connection. By default, this value is 10. - UserConnTimeout int64 `ini:"user_conn_timeout",json:"user_conn_timeout"` - // HTTPPlugins specify the server plugins support HTTP protocol. - HTTPPlugins map[string]plugin.HTTPPluginOptions `ini:"-",json:"http_plugins"` - // UDPPacketSize specifies the UDP packet size - // By default, this value is 1500 - UDPPacketSize int64 `ini:"udp_packet_size",json:"udp_packet_size"` -} - -// Proxy -var ( - ProxyConfTypeMap = map[string]reflect.Type{ - consts.TCPProxy: reflect.TypeOf(TCPProxyConf{}), - consts.TCPMuxProxy: reflect.TypeOf(TCPMuxProxyConf{}), - consts.UDPProxy: reflect.TypeOf(UDPProxyConf{}), - consts.HTTPProxy: reflect.TypeOf(HTTPProxyConf{}), - consts.HTTPSProxy: reflect.TypeOf(HTTPSProxyConf{}), - consts.STCPProxy: reflect.TypeOf(STCPProxyConf{}), - consts.XTCPProxy: reflect.TypeOf(XTCPProxyConf{}), - consts.SUDPProxy: reflect.TypeOf(SUDPProxyConf{}), +func NewBandwidthQuantity(s string) (BandwidthQuantity, error) { + q := BandwidthQuantity{} + err := q.UnmarshalString(s) + if err != nil { + return q, err } -) - -type ProxyConf interface { - GetBaseInfo() *BaseProxyConf - UnmarshalFromMsg(*msg.NewProxy) - UnmarshalFromIni(string, string, *ini.Section) error - MarshalToMsg(*msg.NewProxy) - CheckForCli() error - CheckForSvr(ServerCommonConf) error - Compare(ProxyConf) bool + return q, nil } -// LocalSvrConf configures what location the client will to, or what -// plugin will be used. -type LocalSvrConf struct { - // LocalIP specifies the IP address or host name to to. - LocalIP string `ini:"local_ip",json:"local_ip"` - // LocalPort specifies the port to to. - LocalPort int `ini:"local_port",json:"local_port"` - - // Plugin specifies what plugin should be used for ng. If this value - // is set, the LocalIp and LocalPort values will be ignored. By default, - // this value is "". - Plugin string `ini:"plugin",json:"plugin"` - // PluginParams specify parameters to be passed to the plugin, if one is - // being used. By default, this value is an empty map. - PluginParams map[string]string `ini:"-"` -} - -// HealthCheckConf configures health checking. This can be useful for load -// balancing purposes to detect and remove proxies to failing services. -type HealthCheckConf struct { - // HealthCheckType specifies what protocol to use for health checking. - // Valid values include "tcp", "http", and "". If this value is "", health - // checking will not be performed. By default, this value is "". - // - // If the type is "tcp", a connection will be attempted to the target - // server. If a connection cannot be established, the health check fails. - // - // If the type is "http", a GET request will be made to the endpoint - // specified by HealthCheckURL. If the response is not a 200, the health - // check fails. - HealthCheckType string `ini:"health_check_type",json:"health_check_type"` // tcp | http - // HealthCheckTimeoutS specifies the number of seconds to wait for a health - // check attempt to connect. If the timeout is reached, this counts as a - // health check failure. By default, this value is 3. - HealthCheckTimeoutS int `ini:"health_check_timeout_s",json:"health_check_timeout_s"` - // HealthCheckMaxFailed specifies the number of allowed failures before the - // is stopped. By default, this value is 1. - HealthCheckMaxFailed int `ini:"health_check_max_failed",json:"health_check_max_failed"` - // HealthCheckIntervalS specifies the time in seconds between health - // checks. By default, this value is 10. - HealthCheckIntervalS int `ini:"health_check_interval_s",json:"health_check_interval_s"` - // HealthCheckURL specifies the address to send health checks to if the - // health check type is "http". - HealthCheckURL string `ini:"health_check_url",json:"health_check_interval_s"` - // HealthCheckAddr specifies the address to connect to if the health check - // type is "tcp". - HealthCheckAddr string `ini:"-"` -} - -// BaseProxyConf provides configuration info that is common to all types. -type BaseProxyConf struct { - // ProxyName is the name of this - ProxyName string `ini:"name",json:"name"` - // ProxyType specifies the type of this Valid values include "tcp", - // "udp", "http", "https", "stcp", and "xtcp". By default, this value is - // "tcp". - ProxyType string `ini:"type",json:"type"` - - // UseEncryption controls whether or not communication with the server will - // be encrypted. Encryption is done using the tokens supplied in the server - // and client configuration. By default, this value is false. - UseEncryption bool `ini:"use_encryption",json:"use_encryption"` - // UseCompression controls whether or not communication with the server - // will be compressed. By default, this value is false. - UseCompression bool `ini:"use_compression",json:"use_compression"` - // Group specifies which group the is a part of. The server will use - // this information to load balance proxies in the same group. If the value - // is "", this will not be in a group. By default, this value is "". - Group string `ini:"group",json:"group"` - // GroupKey specifies a group key, which should be the same among proxies - // of the same group. By default, this value is "". - GroupKey string `ini:"group_key",json:"group_key"` - - // ProxyProtocolVersion specifies which protocol version to use. Valid - // values include "v1", "v2", and "". If the value is "", a protocol - // version will be automatically selected. By default, this value is "". - ProxyProtocolVersion string `ini:"proxy_protocol_version",json:"proxy_protocol_version"` - - // BandwidthLimit limit the bandwidth - // 0 means no limit - BandwidthLimit BandwidthQuantity `ini:"bandwidth_limit",json:"bandwidth_limit"` - - // meta info for each proxy - Metas map[string]string `ini:"-",json:"metas"` - - // TODO: LocalSvrConf => LocalAppConf - LocalSvrConf `ini:",,,,extends",json:"inline"` - HealthCheckConf `ini:",,,,extends",json:"inline"` -} - -type DomainConf struct { - CustomDomains []string `ini:"custom_domains",json:"custom_domains"` - SubDomain string `ini:"subdomain",json:"subdomain"` -} - -// HTTP -type HTTPProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - HTTPProxySpec `ini:",,,,extends",json:"inline"` -} - -type HTTPProxySpec struct { - DomainConf `ini:",,,,extends",json:"inline"` - Locations []string `ini:"locations",json:"locations"` - HTTPUser string `ini:"http_user",json:"http_user"` - HTTPPwd string `ini:"http_pwd",json:"http_pwd"` - HostHeaderRewrite string `ini:"host_header_rewrite",json:"host_header_rewrite"` - Headers map[string]string `ini:"-",json:"headers"` -} - -// HTTPS -type HTTPSProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - HTTPSProxySpec `ini:",,,,extends",json:"inline"` -} - -type HTTPSProxySpec struct { - DomainConf `ini:",,,,extends",json:"inline"` -} - -// TCP -type TCPProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - TCPProxySpec `ini:",,,,extends",json:"inline"` -} - -type TCPProxySpec struct { - RemotePort int `ini:"remote_port",json:"remote_port"` -} - -// TCPMux -type TCPMuxProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - TCPMuxProxySpec `ini:",,,,extends",json:"inline"` -} - -type TCPMuxProxySpec struct { - DomainConf `ini:",,,,extends",json:"inline"` - Multiplexer string `ini:"multiplexer"` -} - -// STCP -type STCPProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - STCPProxySpec `ini:",,,,extends",json:"inline"` -} - -type STCPProxySpec struct { - Role string `ini:"role",json:"role"` - Sk string `ini:"sk",json:"sk"` -} - -// XTCP -type XTCPProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - XTCPProxySpec `ini:",,,,extends",json:"inline"` -} - -type XTCPProxySpec struct { - Role string `ini:"role",json:"role"` - Sk string `ini:"sk",json:"sk"` -} - -// UDP -type UDPProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - UDPProxySpec `ini:",,,,extends",json:"inline"` -} - -type UDPProxySpec struct { - RemotePort int `ini:"remote_port",json:"remote_port"` -} - -// SUDP -type SUDPProxyConf struct { - BaseProxyConf `ini:",,,,extends",json:"inline"` - SUDPProxySpec `ini:",,,,extends",json:"inline"` -} - -type SUDPProxySpec struct { - Role string `ini:"role",json:"role"` - Sk string `ini:"sk",json:"sk"` -} - -// Visitor -var ( - VisitorConfTypeMap = map[string]reflect.Type{ - consts.STCPProxy: reflect.TypeOf(STCPVisitorConf{}), - consts.XTCPProxy: reflect.TypeOf(XTCPVisitorConf{}), - consts.SUDPProxy: reflect.TypeOf(SUDPVisitorConf{}), +func MustBandwidthQuantity(s string) BandwidthQuantity { + q := BandwidthQuantity{} + err := q.UnmarshalString(s) + if err != nil { + panic(err) } -) - -type VisitorConf interface { - GetBaseInfo() *BaseVisitorConf - Compare(cmp VisitorConf) bool - UnmarshalFromIni(prefix string, name string, section *ini.Section) error - Check() error + return q } -type BaseVisitorConf struct { - ProxyName string `ini:"name",json:"name"` - ProxyType string `ini:"type",json:"type"` - UseEncryption bool `ini:"use_encryption",json:"use_encryption"` - UseCompression bool `ini:"use_compression",json:"use_compression"` - Role string `ini:"role",json:"role"` - Sk string `ini:"sk",json:"sk"` - ServerName string `ini:"server_name",json:"server_name"` - BindAddr string `ini:"bind_addr",json:"bind_addr"` - BindPort int `ini:"bind_port",json:"bind_port"` +func (q *BandwidthQuantity) Equal(u *BandwidthQuantity) bool { + if q == nil && u == nil { + return true + } + if q != nil && u != nil { + return q.i == u.i + } + return false } -type SUDPVisitorConf struct { - BaseVisitorConf `ini:",,,,extends",json:"inline"` +func (q *BandwidthQuantity) String() string { + return q.s } -type STCPVisitorConf struct { - BaseVisitorConf `ini:",,,,extends",json:"inline"` +func (q *BandwidthQuantity) UnmarshalString(s string) error { + s = strings.TrimSpace(s) + if s == "" { + return nil + } + + var ( + base int64 + f float64 + err error + ) + if strings.HasSuffix(s, "MB") { + base = MB + fstr := strings.TrimSuffix(s, "MB") + f, err = strconv.ParseFloat(fstr, 64) + if err != nil { + return err + } + } else if strings.HasSuffix(s, "KB") { + base = KB + fstr := strings.TrimSuffix(s, "KB") + f, err = strconv.ParseFloat(fstr, 64) + if err != nil { + return err + } + } else { + return errors.New("unit not support") + } + + q.s = s + q.i = int64(f * float64(base)) + return nil } -type XTCPVisitorConf struct { - BaseVisitorConf `ini:",,,,extends",json:"inline"` +func (q *BandwidthQuantity) UnmarshalJSON(b []byte) error { + if len(b) == 4 && string(b) == "null" { + return nil + } + + var str string + err := json.Unmarshal(b, &str) + if err != nil { + return err + } + + return q.UnmarshalString(str) +} + +func (q *BandwidthQuantity) MarshalJSON() ([]byte, error) { + return []byte("\"" + q.s + "\""), nil +} + +func (q *BandwidthQuantity) Bytes() int64 { + return q.i } diff --git a/pkg/config/bandwidth_test.go b/pkg/config/types_test.go similarity index 100% rename from pkg/config/bandwidth_test.go rename to pkg/config/types_test.go diff --git a/pkg/config/visitor.go b/pkg/config/visitor.go index 051a1618..f3552b32 100644 --- a/pkg/config/visitor.go +++ b/pkg/config/visitor.go @@ -18,13 +18,57 @@ import ( "fmt" "reflect" + "github.com/fatedier/frp/pkg/consts" + "gopkg.in/ini.v1" ) + +// Visitor +var ( + visitorConfTypeMap = map[string]reflect.Type{ + consts.STCPProxy: reflect.TypeOf(STCPVisitorConf{}), + consts.XTCPProxy: reflect.TypeOf(XTCPVisitorConf{}), + consts.SUDPProxy: reflect.TypeOf(SUDPVisitorConf{}), + } +) + +type VisitorConf interface { + GetBaseInfo() *BaseVisitorConf + Compare(cmp VisitorConf) bool + UnmarshalFromIni(prefix string, name string, section *ini.Section) error + Check() error +} + +type BaseVisitorConf struct { + ProxyName string `ini:"name" json:"name"` + ProxyType string `ini:"type" json:"type"` + UseEncryption bool `ini:"use_encryption" json:"use_encryption"` + UseCompression bool `ini:"use_compression" json:"use_compression"` + Role string `ini:"role" json:"role"` + Sk string `ini:"sk" json:"sk"` + ServerName string `ini:"server_name" json:"server_name"` + BindAddr string `ini:"bind_addr" json:"bind_addr"` + BindPort int `ini:"bind_port" json:"bind_port"` +} + +type SUDPVisitorConf struct { + BaseVisitorConf `ini:",extends" json:"inline"` +} + +type STCPVisitorConf struct { + BaseVisitorConf `ini:",extends" json:"inline"` +} + +type XTCPVisitorConf struct { + BaseVisitorConf `ini:",extends" json:"inline"` +} + + // DefaultVisitorConf creates a empty VisitorConf object by visitorType. // If visitorType doesn't exist, return nil. func DefaultVisitorConf(visitorType string) VisitorConf { - v, ok := VisitorConfTypeMap[visitorType] + v, ok := visitorConfTypeMap[visitorType] if !ok { return nil } @@ -93,8 +137,9 @@ func (cfg *BaseVisitorConf) check() (err error) { return } -func (cfg *BaseVisitorConf) decorate(prefix string, name string, section *ini.Section) error { +func (cfg *BaseVisitorConf) unmarshalFromIni(prefix string, name string, section *ini.Section) error { + // Custom decoration after basic unmarshal: // proxy name cfg.ProxyName = prefix + name @@ -109,6 +154,20 @@ func (cfg *BaseVisitorConf) decorate(prefix string, name string, section *ini.Se return nil } +func preVisitorUnmarshalFromIni(cfg VisitorConf, prefix string, name string, section *ini.Section) error { + err := section.MapTo(cfg) + if err != nil { + return err + } + + err = cfg.GetBaseInfo().unmarshalFromIni(prefix, name, section) + if err != nil { + return err + } + + return nil +} + // SUDP var _ VisitorConf = &SUDPVisitorConf{} @@ -127,20 +186,15 @@ func (cfg *SUDPVisitorConf) Compare(cmp VisitorConf) bool { return true } -func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) +func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) { + err = preVisitorUnmarshalFromIni(cfg, prefix, name, section) if err != nil { - return err - } - - err = cfg.BaseVisitorConf.decorate(prefix, name, section) - if err != nil { - return err + return } // Add custom logic unmarshal, if exists - return nil + return } func (cfg *SUDPVisitorConf) Check() (err error) { @@ -171,20 +225,15 @@ func (cfg *STCPVisitorConf) Compare(cmp VisitorConf) bool { return true } -func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) +func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) { + err = preVisitorUnmarshalFromIni(cfg, prefix, name, section) if err != nil { - return err - } - - err = cfg.BaseVisitorConf.decorate(prefix, name, section) - if err != nil { - return err + return } // Add custom logic unmarshal, if exists - return nil + return } func (cfg *STCPVisitorConf) Check() (err error) { @@ -215,20 +264,15 @@ func (cfg *XTCPVisitorConf) Compare(cmp VisitorConf) bool { return true } -func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { - err := section.MapTo(cfg) +func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) { + err = preVisitorUnmarshalFromIni(cfg, prefix, name, section) if err != nil { - return err - } - - err = cfg.BaseVisitorConf.decorate(prefix, name, section) - if err != nil { - return err + return } // Add custom logic unmarshal, if exists - return nil + return } func (cfg *XTCPVisitorConf) Check() (err error) { diff --git a/pkg/config/visitor_test.go b/pkg/config/visitor_test.go index 193cb163..98b2f097 100644 --- a/pkg/config/visitor_test.go +++ b/pkg/config/visitor_test.go @@ -17,15 +17,20 @@ package config import ( "testing" - "gopkg.in/ini.v1" - "github.com/fatedier/frp/pkg/consts" + "gopkg.in/ini.v1" "github.com/stretchr/testify/assert" ) const testVisitorPrefix = "test." +func Test_Visitor_Interface(t *testing.T) { + for name := range visitorConfTypeMap { + DefaultVisitorConf(name) + } +} + func Test_Visitor_UnmarshalFromIni(t *testing.T) { assert := assert.New(t) diff --git a/pkg/util/util/http.go b/pkg/util/util/http.go index 2d6089b1..e48ef4ab 100644 --- a/pkg/util/util/http.go +++ b/pkg/util/util/http.go @@ -74,4 +74,4 @@ func hasPort(host string) bool { return true } return host[0] == '[' && strings.Contains(host, "]:") -} +} \ No newline at end of file