This commit is contained in:
yuyulei 2020-12-25 14:43:25 +08:00
parent c7438fd2fd
commit 369c545647
18 changed files with 936 additions and 796 deletions

View File

@ -54,7 +54,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
return return
} }
newCommonCfg, err := config.LoadClientCommonConf(content) newCommonCfg, err := config.UnmarshalClientConfFromIni(content)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@ -62,7 +62,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
return return
} }
pxyCfgs, visitorCfgs, err := config.LoadClientBasicConf(svr.cfg.User, content, newCommonCfg.Start) pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(svr.cfg.User, content, newCommonCfg.Start)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()

View File

@ -131,7 +131,7 @@ func handleSignal(svr *client.Service) {
func parseClientCommonCfg(fileType int, source interface{}) (cfg config.ClientCommonConf, err error) { func parseClientCommonCfg(fileType int, source interface{}) (cfg config.ClientCommonConf, err error) {
if fileType == CfgFileTypeIni { if fileType == CfgFileTypeIni {
cfg, err = config.LoadClientCommonConf(source) cfg, err = config.UnmarshalClientConfFromIni(source)
} else if fileType == CfgFileTypeCmd { } else if fileType == CfgFileTypeCmd {
cfg, err = parseClientCommonCfgFromCmd() cfg, err = parseClientCommonCfgFromCmd()
} }
@ -147,7 +147,7 @@ func parseClientCommonCfg(fileType int, source interface{}) (cfg config.ClientCo
} }
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) { func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
cfg = config.DefaultClientConf() cfg = config.GetDefaultClientConf()
strs := strings.Split(serverAddr, ":") strs := strings.Split(serverAddr, ":")
if len(strs) < 2 { if len(strs) < 2 {
@ -183,24 +183,25 @@ func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
return return
} }
func runClient(cfgFilePath string) error { func runClient(cfgFilePath string) (err error) {
content, err := config.GetRenderedConfFromFile(cfgFilePath) var content string
content, err = config.GetRenderedConfFromFile(cfgFilePath)
if err != nil { if err != nil {
return err return
} }
cfg, err := parseClientCommonCfg(CfgFileTypeIni, content) cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
if err != nil { if err != nil {
return err return
} }
pxyCfgs, visitorCfgs, err := config.LoadClientBasicConf(cfg.User, content, cfg.Start) pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(cfg.User, content, cfg.Start)
if err != nil { if err != nil {
return err return
} }
err = startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath) err = startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
return err return
} }
func startService( func startService(

View File

@ -104,10 +104,9 @@ var rootCmd = &cobra.Command{
var cfg config.ServerCommonConf var cfg config.ServerCommonConf
var err error var err error
if cfgFile != "" { if cfgFile != "" {
log.Info("frps uses config file: %s", cfgFile) log.Info("frps uses config file: %s", cfgFile)
var content []byte var content string
content, err = config.GetRenderedConfFromFile(cfgFile) content, err = config.GetRenderedConfFromFile(cfgFile)
if err != nil { if err != nil {
return err return err
@ -138,7 +137,7 @@ func Execute() {
func parseServerCommonCfg(fileType int, source interface{}) (cfg config.ServerCommonConf, err error) { func parseServerCommonCfg(fileType int, source interface{}) (cfg config.ServerCommonConf, err error) {
if fileType == CfgFileTypeIni { if fileType == CfgFileTypeIni {
cfg, err = config.LoadServerCommonConf(source) cfg, err = config.UnmarshalServerConfFromIni(source)
} else if fileType == CfgFileTypeCmd { } else if fileType == CfgFileTypeCmd {
cfg, err = parseServerCommonCfgFromCmd() cfg, err = parseServerCommonCfgFromCmd()
} }
@ -154,7 +153,7 @@ func parseServerCommonCfg(fileType int, source interface{}) (cfg config.ServerCo
} }
func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) { func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
cfg = config.DefaultServerConf() cfg = config.GetDefaultServerConf()
cfg.BindAddr = bindAddr cfg.BindAddr = bindAddr
cfg.BindPort = bindPort cfg.BindPort = bindPort

View File

@ -177,7 +177,7 @@ use_compression = true
http_user = admin http_user = admin
http_pwd = admin http_pwd = admin
# if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com # if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com
sub_domain = web01 subdomain = web01
custom_domains = web02.yourdomain.com custom_domains = web02.yourdomain.com
# locations is only available for http type # locations is only available for http type
locations = /,/pic locations = /,/pic

121
pkg/config/bandwidth.go Normal file
View File

@ -0,0 +1,121 @@
// 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
}

View File

@ -0,0 +1,40 @@
// 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"
"testing"
"github.com/stretchr/testify/assert"
)
type Wrap struct {
B BandwidthQuantity `json:"b"`
Int int `json:"int"`
}
func TestBandwidthQuantity(t *testing.T) {
assert := assert.New(t)
var w Wrap
err := json.Unmarshal([]byte(`{"b":"1KB","int":5}`), &w)
assert.NoError(err)
assert.EqualValues(1*KB, w.B.Bytes())
buf, err := json.Marshal(&w)
assert.NoError(err)
assert.Equal(`{"b":"1KB","int":5}`, string(buf))
}

View File

@ -1,4 +1,4 @@
// Copyright 2016 fatedier, fatedier@gmail.com // Copyright 2020 The frp Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -26,7 +26,7 @@ import (
) )
// GetDefaultClientConf returns a client configuration with default values. // GetDefaultClientConf returns a client configuration with default values.
func DefaultClientConf() ClientCommonConf { func GetDefaultClientConf() ClientCommonConf {
return ClientCommonConf{ return ClientCommonConf{
ClientConfig: auth.GetDefaultClientConf(), ClientConfig: auth.GetDefaultClientConf(),
ServerAddr: "0.0.0.0", ServerAddr: "0.0.0.0",
@ -87,7 +87,7 @@ func (cfg *ClientCommonConf) Check() error {
} }
// Supported sources including: string(file path), []byte, Reader interface. // Supported sources including: string(file path), []byte, Reader interface.
func LoadClientCommonConf(source interface{}) (ClientCommonConf, error) { func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: false, Insensitive: false,
InsensitiveSections: false, InsensitiveSections: false,
@ -105,7 +105,7 @@ func LoadClientCommonConf(source interface{}) (ClientCommonConf, error) {
return ClientCommonConf{}, err return ClientCommonConf{}, err
} }
common := DefaultClientConf() common := GetDefaultClientConf()
err = s.MapTo(&common) err = s.MapTo(&common)
if err != nil { if err != nil {
return ClientCommonConf{}, err return ClientCommonConf{}, err
@ -118,7 +118,7 @@ func LoadClientCommonConf(source interface{}) (ClientCommonConf, error) {
// if len(startProxy) is 0, start all // if len(startProxy) is 0, start all
// otherwise just start proxies in startProxy map // otherwise just start proxies in startProxy map
func LoadClientBasicConf( func LoadAllConfFromIni(
prefix string, prefix string,
source interface{}, source interface{},
start []string, start []string,

View File

@ -1,3 +1,17 @@
// Copyright 2020 The frp Authors
//
// 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 package config
import ( import (
@ -277,7 +291,7 @@ func Test_LoadClientCommonConf(t *testing.T) {
UDPPacketSize: 1509, UDPPacketSize: 1509,
} }
common, err := LoadClientCommonConf(testClientBytesWithFull) common, err := UnmarshalClientConfFromIni(testClientBytesWithFull)
assert.NoError(err) assert.NoError(err)
assert.Equal(expected, common) assert.Equal(expected, common)
} }
@ -446,7 +460,7 @@ func Test_LoadClientBasicConf(t *testing.T) {
}, },
}, },
HTTPProxySpec: HTTPProxySpec{ HTTPProxySpec: HTTPProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"web02.yourdomain.com"}, CustomDomains: []string{"web02.yourdomain.com"},
SubDomain: "web01", SubDomain: "web01",
}, },
@ -472,7 +486,7 @@ func Test_LoadClientBasicConf(t *testing.T) {
ProxyProtocolVersion: "v2", ProxyProtocolVersion: "v2",
}, },
HTTPSProxySpec: HTTPSProxySpec{ HTTPSProxySpec: HTTPSProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"web02.yourdomain.com"}, CustomDomains: []string{"web02.yourdomain.com"},
SubDomain: "web01", SubDomain: "web01",
}, },
@ -516,7 +530,7 @@ func Test_LoadClientBasicConf(t *testing.T) {
}, },
}, },
TCPMuxProxySpec: TCPMuxProxySpec{ TCPMuxProxySpec: TCPMuxProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"tunnel1"}, CustomDomains: []string{"tunnel1"},
SubDomain: "", SubDomain: "",
}, },
@ -609,7 +623,7 @@ func Test_LoadClientBasicConf(t *testing.T) {
}, },
}, },
HTTPSProxySpec: HTTPSProxySpec{ HTTPSProxySpec: HTTPSProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"test.yourdomain.com"}, CustomDomains: []string{"test.yourdomain.com"},
}, },
}, },
@ -629,7 +643,7 @@ func Test_LoadClientBasicConf(t *testing.T) {
}, },
}, },
HTTPProxySpec: HTTPProxySpec{ HTTPProxySpec: HTTPProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"test.yourdomain.com"}, CustomDomains: []string{"test.yourdomain.com"},
}, },
}, },
@ -661,7 +675,7 @@ func Test_LoadClientBasicConf(t *testing.T) {
}, },
} }
proxyActual, visitorActual, err := LoadClientBasicConf(testUser, testClientBytesWithFull, nil) proxyActual, visitorActual, err := LoadAllConfFromIni(testUser, testClientBytesWithFull, nil)
assert.NoError(err) assert.NoError(err)
assert.Equal(proxyExpected, proxyActual) assert.Equal(proxyExpected, proxyActual)
assert.Equal(visitorExpected, visitorActual) assert.Equal(visitorExpected, visitorActual)

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,17 @@
// Copyright 2020 The frp Authors
//
// 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 package config
import ( import (
@ -171,7 +185,7 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) {
}, },
}, },
HTTPProxySpec: HTTPProxySpec{ HTTPProxySpec: HTTPProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"web02.yourdomain.com"}, CustomDomains: []string{"web02.yourdomain.com"},
SubDomain: "web01", SubDomain: "web01",
}, },
@ -211,7 +225,7 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) {
ProxyProtocolVersion: "v2", ProxyProtocolVersion: "v2",
}, },
HTTPSProxySpec: HTTPSProxySpec{ HTTPSProxySpec: HTTPSProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"web02.yourdomain.com"}, CustomDomains: []string{"web02.yourdomain.com"},
SubDomain: "web01", SubDomain: "web01",
}, },
@ -290,7 +304,7 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) {
}, },
}, },
TCPMuxProxySpec: TCPMuxProxySpec{ TCPMuxProxySpec: TCPMuxProxySpec{
DomainSpec: DomainSpec{ DomainConf: DomainConf{
CustomDomains: []string{"tunnel1"}, CustomDomains: []string{"tunnel1"},
SubDomain: "", SubDomain: "",
}, },

View File

@ -1,4 +1,4 @@
// Copyright 2016 fatedier, fatedier@gmail.com // Copyright 2020 The frp Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -27,7 +27,7 @@ import (
// GetDefaultServerConf returns a server configuration with reasonable // GetDefaultServerConf returns a server configuration with reasonable
// defaults. // defaults.
func DefaultServerConf() ServerCommonConf { func GetDefaultServerConf() ServerCommonConf {
return ServerCommonConf{ return ServerCommonConf{
ServerConfig: auth.GetDefaultServerConf(), ServerConfig: auth.GetDefaultServerConf(),
BindAddr: "0.0.0.0", BindAddr: "0.0.0.0",
@ -72,7 +72,7 @@ func (cfg *ServerCommonConf) Check() error {
return nil return nil
} }
func LoadServerCommonConf(source interface{}) (ServerCommonConf, error) { func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: false, Insensitive: false,
@ -91,7 +91,7 @@ func LoadServerCommonConf(source interface{}) (ServerCommonConf, error) {
return ServerCommonConf{}, err return ServerCommonConf{}, err
} }
common := DefaultServerConf() common := GetDefaultServerConf()
err = s.MapTo(&common) err = s.MapTo(&common)
if err != nil { if err != nil {
return ServerCommonConf{}, err return ServerCommonConf{}, err

View File

@ -1,3 +1,17 @@
// Copyright 2020 The frp Authors
//
// 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 package config
import ( import (
@ -184,7 +198,7 @@ func Test_LoadServerCommonConf(t *testing.T) {
} }
for _, c := range testcases { for _, c := range testcases {
actual, err := LoadServerCommonConf(c.source) actual, err := UnmarshalServerConfFromIni(c.source)
assert.NoError(err) assert.NoError(err)
assert.Equal(c.expected, actual) assert.Equal(c.expected, actual)
} }

View File

@ -1,4 +1,4 @@
// Copyright 2019 fatedier, fatedier@gmail.com // Copyright 2020 The frp Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -25,7 +25,9 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
) )
// Client // 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 { type ClientCommonConf struct {
auth.ClientConfig `ini:",,,,extends",json:"inline"` auth.ClientConfig `ini:",,,,extends",json:"inline"`
// ServerAddr specifies the address of the server to connect to. By // ServerAddr specifies the address of the server to connect to. By
@ -96,7 +98,7 @@ type ClientCommonConf struct {
// Start specifies a set of enabled proxies by name. If this set is empty, // 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 // all supplied proxies are enabled. By default, this value is an empty
// set. // set.
Start []string `ini:"start",json:"start""` Start []string `ini:"start",json:"start"`
//Start map[string]struct{} `json:"start"` //Start map[string]struct{} `json:"start"`
// Protocol specifies the protocol to use when interacting with the server. // Protocol specifies the protocol to use when interacting with the server.
// Valid values are "tcp", "kcp" and "websocket". By default, this value // Valid values are "tcp", "kcp" and "websocket". By default, this value
@ -132,8 +134,6 @@ type ClientCommonConf struct {
UDPPacketSize int64 `ini:"udp_packet_size",json:"udp_packet_size"` UDPPacketSize int64 `ini:"udp_packet_size",json:"udp_packet_size"`
} }
// Server
// ServerCommonConf contains information for a server service. It is // ServerCommonConf contains information for a server service. It is
// recommended to use GetDefaultServerConf instead of creating this object // recommended to use GetDefaultServerConf instead of creating this object
// directly, so that all unspecified fields have reasonable default values. // directly, so that all unspecified fields have reasonable default values.
@ -387,7 +387,7 @@ type BaseProxyConf struct {
HealthCheckConf `ini:",,,,extends",json:"inline"` HealthCheckConf `ini:",,,,extends",json:"inline"`
} }
type DomainSpec struct { type DomainConf struct {
CustomDomains []string `ini:"custom_domains",json:"custom_domains"` CustomDomains []string `ini:"custom_domains",json:"custom_domains"`
SubDomain string `ini:"subdomain",json:"subdomain"` SubDomain string `ini:"subdomain",json:"subdomain"`
} }
@ -399,7 +399,7 @@ type HTTPProxyConf struct {
} }
type HTTPProxySpec struct { type HTTPProxySpec struct {
DomainSpec `ini:",,,,extends",json:"inline"` DomainConf `ini:",,,,extends",json:"inline"`
Locations []string `ini:"locations",json:"locations"` Locations []string `ini:"locations",json:"locations"`
HTTPUser string `ini:"http_user",json:"http_user"` HTTPUser string `ini:"http_user",json:"http_user"`
HTTPPwd string `ini:"http_pwd",json:"http_pwd"` HTTPPwd string `ini:"http_pwd",json:"http_pwd"`
@ -414,7 +414,7 @@ type HTTPSProxyConf struct {
} }
type HTTPSProxySpec struct { type HTTPSProxySpec struct {
DomainSpec `ini:",,,,extends",json:"inline"` DomainConf `ini:",,,,extends",json:"inline"`
} }
// TCP // TCP
@ -434,7 +434,7 @@ type TCPMuxProxyConf struct {
} }
type TCPMuxProxySpec struct { type TCPMuxProxySpec struct {
DomainSpec `ini:",,,,extends",json:"inline"` DomainConf `ini:",,,,extends",json:"inline"`
Multiplexer string `ini:"multiplexer"` Multiplexer string `ini:"multiplexer"`
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 fatedier, fatedier@gmail.com // Copyright 2020 The frp Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -15,14 +15,7 @@
package config package config
import ( import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"os"
"strconv"
"strings" "strings"
"text/template"
) )
func GetMapWithoutPrefix(set map[string]string, prefix string) map[string]string { func GetMapWithoutPrefix(set map[string]string, prefix string) map[string]string {
@ -56,152 +49,3 @@ func GetMapByPrefix(set map[string]string, prefix string) map[string]string {
return m return m
} }
// Render Env Values
var glbEnvs map[string]string
func init() {
glbEnvs = make(map[string]string)
envs := os.Environ()
for _, env := range envs {
kv := strings.Split(env, "=")
if len(kv) != 2 {
continue
}
glbEnvs[kv[0]] = kv[1]
}
}
type Values struct {
Envs map[string]string // environment vars
}
func GetValues() *Values {
return &Values{
Envs: glbEnvs,
}
}
func RenderContent(in []byte) ([]byte, error) {
tmpl, err := template.New("frp").Parse(string(in))
if err != nil {
return nil, err
}
buffer := bytes.NewBufferString("")
v := GetValues()
err = tmpl.Execute(buffer, v)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func GetRenderedConfFromFile(path string) ([]byte, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return RenderContent(data)
}
// BandwidthQuantity
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
}

78
pkg/config/value.go Normal file
View File

@ -0,0 +1,78 @@
// Copyright 2020 The frp Authors
//
// 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 (
"bytes"
"io/ioutil"
"os"
"strings"
"text/template"
)
var (
glbEnvs map[string]string
)
func init() {
glbEnvs = make(map[string]string)
envs := os.Environ()
for _, env := range envs {
kv := strings.Split(env, "=")
if len(kv) != 2 {
continue
}
glbEnvs[kv[0]] = kv[1]
}
}
type Values struct {
Envs map[string]string // environment vars
}
func GetValues() *Values {
return &Values{
Envs: glbEnvs,
}
}
func RenderContent(in string) (out string, err error) {
tmpl, errRet := template.New("frp").Parse(in)
if errRet != nil {
err = errRet
return
}
buffer := bytes.NewBufferString("")
v := GetValues()
err = tmpl.Execute(buffer, v)
if err != nil {
return
}
out = buffer.String()
return
}
func GetRenderedConfFromFile(path string) (out string, err error) {
var b []byte
b, err = ioutil.ReadFile(path)
if err != nil {
return
}
content := string(b)
out, err = RenderContent(content)
return
}

View File

@ -21,7 +21,6 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
) )
// Visitor Conf Loader
// DefaultVisitorConf creates a empty VisitorConf object by visitorType. // DefaultVisitorConf creates a empty VisitorConf object by visitorType.
// If visitorType doesn't exist, return nil. // If visitorType doesn't exist, return nil.
func DefaultVisitorConf(visitorType string) VisitorConf { func DefaultVisitorConf(visitorType string) VisitorConf {
@ -59,111 +58,67 @@ func NewVisitorConfFromIni(prefix string, name string, section *ini.Section) (Vi
} }
// Base // Base
func (c *BaseVisitorConf) GetBaseInfo() *BaseVisitorConf { func (cfg *BaseVisitorConf) GetBaseInfo() *BaseVisitorConf {
return c return cfg
} }
func (c *BaseVisitorConf) compare(cmp *BaseVisitorConf) bool { func (cfg *BaseVisitorConf) compare(cmp *BaseVisitorConf) bool {
if c.ProxyName != cmp.ProxyName || if cfg.ProxyName != cmp.ProxyName ||
c.ProxyType != cmp.ProxyType || cfg.ProxyType != cmp.ProxyType ||
c.UseEncryption != cmp.UseEncryption || cfg.UseEncryption != cmp.UseEncryption ||
c.UseCompression != cmp.UseCompression || cfg.UseCompression != cmp.UseCompression ||
c.Role != cmp.Role || cfg.Role != cmp.Role ||
c.Sk != cmp.Sk || cfg.Sk != cmp.Sk ||
c.ServerName != cmp.ServerName || cfg.ServerName != cmp.ServerName ||
c.BindAddr != cmp.BindAddr || cfg.BindAddr != cmp.BindAddr ||
c.BindPort != cmp.BindPort { cfg.BindPort != cmp.BindPort {
return false return false
} }
return true return true
} }
func (c *BaseVisitorConf) check() (err error) { func (cfg *BaseVisitorConf) check() (err error) {
if c.Role != "visitor" { if cfg.Role != "visitor" {
err = fmt.Errorf("invalid role") err = fmt.Errorf("invalid role")
return return
} }
if c.BindAddr == "" { if cfg.BindAddr == "" {
err = fmt.Errorf("bind_addr shouldn't be empty") err = fmt.Errorf("bind_addr shouldn't be empty")
return return
} }
if c.BindPort <= 0 { if cfg.BindPort <= 0 {
err = fmt.Errorf("bind_port is required") err = fmt.Errorf("bind_port is required")
return return
} }
return return
} }
func (c *BaseVisitorConf) decorate(prefix string, name string, section *ini.Section) error { func (cfg *BaseVisitorConf) decorate(prefix string, name string, section *ini.Section) error {
// proxy name // proxy name
c.ProxyName = prefix + name cfg.ProxyName = prefix + name
// server_name // server_name
c.ServerName = prefix + c.ServerName cfg.ServerName = prefix + cfg.ServerName
// bind_addr // bind_addr
if c.BindAddr == "" { if cfg.BindAddr == "" {
c.BindAddr = "127.0.0.1" cfg.BindAddr = "127.0.0.1"
} }
return nil return nil
} }
// STCP
var _ VisitorConf = &STCPVisitorConf{}
func (c *STCPVisitorConf) Compare(conf VisitorConf) bool {
cmp, ok := conf.(*STCPVisitorConf)
if !ok {
return false
}
if !c.BaseVisitorConf.compare(&cmp.BaseVisitorConf) {
return false
}
// Add custom login equal, if exists
return true
}
func (c *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
err := section.MapTo(c)
if err != nil {
return err
}
err = c.BaseVisitorConf.decorate(prefix, name, section)
if err != nil {
return err
}
// Add custom logic unmarshal, if exists
return nil
}
func (cfg *STCPVisitorConf) Check() error {
if err := cfg.BaseVisitorConf.check(); err != nil {
return err
}
// Add custom logic validate, if exists
return nil
}
// SUDP // SUDP
var _ VisitorConf = &SUDPVisitorConf{} var _ VisitorConf = &SUDPVisitorConf{}
func (c *SUDPVisitorConf) Compare(conf VisitorConf) bool { func (cfg *SUDPVisitorConf) Compare(cmp VisitorConf) bool {
cmp, ok := conf.(*SUDPVisitorConf) cmpConf, ok := cmp.(*SUDPVisitorConf)
if !ok { if !ok {
return false return false
} }
if !c.BaseVisitorConf.compare(&cmp.BaseVisitorConf) { if !cfg.BaseVisitorConf.compare(&cmpConf.BaseVisitorConf) {
return false return false
} }
@ -172,13 +127,13 @@ func (c *SUDPVisitorConf) Compare(conf VisitorConf) bool {
return true return true
} }
func (c *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
err := section.MapTo(c) err := section.MapTo(cfg)
if err != nil { if err != nil {
return err return err
} }
err = c.BaseVisitorConf.decorate(prefix, name, section) err = cfg.BaseVisitorConf.decorate(prefix, name, section)
if err != nil { if err != nil {
return err return err
} }
@ -188,26 +143,70 @@ func (c *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *
return nil return nil
} }
func (cfg *SUDPVisitorConf) Check() error { func (cfg *SUDPVisitorConf) Check() (err error) {
if err := cfg.BaseVisitorConf.check(); err != nil { if err = cfg.BaseVisitorConf.check(); err != nil {
return err return
} }
// Add custom logic validate, if exists // Add custom logic validate, if exists
return
}
// STCP
var _ VisitorConf = &STCPVisitorConf{}
func (cfg *STCPVisitorConf) Compare(cmp VisitorConf) bool {
cmpConf, ok := cmp.(*STCPVisitorConf)
if !ok {
return false
}
if !cfg.BaseVisitorConf.compare(&cmpConf.BaseVisitorConf) {
return false
}
// Add custom login equal, if exists
return true
}
func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
err := section.MapTo(cfg)
if err != nil {
return err
}
err = cfg.BaseVisitorConf.decorate(prefix, name, section)
if err != nil {
return err
}
// Add custom logic unmarshal, if exists
return nil return nil
} }
func (cfg *STCPVisitorConf) Check() (err error) {
if err = cfg.BaseVisitorConf.check(); err != nil {
return
}
// Add custom logic validate, if exists
return
}
// XTCP // XTCP
var _ VisitorConf = &XTCPVisitorConf{} var _ VisitorConf = &XTCPVisitorConf{}
func (c *XTCPVisitorConf) Compare(conf VisitorConf) bool { func (cfg *XTCPVisitorConf) Compare(cmp VisitorConf) bool {
cmp, ok := conf.(*XTCPVisitorConf) cmpConf, ok := cmp.(*XTCPVisitorConf)
if !ok { if !ok {
return false return false
} }
if !c.BaseVisitorConf.compare(&cmp.BaseVisitorConf) { if !cfg.BaseVisitorConf.compare(&cmpConf.BaseVisitorConf) {
return false return false
} }
@ -216,13 +215,13 @@ func (c *XTCPVisitorConf) Compare(conf VisitorConf) bool {
return true return true
} }
func (c *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error { func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
err := section.MapTo(c) err := section.MapTo(cfg)
if err != nil { if err != nil {
return err return err
} }
err = c.BaseVisitorConf.decorate(prefix, name, section) err = cfg.BaseVisitorConf.decorate(prefix, name, section)
if err != nil { if err != nil {
return err return err
} }
@ -232,12 +231,12 @@ func (c *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *
return nil return nil
} }
func (cfg *XTCPVisitorConf) Check() error { func (cfg *XTCPVisitorConf) Check() (err error) {
if err := cfg.BaseVisitorConf.check(); err != nil { if err = cfg.BaseVisitorConf.check(); err != nil {
return err return
} }
// Add custom logic validate, if exists // Add custom logic validate, if exists
return nil return
} }

View File

@ -1,3 +1,17 @@
// Copyright 2020 The frp Authors
//
// 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 package config
import ( import (

View File

@ -98,7 +98,7 @@ type TCPOutConf struct {
type TCPMuxOutConf struct { type TCPMuxOutConf struct {
BaseOutConf BaseOutConf
config.DomainSpec config.DomainConf
Multiplexer string `json:"multiplexer"` Multiplexer string `json:"multiplexer"`
} }
@ -109,14 +109,14 @@ type UDPOutConf struct {
type HTTPOutConf struct { type HTTPOutConf struct {
BaseOutConf BaseOutConf
config.DomainSpec config.DomainConf
Locations []string `json:"locations"` Locations []string `json:"locations"`
HostHeaderRewrite string `json:"host_header_rewrite"` HostHeaderRewrite string `json:"host_header_rewrite"`
} }
type HTTPSOutConf struct { type HTTPSOutConf struct {
BaseOutConf BaseOutConf
config.DomainSpec config.DomainConf
} }
type STCPOutConf struct { type STCPOutConf struct {