From c33b5152e722a50d65cd6925ba498f3016c7c19c Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 25 Jun 2018 18:22:35 +0800 Subject: [PATCH 01/20] split visitors from proxies and add health check config --- client/admin_api.go | 2 +- client/control.go | 17 ++- client/health.go | 32 +++++ client/proxy_manager.go | 87 +++--------- client/service.go | 2 +- client/visitor.go | 14 +- client/visitor_manager.go | 111 +++++++++++++++ cmd/frpc/sub/root.go | 4 +- cmd/frpc/sub/stcp.go | 75 +++++----- cmd/frpc/sub/xtcp.go | 75 +++++----- conf/frpc_full.ini | 9 ++ models/config/proxy.go | 287 ++++++++++++++++++++------------------ models/config/visitor.go | 213 ++++++++++++++++++++++++++++ 13 files changed, 641 insertions(+), 287 deletions(-) create mode 100644 client/health.go create mode 100644 client/visitor_manager.go create mode 100644 models/config/visitor.go diff --git a/client/admin_api.go b/client/admin_api.go index 854e9708..4eafa103 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -77,7 +77,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { return } - pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start) + pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start) if err != nil { res.Code = 3 res.Msg = err.Error() diff --git a/client/control.go b/client/control.go index 53669ae5..04be13ce 100644 --- a/client/control.go +++ b/client/control.go @@ -47,8 +47,12 @@ type Control struct { // login message to server, only used loginMsg *msg.Login + // manage all proxies pm *ProxyManager + // manage all visitors + vm *VisitorManager + // control connection conn frpNet.Conn @@ -82,7 +86,7 @@ type Control struct { log.Logger } -func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) *Control { +func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) *Control { loginMsg := &msg.Login{ Arch: runtime.GOARCH, Os: runtime.GOOS, @@ -102,7 +106,9 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m Logger: log.NewPrefixLogger(""), } ctl.pm = NewProxyManager(ctl, ctl.sendCh, "") - ctl.pm.Reload(pxyCfgs, visitorCfgs, false) + ctl.pm.Reload(pxyCfgs, false) + ctl.vm = NewVisitorManager(ctl) + ctl.vm.Reload(visitorCfgs) return ctl } @@ -129,6 +135,8 @@ func (ctl *Control) Run() (err error) { // start all local visitors and send NewProxy message for all configured proxies ctl.pm.Reset(ctl.sendCh, ctl.runId) ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew}) + + go ctl.vm.Run() return nil } @@ -444,7 +452,8 @@ func (ctl *Control) worker() { } } -func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) error { - err := ctl.pm.Reload(pxyCfgs, visitorCfgs, true) +func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error { + ctl.vm.Reload(visitorCfgs) + err := ctl.pm.Reload(pxyCfgs, true) return err } diff --git a/client/health.go b/client/health.go new file mode 100644 index 00000000..ad58554d --- /dev/null +++ b/client/health.go @@ -0,0 +1,32 @@ +// Copyright 2018 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 client + +import ( + "github.com/fatedier/frp/models/config" +) + +type HealthCheckMonitor struct { + cfg config.HealthCheckConf +} + +func NewHealthCheckMonitor(cfg *config.HealthCheckConf) *HealthCheckMonitor { + return &HealthCheckMonitor{ + cfg: *cfg, + } +} + +func (monitor *HealthCheckMonitor) Start() { +} diff --git a/client/proxy_manager.go b/client/proxy_manager.go index 67029724..cfa56fc5 100644 --- a/client/proxy_manager.go +++ b/client/proxy_manager.go @@ -13,25 +13,21 @@ import ( ) const ( - ProxyStatusNew = "new" - ProxyStatusStartErr = "start error" - ProxyStatusWaitStart = "wait start" - ProxyStatusRunning = "running" - ProxyStatusClosed = "closed" + ProxyStatusNew = "new" + ProxyStatusStartErr = "start error" + ProxyStatusWaitStart = "wait start" + ProxyStatusRunning = "running" + ProxyStatusCheckFailed = "check failed" + ProxyStatusCheckSuccess = "check success" + ProxyStatusClosed = "closed" ) type ProxyManager struct { - ctl *Control - + ctl *Control + sendCh chan (msg.Message) proxies map[string]*ProxyWrapper - - visitorCfgs map[string]config.ProxyConf - visitors map[string]Visitor - - sendCh chan (msg.Message) - - closed bool - mu sync.RWMutex + closed bool + mu sync.RWMutex log.Logger } @@ -151,13 +147,11 @@ func (pw *ProxyWrapper) Close() { func NewProxyManager(ctl *Control, msgSendCh chan (msg.Message), logPrefix string) *ProxyManager { return &ProxyManager{ - ctl: ctl, - proxies: make(map[string]*ProxyWrapper), - visitorCfgs: make(map[string]config.ProxyConf), - visitors: make(map[string]Visitor), - sendCh: msgSendCh, - closed: false, - Logger: log.NewPrefixLogger(logPrefix), + ctl: ctl, + proxies: make(map[string]*ProxyWrapper), + sendCh: msgSendCh, + closed: false, + Logger: log.NewPrefixLogger(logPrefix), } } @@ -239,24 +233,9 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) { } } } - - for _, cfg := range pm.visitorCfgs { - name := cfg.GetBaseInfo().ProxyName - if _, exist := pm.visitors[name]; !exist { - pm.Info("try to start visitor [%s]", name) - visitor := NewVisitor(pm.ctl, cfg) - err := visitor.Run() - if err != nil { - visitor.Warn("start error: %v", err) - continue - } - pm.visitors[name] = visitor - visitor.Info("start visitor success") - } - } } -func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf, startNow bool) error { +func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, startNow bool) error { pm.mu.Lock() defer func() { pm.mu.Unlock() @@ -308,38 +287,6 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, visitorCfgs } } pm.Info("proxy added: %v", addPxyNames) - - delVisitorName := make([]string, 0) - for name, oldVisitorCfg := range pm.visitorCfgs { - del := false - cfg, ok := visitorCfgs[name] - if !ok { - del = true - } else { - if !oldVisitorCfg.Compare(cfg) { - del = true - } - } - - if del { - delVisitorName = append(delVisitorName, name) - delete(pm.visitorCfgs, name) - if visitor, ok := pm.visitors[name]; ok { - visitor.Close() - } - delete(pm.visitors, name) - } - } - pm.Info("visitor removed: %v", delVisitorName) - - addVisitorName := make([]string, 0) - for name, visitorCfg := range visitorCfgs { - if _, ok := pm.visitorCfgs[name]; !ok { - pm.visitorCfgs[name] = visitorCfg - addVisitorName = append(addVisitorName, name) - } - } - pm.Info("visitor added: %v", addVisitorName) return nil } diff --git a/client/service.go b/client/service.go index 5fbf33c7..2589f520 100644 --- a/client/service.go +++ b/client/service.go @@ -27,7 +27,7 @@ type Service struct { closedCh chan int } -func NewService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) (svr *Service) { +func NewService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (svr *Service) { svr = &Service{ closedCh: make(chan int), } diff --git a/client/visitor.go b/client/visitor.go index 44e384d6..6e1e1c8d 100644 --- a/client/visitor.go +++ b/client/visitor.go @@ -44,18 +44,18 @@ type Visitor interface { log.Logger } -func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) { +func NewVisitor(ctl *Control, cfg config.VisitorConf) (visitor Visitor) { baseVisitor := BaseVisitor{ ctl: ctl, - Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName), + Logger: log.NewPrefixLogger(cfg.GetBaseInfo().ProxyName), } - switch cfg := pxyConf.(type) { - case *config.StcpProxyConf: + switch cfg := cfg.(type) { + case *config.StcpVisitorConf: visitor = &StcpVisitor{ BaseVisitor: baseVisitor, cfg: cfg, } - case *config.XtcpProxyConf: + case *config.XtcpVisitorConf: visitor = &XtcpVisitor{ BaseVisitor: baseVisitor, cfg: cfg, @@ -75,7 +75,7 @@ type BaseVisitor struct { type StcpVisitor struct { BaseVisitor - cfg *config.StcpProxyConf + cfg *config.StcpVisitorConf } func (sv *StcpVisitor) Run() (err error) { @@ -162,7 +162,7 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) { type XtcpVisitor struct { BaseVisitor - cfg *config.XtcpProxyConf + cfg *config.XtcpVisitorConf } func (sv *XtcpVisitor) Run() (err error) { diff --git a/client/visitor_manager.go b/client/visitor_manager.go new file mode 100644 index 00000000..3e0aa80b --- /dev/null +++ b/client/visitor_manager.go @@ -0,0 +1,111 @@ +// Copyright 2018 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 client + +import ( + "sync" + "time" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/utils/log" +) + +type VisitorManager struct { + ctl *Control + + cfgs map[string]config.VisitorConf + visitors map[string]Visitor + + checkInterval time.Duration + + mu sync.Mutex +} + +func NewVisitorManager(ctl *Control) *VisitorManager { + return &VisitorManager{ + ctl: ctl, + cfgs: make(map[string]config.VisitorConf), + visitors: make(map[string]Visitor), + checkInterval: 10 * time.Second, + } +} + +func (vm *VisitorManager) Run() { + for { + time.Sleep(vm.checkInterval) + vm.mu.Lock() + for _, cfg := range vm.cfgs { + name := cfg.GetBaseInfo().ProxyName + if _, exist := vm.visitors[name]; !exist { + log.Info("try to start visitor [%s]", name) + vm.startVisitor(cfg) + } + } + vm.mu.Unlock() + } +} + +// Hold lock before calling this function. +func (vm *VisitorManager) startVisitor(cfg config.VisitorConf) (err error) { + name := cfg.GetBaseInfo().ProxyName + visitor := NewVisitor(vm.ctl, cfg) + err = visitor.Run() + if err != nil { + visitor.Warn("start error: %v", err) + } else { + vm.visitors[name] = visitor + visitor.Info("start visitor success") + } + return +} + +func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) { + vm.mu.Lock() + defer vm.mu.Unlock() + + delNames := make([]string, 0) + for name, oldCfg := range vm.cfgs { + del := false + cfg, ok := cfgs[name] + if !ok { + del = true + } else { + if !oldCfg.Compare(cfg) { + del = true + } + } + + if del { + delNames = append(delNames, name) + delete(vm.cfgs, name) + if visitor, ok := vm.visitors[name]; ok { + visitor.Close() + } + delete(vm.visitors, name) + } + } + log.Info("visitor removed: %v", delNames) + + addNames := make([]string, 0) + for name, cfg := range cfgs { + if _, ok := vm.cfgs[name]; !ok { + vm.cfgs[name] = cfg + addNames = append(addNames, name) + vm.startVisitor(cfg) + } + } + log.Info("visitor added: %v", addNames) + return +} diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index 9b9a2262..23b3bf9c 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -180,7 +180,7 @@ func runClient(cfgFilePath string) (err error) { return err } - pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start) + pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start) if err != nil { return err } @@ -189,7 +189,7 @@ func runClient(cfgFilePath string) (err error) { return } -func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) (err error) { +func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (err error) { log.InitLog(g.GlbClientCfg.LogWay, g.GlbClientCfg.LogFile, g.GlbClientCfg.LogLevel, g.GlbClientCfg.LogMaxDays) if g.GlbClientCfg.DnsServer != "" { s := g.GlbClientCfg.DnsServer diff --git a/cmd/frpc/sub/stcp.go b/cmd/frpc/sub/stcp.go index 4915e520..0920927b 100644 --- a/cmd/frpc/sub/stcp.go +++ b/cmd/frpc/sub/stcp.go @@ -57,48 +57,57 @@ var stcpCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.StcpProxyConf{} + proxyConfs := make(map[string]config.ProxyConf) + visitorConfs := make(map[string]config.VisitorConf) + var prefix string if user != "" { prefix = user + "." } - cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.StcpProxy - cfg.Role = role - cfg.Sk = sk - cfg.ServerName = serverName - cfg.LocalIp = localIp - cfg.LocalPort = localPort - cfg.BindAddr = bindAddr - cfg.BindPort = bindPort - cfg.UseEncryption = useEncryption - cfg.UseCompression = useCompression - err = cfg.CheckForCli() + if role == "server" { + cfg := &config.StcpProxyConf{} + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.StcpProxy + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + cfg.Role = role + cfg.Sk = sk + cfg.LocalIp = localIp + cfg.LocalPort = localPort + err = cfg.CheckForCli() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + proxyConfs[cfg.ProxyName] = cfg + } else if role == "visitor" { + cfg := &config.StcpVisitorConf{} + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.StcpProxy + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + cfg.Role = role + cfg.Sk = sk + cfg.ServerName = serverName + cfg.BindAddr = bindAddr + cfg.BindPort = bindPort + err = cfg.Check() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + visitorConfs[cfg.ProxyName] = cfg + } else { + fmt.Println("invalid role") + os.Exit(1) + } + + err = startService(proxyConfs, visitorConfs) if err != nil { fmt.Println(err) os.Exit(1) } - - if cfg.Role == "server" { - proxyConfs := map[string]config.ProxyConf{ - cfg.ProxyName: cfg, - } - err = startService(proxyConfs, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } else { - visitorConfs := map[string]config.ProxyConf{ - cfg.ProxyName: cfg, - } - err = startService(nil, visitorConfs) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } return nil }, } diff --git a/cmd/frpc/sub/xtcp.go b/cmd/frpc/sub/xtcp.go index 8c18a859..b6ae541d 100644 --- a/cmd/frpc/sub/xtcp.go +++ b/cmd/frpc/sub/xtcp.go @@ -57,48 +57,57 @@ var xtcpCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.XtcpProxyConf{} + proxyConfs := make(map[string]config.ProxyConf) + visitorConfs := make(map[string]config.VisitorConf) + var prefix string if user != "" { prefix = user + "." } - cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.XtcpProxy - cfg.Role = role - cfg.Sk = sk - cfg.ServerName = serverName - cfg.LocalIp = localIp - cfg.LocalPort = localPort - cfg.BindAddr = bindAddr - cfg.BindPort = bindPort - cfg.UseEncryption = useEncryption - cfg.UseCompression = useCompression - err = cfg.CheckForCli() + if role == "server" { + cfg := &config.XtcpProxyConf{} + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.StcpProxy + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + cfg.Role = role + cfg.Sk = sk + cfg.LocalIp = localIp + cfg.LocalPort = localPort + err = cfg.CheckForCli() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + proxyConfs[cfg.ProxyName] = cfg + } else if role == "visitor" { + cfg := &config.XtcpVisitorConf{} + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.StcpProxy + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + cfg.Role = role + cfg.Sk = sk + cfg.ServerName = serverName + cfg.BindAddr = bindAddr + cfg.BindPort = bindPort + err = cfg.Check() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + visitorConfs[cfg.ProxyName] = cfg + } else { + fmt.Println("invalid role") + os.Exit(1) + } + + err = startService(proxyConfs, visitorConfs) if err != nil { fmt.Println(err) os.Exit(1) } - - if cfg.Role == "server" { - proxyConfs := map[string]config.ProxyConf{ - cfg.ProxyName: cfg, - } - err = startService(proxyConfs, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } else { - visitorConfs := map[string]config.ProxyConf{ - cfg.ProxyName: cfg, - } - err = startService(nil, visitorConfs) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } return nil }, } diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index c3d13ffd..2307eeb3 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -73,6 +73,10 @@ remote_port = 6001 group = test_group # group should have same group key group_key = 123456 +# enable health check for the backend service, it support 'tcp' and 'http' now +# frpc will connect local service's port to detect it's healthy status +health_check_type = tcp +health_check_interval_s = 10 [ssh_random] type = tcp @@ -126,6 +130,11 @@ locations = /,/pic host_header_rewrite = example.com # params with prefix "header_" will be used to update http request headers header_X-From-Where = frp +health_check_type = http +# frpc will send a GET http request '/status' to local http service +# http service is alive when it return 2xx http response code +health_check_url = /status +health_check_interval_s = 10 [web02] type = https diff --git a/models/config/proxy.go b/models/config/proxy.go index 0270c1c0..b600be5c 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -91,7 +91,9 @@ func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg P if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil { return } - err = cfg.CheckForCli() + if err = cfg.CheckForCli(); err != nil { + return + } return } @@ -104,6 +106,9 @@ type BaseProxyConf struct { UseCompression bool `json:"use_compression"` Group string `json:"group"` GroupKey string `json:"group_key"` + + LocalSvrConf + HealthCheckConf // only used for client } func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf { @@ -119,6 +124,12 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool { cfg.GroupKey != cmp.GroupKey { return false } + if !cfg.LocalSvrConf.compare(&cmp.LocalSvrConf) { + return false + } + if !cfg.HealthCheckConf.compare(&cmp.HealthCheckConf) { + return false + } return true } @@ -151,6 +162,14 @@ func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section i cfg.Group = section["group"] cfg.GroupKey = section["group_key"] + + if err := cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return err + } + + if err := cfg.HealthCheckConf.UnmarshalFromIni(prefix, name, section); err != nil { + return err + } return nil } @@ -163,6 +182,16 @@ func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { pMsg.GroupKey = cfg.GroupKey } +func (cfg *BaseProxyConf) checkForCli() (err error) { + if err = cfg.LocalSvrConf.checkForCli(); err != nil { + return + } + if err = cfg.HealthCheckConf.checkForCli(); err != nil { + return + } + return nil +} + // Bind info type BindInfoConf struct { RemotePort int `json:"remote_port"` @@ -335,12 +364,70 @@ func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section in return } +func (cfg *LocalSvrConf) checkForCli() (err error) { + if cfg.Plugin == "" { + if cfg.LocalIp == "" { + err = fmt.Errorf("local ip or plugin is required") + return + } + if cfg.LocalPort <= 0 { + err = fmt.Errorf("error local_port") + return + } + } + return +} + +// Health check info +type HealthCheckConf struct { + HealthCheckType string `json:"health_check_type"` // tcp | http + HealthCheckIntervalS int `json:"health_check_interval_s"` + HealthCheckUrl string `json:"health_check_url"` + + // local_ip + local_port + HealthCheckAddr string `json:"-"` +} + +func (cfg *HealthCheckConf) compare(cmp *HealthCheckConf) bool { + if cfg.HealthCheckType != cmp.HealthCheckType || + cfg.HealthCheckUrl != cmp.HealthCheckUrl || + cfg.HealthCheckIntervalS != cmp.HealthCheckIntervalS { + return false + } + return true +} + +func (cfg *HealthCheckConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + cfg.HealthCheckType = section["health_check_type"] + cfg.HealthCheckUrl = section["health_check_url"] + + if tmpStr, ok := section["health_check_interval_s"]; ok { + if cfg.HealthCheckIntervalS, err = strconv.Atoi(tmpStr); err != nil { + return fmt.Errorf("Parse conf error: proxy [%s] health_check_interval_s error", name) + } + } + return +} + +func (cfg *HealthCheckConf) checkForCli() error { + if cfg.HealthCheckType != "" && cfg.HealthCheckType != "tcp" && cfg.HealthCheckType != "http" { + return fmt.Errorf("unsupport health check type") + } + if cfg.HealthCheckType != "" { + if cfg.HealthCheckType == "http" && cfg.HealthCheckUrl == "" { + return fmt.Errorf("health_check_url is required for health check type 'http'") + } + if cfg.HealthCheckIntervalS <= 0 { + return fmt.Errorf("health_check_interval_s is required and should greater than 0") + } + } + return nil +} + // TCP type TcpProxyConf struct { BaseProxyConf BindInfoConf - - LocalSvrConf } func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool { @@ -350,8 +437,7 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool { } if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || - !cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) { + !cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) { return false } return true @@ -369,9 +455,6 @@ func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section in if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { - return - } return } @@ -380,7 +463,12 @@ func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BindInfoConf.MarshalToMsg(pMsg) } -func (cfg *TcpProxyConf) CheckForCli() error { return nil } +func (cfg *TcpProxyConf) CheckForCli() (err error) { + if err = cfg.BaseProxyConf.checkForCli(); err != nil { + return err + } + return +} func (cfg *TcpProxyConf) CheckForSvr() error { return nil } @@ -388,8 +476,6 @@ func (cfg *TcpProxyConf) CheckForSvr() error { return nil } type UdpProxyConf struct { BaseProxyConf BindInfoConf - - LocalSvrConf } func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool { @@ -399,8 +485,7 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool { } if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || - !cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) { + !cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) { return false } return true @@ -418,9 +503,6 @@ func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section in if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { - return - } return } @@ -429,7 +511,12 @@ func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BindInfoConf.MarshalToMsg(pMsg) } -func (cfg *UdpProxyConf) CheckForCli() error { return nil } +func (cfg *UdpProxyConf) CheckForCli() (err error) { + if err = cfg.BaseProxyConf.checkForCli(); err != nil { + return + } + return +} func (cfg *UdpProxyConf) CheckForSvr() error { return nil } @@ -438,8 +525,6 @@ type HttpProxyConf struct { BaseProxyConf DomainConf - LocalSvrConf - Locations []string `json:"locations"` HttpUser string `json:"http_user"` HttpPwd string `json:"http_pwd"` @@ -455,7 +540,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.DomainConf.compare(&cmpConf.DomainConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") || cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite || cfg.HttpUser != cmpConf.HttpUser || @@ -494,9 +578,6 @@ func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section i if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { - return - } var ( tmpStr string @@ -533,6 +614,9 @@ func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { } func (cfg *HttpProxyConf) CheckForCli() (err error) { + if err = cfg.BaseProxyConf.checkForCli(); err != nil { + return + } if err = cfg.DomainConf.checkForCli(); err != nil { return } @@ -554,8 +638,6 @@ func (cfg *HttpProxyConf) CheckForSvr() (err error) { type HttpsProxyConf struct { BaseProxyConf DomainConf - - LocalSvrConf } func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool { @@ -565,8 +647,7 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool { } if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || - !cfg.DomainConf.compare(&cmpConf.DomainConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) { + !cfg.DomainConf.compare(&cmpConf.DomainConf) { return false } return true @@ -584,9 +665,6 @@ func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { - return - } return } @@ -596,6 +674,9 @@ func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { } func (cfg *HttpsProxyConf) CheckForCli() (err error) { + if err = cfg.BaseProxyConf.checkForCli(); err != nil { + return + } if err = cfg.DomainConf.checkForCli(); err != nil { return } @@ -619,14 +700,6 @@ type StcpProxyConf struct { Role string `json:"role"` Sk string `json:"sk"` - - // used in role server - LocalSvrConf - - // used in role visitor - ServerName string `json:"server_name"` - BindAddr string `json:"bind_addr"` - BindPort int `json:"bind_port"` } func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool { @@ -636,12 +709,8 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool { } if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || cfg.Role != cmpConf.Role || - cfg.Sk != cmpConf.Sk || - cfg.ServerName != cmpConf.ServerName || - cfg.BindAddr != cmpConf.BindAddr || - cfg.BindPort != cmpConf.BindPort { + cfg.Sk != cmpConf.Sk { return false } return true @@ -658,35 +727,15 @@ func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section i return } - tmpStr := section["role"] - if tmpStr == "" { - tmpStr = "server" - } - if tmpStr == "server" || tmpStr == "visitor" { - cfg.Role = tmpStr - } else { - return fmt.Errorf("Parse conf error: proxy [%s] incorrect role [%s]", name, tmpStr) + cfg.Role = section["role"] + if cfg.Role != "server" { + return fmt.Errorf("Parse conf error: proxy [%s] incorrect role [%s]", name, cfg.Role) } cfg.Sk = section["sk"] - if tmpStr == "visitor" { - cfg.ServerName = prefix + section["server_name"] - if cfg.BindAddr = section["bind_addr"]; cfg.BindAddr == "" { - cfg.BindAddr = "127.0.0.1" - } - - if tmpStr, ok := section["bind_port"]; ok { - if cfg.BindPort, err = strconv.Atoi(tmpStr); err != nil { - return fmt.Errorf("Parse conf error: proxy [%s] bind_port error", name) - } - } else { - return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name) - } - } else { - if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } return } @@ -697,19 +746,12 @@ func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { } func (cfg *StcpProxyConf) CheckForCli() (err error) { - if cfg.Role != "server" && cfg.Role != "visitor" { - err = fmt.Errorf("role should be 'server' or 'visitor'") + if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } - if cfg.Role == "visitor" { - if cfg.BindAddr == "" { - err = fmt.Errorf("bind_addr shouldn't be empty") - return - } - if cfg.BindPort == 0 { - err = fmt.Errorf("bind_port should be set") - return - } + if cfg.Role != "server" { + err = fmt.Errorf("role should be 'server'") + return } return } @@ -724,14 +766,6 @@ type XtcpProxyConf struct { Role string `json:"role"` Sk string `json:"sk"` - - // used in role server - LocalSvrConf - - // used in role visitor - ServerName string `json:"server_name"` - BindAddr string `json:"bind_addr"` - BindPort int `json:"bind_port"` } func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool { @@ -743,10 +777,7 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || cfg.Role != cmpConf.Role || - cfg.Sk != cmpConf.Sk || - cfg.ServerName != cmpConf.ServerName || - cfg.BindAddr != cmpConf.BindAddr || - cfg.BindPort != cmpConf.BindPort { + cfg.Sk != cmpConf.Sk { return false } return true @@ -763,35 +794,15 @@ func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section i return } - tmpStr := section["role"] - if tmpStr == "" { - tmpStr = "server" - } - if tmpStr == "server" || tmpStr == "visitor" { - cfg.Role = tmpStr - } else { - return fmt.Errorf("Parse conf error: proxy [%s] incorrect role [%s]", name, tmpStr) + cfg.Role = section["role"] + if cfg.Role != "server" { + return fmt.Errorf("Parse conf error: proxy [%s] incorrect role [%s]", name, cfg.Role) } cfg.Sk = section["sk"] - if tmpStr == "visitor" { - cfg.ServerName = prefix + section["server_name"] - if cfg.BindAddr = section["bind_addr"]; cfg.BindAddr == "" { - cfg.BindAddr = "127.0.0.1" - } - - if tmpStr, ok := section["bind_port"]; ok { - if cfg.BindPort, err = strconv.Atoi(tmpStr); err != nil { - return fmt.Errorf("Parse conf error: proxy [%s] bind_port error", name) - } - } else { - return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name) - } - } else { - if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } return } @@ -802,19 +813,12 @@ func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { } func (cfg *XtcpProxyConf) CheckForCli() (err error) { - if cfg.Role != "server" && cfg.Role != "visitor" { - err = fmt.Errorf("role should be 'server' or 'visitor'") + if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } - if cfg.Role == "visitor" { - if cfg.BindAddr == "" { - err = fmt.Errorf("bind_addr shouldn't be empty") - return - } - if cfg.BindPort == 0 { - err = fmt.Errorf("bind_port should be set") - return - } + if cfg.Role != "server" { + err = fmt.Errorf("role should be 'server'") + return } return } @@ -857,8 +861,8 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in // if len(startProxy) is 0, start all // otherwise just start proxies in startProxy map -func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) ( - proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) { +func LoadAllConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) ( + proxyConfs map[string]ProxyConf, visitorConfs map[string]VisitorConf, err error) { if prefix != "" { prefix += "." @@ -869,7 +873,7 @@ func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]st startAll = false } proxyConfs = make(map[string]ProxyConf) - visitorConfs = make(map[string]ProxyConf) + visitorConfs = make(map[string]VisitorConf) for name, section := range conf { if name == "common" { continue @@ -894,16 +898,27 @@ func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]st } for subName, subSection := range subSections { - cfg, err := NewProxyConfFromIni(prefix, subName, subSection) - if err != nil { - return proxyConfs, visitorConfs, err + if subSection["role"] == "" { + subSection["role"] = "server" } - role := subSection["role"] - if role == "visitor" { + if role == "server" { + cfg, errRet := NewProxyConfFromIni(prefix, subName, subSection) + if errRet != nil { + err = errRet + return + } + proxyConfs[prefix+subName] = cfg + } else if role == "visitor" { + cfg, errRet := NewVisitorConfFromIni(prefix, subName, subSection) + if errRet != nil { + err = errRet + return + } visitorConfs[prefix+subName] = cfg } else { - proxyConfs[prefix+subName] = cfg + err = fmt.Errorf("role should be 'server' or 'visitor'") + return } } } diff --git a/models/config/visitor.go b/models/config/visitor.go new file mode 100644 index 00000000..4233375c --- /dev/null +++ b/models/config/visitor.go @@ -0,0 +1,213 @@ +// Copyright 2018 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 ( + "fmt" + "reflect" + "strconv" + + "github.com/fatedier/frp/models/consts" + + ini "github.com/vaughan0/go-ini" +) + +var ( + visitorConfTypeMap map[string]reflect.Type +) + +func init() { + visitorConfTypeMap = make(map[string]reflect.Type) + visitorConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpVisitorConf{}) + visitorConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpVisitorConf{}) +} + +type VisitorConf interface { + GetBaseInfo() *BaseVisitorConf + Compare(cmp VisitorConf) bool + UnmarshalFromIni(prefix string, name string, section ini.Section) error + Check() error +} + +func NewVisitorConfByType(cfgType string) VisitorConf { + v, ok := visitorConfTypeMap[cfgType] + if !ok { + return nil + } + cfg := reflect.New(v).Interface().(VisitorConf) + return cfg +} + +func NewVisitorConfFromIni(prefix string, name string, section ini.Section) (cfg VisitorConf, err error) { + cfgType := section["type"] + if cfgType == "" { + err = fmt.Errorf("visitor [%s] type shouldn't be empty", name) + return + } + cfg = NewVisitorConfByType(cfgType) + if cfg == nil { + err = fmt.Errorf("visitor [%s] type [%s] error", name, cfgType) + return + } + if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil { + return + } + if err = cfg.Check(); err != nil { + return + } + return +} + +type BaseVisitorConf struct { + ProxyName string `json:"proxy_name"` + ProxyType string `json:"proxy_type"` + UseEncryption bool `json:"use_encryption"` + UseCompression bool `json:"use_compression"` + Role string `json:"role"` + Sk string `json:"sk"` + ServerName string `json:"server_name"` + BindAddr string `json:"bind_addr"` + BindPort int `json:"bind_port"` +} + +func (cfg *BaseVisitorConf) GetBaseInfo() *BaseVisitorConf { + return cfg +} + +func (cfg *BaseVisitorConf) compare(cmp *BaseVisitorConf) bool { + if cfg.ProxyName != cmp.ProxyName || + cfg.ProxyType != cmp.ProxyType || + cfg.UseEncryption != cmp.UseEncryption || + cfg.UseCompression != cmp.UseCompression || + cfg.Role != cmp.Role || + cfg.Sk != cmp.Sk || + cfg.ServerName != cmp.ServerName || + cfg.BindAddr != cmp.BindAddr || + cfg.BindPort != cmp.BindPort { + return false + } + return true +} + +func (cfg *BaseVisitorConf) check() (err error) { + if cfg.Role != "visitor" { + err = fmt.Errorf("invalid role") + return + } + if cfg.BindAddr == "" { + err = fmt.Errorf("bind_addr shouldn't be empty") + return + } + if cfg.BindPort <= 0 { + err = fmt.Errorf("bind_port is required") + return + } + return +} + +func (cfg *BaseVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + var ( + tmpStr string + ok bool + ) + cfg.ProxyName = prefix + name + cfg.ProxyType = section["type"] + + if tmpStr, ok = section["use_encryption"]; ok && tmpStr == "true" { + cfg.UseEncryption = true + } + if tmpStr, ok = section["use_compression"]; ok && tmpStr == "true" { + cfg.UseCompression = true + } + + cfg.Role = section["role"] + if cfg.Role != "visitor" { + return fmt.Errorf("Parse conf error: proxy [%s] incorrect role [%s]", name, cfg.Role) + } + cfg.Sk = section["sk"] + cfg.ServerName = prefix + section["server_name"] + if cfg.BindAddr = section["bind_addr"]; cfg.BindAddr == "" { + cfg.BindAddr = "127.0.0.1" + } + + if tmpStr, ok = section["bind_port"]; ok { + if cfg.BindPort, err = strconv.Atoi(tmpStr); err != nil { + return fmt.Errorf("Parse conf error: proxy [%s] bind_port incorrect", name) + } + } else { + return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name) + } + return nil +} + +type StcpVisitorConf struct { + BaseVisitorConf +} + +func (cfg *StcpVisitorConf) Compare(cmp VisitorConf) bool { + cmpConf, ok := cmp.(*StcpVisitorConf) + if !ok { + return false + } + + if !cfg.BaseVisitorConf.compare(&cmpConf.BaseVisitorConf) { + return false + } + return true +} + +func (cfg *StcpVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseVisitorConf.UnmarshalFromIni(prefix, name, section); err != nil { + return + } + return +} + +func (cfg *StcpVisitorConf) Check() (err error) { + if err = cfg.BaseVisitorConf.check(); err != nil { + return + } + return +} + +type XtcpVisitorConf struct { + BaseVisitorConf +} + +func (cfg *XtcpVisitorConf) Compare(cmp VisitorConf) bool { + cmpConf, ok := cmp.(*XtcpVisitorConf) + if !ok { + return false + } + + if !cfg.BaseVisitorConf.compare(&cmpConf.BaseVisitorConf) { + return false + } + return true +} + +func (cfg *XtcpVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseVisitorConf.UnmarshalFromIni(prefix, name, section); err != nil { + return + } + return +} + +func (cfg *XtcpVisitorConf) Check() (err error) { + if err = cfg.BaseVisitorConf.check(); err != nil { + return + } + return +} From 42ee536dae2ace3d423de6c5262819b71e82312c Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 27 Jun 2018 11:46:53 +0800 Subject: [PATCH 02/20] add module comments for vgo --- cmd/frpc/main.go | 2 +- cmd/frps/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/frpc/main.go b/cmd/frpc/main.go index 215325dd..3c6f8e60 100644 --- a/cmd/frpc/main.go +++ b/cmd/frpc/main.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package main // "github.com/fatedier/frp/cmd/frpc" import ( "github.com/fatedier/frp/cmd/frpc/sub" diff --git a/cmd/frps/main.go b/cmd/frps/main.go index 4e235ac8..34f90d10 100644 --- a/cmd/frps/main.go +++ b/cmd/frps/main.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package main // "github.com/fatedier/frp/cmd/frps" import ( "github.com/fatedier/golib/crypto" From 0d02f291e323625bbb6ef6efc5f30374cb92e129 Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 11 Jul 2018 23:27:47 +0800 Subject: [PATCH 03/20] refactor ci test --- Makefile | 20 +- tests/clean_test.sh | 20 -- tests/{conf => config}/auto_test_frpc.ini | 2 +- .../auto_test_frpc_visitor.ini | 2 +- tests/{conf => config}/auto_test_frps.ini | 2 +- tests/config/config.go | 9 + tests/consts/consts.go | 65 ++++++ tests/func_test.go | 215 ++++++++---------- tests/{ => mock}/echo_server.go | 17 +- tests/{ => mock}/http_server.go | 16 +- tests/run_test.sh | 9 - tests/util/process.go | 29 +++ tests/{ => util}/util.go | 20 +- 13 files changed, 240 insertions(+), 186 deletions(-) delete mode 100755 tests/clean_test.sh rename tests/{conf => config}/auto_test_frpc.ini (99%) rename tests/{conf => config}/auto_test_frpc_visitor.ini (92%) rename tests/{conf => config}/auto_test_frps.ini (88%) create mode 100644 tests/config/config.go create mode 100644 tests/consts/consts.go rename tests/{ => mock}/echo_server.go (83%) rename tests/{ => mock}/http_server.go (80%) delete mode 100755 tests/run_test.sh create mode 100644 tests/util/process.go rename tests/{ => util}/util.go (86%) diff --git a/Makefile b/Makefile index 488e57a1..bc450af9 100644 --- a/Makefile +++ b/Makefile @@ -26,24 +26,18 @@ frpc: test: gotest gotest: - go test -v ./assets/... - go test -v ./client/... - go test -v ./cmd/... - go test -v ./models/... - go test -v ./server/... - go test -v ./utils/... + go test -v --cover ./assets/... + go test -v --cover ./client/... + go test -v --cover ./cmd/... + go test -v --cover ./models/... + go test -v --cover ./server/... + go test -v --cover ./utils/... ci: - cd ./tests && ./run_test.sh && cd - - go test -v ./tests/... - cd ./tests && ./clean_test.sh && cd - - -cic: - cd ./tests && ./clean_test.sh && cd - + go test -count=1 -v ./tests/... alltest: gotest ci clean: rm -f ./bin/frpc rm -f ./bin/frps - cd ./tests && ./clean_test.sh && cd - diff --git a/tests/clean_test.sh b/tests/clean_test.sh deleted file mode 100755 index b0b37636..00000000 --- a/tests/clean_test.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -pid=`ps aux|grep './../bin/frps -c ./conf/auto_test_frps.ini'|grep -v grep|awk {'print $2'}` -if [ -n "${pid}" ]; then - kill ${pid} -fi - -pid=`ps aux|grep './../bin/frpc -c ./conf/auto_test_frpc.ini'|grep -v grep|awk {'print $2'}` -if [ -n "${pid}" ]; then - kill ${pid} -fi - -pid=`ps aux|grep './../bin/frpc -c ./conf/auto_test_frpc_visitor.ini'|grep -v grep|awk {'print $2'}` -if [ -n "${pid}" ]; then - kill ${pid} -fi - -rm -f ./frps.log -rm -f ./frpc.log -rm -f ./frpc_visitor.log diff --git a/tests/conf/auto_test_frpc.ini b/tests/config/auto_test_frpc.ini similarity index 99% rename from tests/conf/auto_test_frpc.ini rename to tests/config/auto_test_frpc.ini index 14f2e851..407d679e 100644 --- a/tests/conf/auto_test_frpc.ini +++ b/tests/config/auto_test_frpc.ini @@ -1,7 +1,7 @@ [common] server_addr = 127.0.0.1 server_port = 10700 -log_file = ./frpc.log +log_file = console # debug, info, warn, error log_level = debug token = 123456 diff --git a/tests/conf/auto_test_frpc_visitor.ini b/tests/config/auto_test_frpc_visitor.ini similarity index 92% rename from tests/conf/auto_test_frpc_visitor.ini rename to tests/config/auto_test_frpc_visitor.ini index e06d1604..660c7931 100644 --- a/tests/conf/auto_test_frpc_visitor.ini +++ b/tests/config/auto_test_frpc_visitor.ini @@ -1,7 +1,7 @@ [common] server_addr = 0.0.0.0 server_port = 10700 -log_file = ./frpc_visitor.log +log_file = console # debug, info, warn, error log_level = debug token = 123456 diff --git a/tests/conf/auto_test_frps.ini b/tests/config/auto_test_frps.ini similarity index 88% rename from tests/conf/auto_test_frps.ini rename to tests/config/auto_test_frps.ini index fc62c39b..9300b551 100644 --- a/tests/conf/auto_test_frps.ini +++ b/tests/config/auto_test_frps.ini @@ -2,7 +2,7 @@ bind_addr = 0.0.0.0 bind_port = 10700 vhost_http_port = 10804 -log_file = ./frps.log +log_file = console log_level = debug token = 123456 allow_ports = 10000-20000,20002,30000-50000 diff --git a/tests/config/config.go b/tests/config/config.go new file mode 100644 index 00000000..80e096b8 --- /dev/null +++ b/tests/config/config.go @@ -0,0 +1,9 @@ +package util + +import ( + "io/ioutil" +) + +func GenerateConfigFile(path string, content string) error { + return ioutil.WriteFile(path, []byte(content), 0666) +} diff --git a/tests/consts/consts.go b/tests/consts/consts.go new file mode 100644 index 00000000..7e70c89b --- /dev/null +++ b/tests/consts/consts.go @@ -0,0 +1,65 @@ +package consts + +import "path/filepath" + +var ( + FRPS_BIN_PATH = "../bin/frps" + FRPC_BIN_PATH = "../bin/frpc" + + SERVER_ADDR = "127.0.0.1" + ADMIN_ADDR = "127.0.0.1:10600" + ADMIN_USER = "abc" + ADMIN_PWD = "abc" + + TEST_STR = "frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet." + TEST_TCP_PORT int = 10701 + TEST_TCP2_PORT int = 10702 + TEST_TCP_FRP_PORT int = 10801 + TEST_TCP2_FRP_PORT int = 10802 + TEST_TCP_EC_FRP_PORT int = 10901 + TEST_TCP_ECHO_STR string = "tcp type:" + TEST_STR + + TEST_UDP_PORT int = 10702 + TEST_UDP_FRP_PORT int = 10802 + TEST_UDP_EC_FRP_PORT int = 10902 + TEST_UDP_ECHO_STR string = "udp type:" + TEST_STR + + TEST_UNIX_DOMAIN_ADDR string = "/tmp/frp_echo_server.sock" + TEST_UNIX_DOMAIN_FRP_PORT int = 10803 + TEST_UNIX_DOMAIN_STR string = "unix domain type:" + TEST_STR + + TEST_HTTP_PORT int = 10704 + TEST_HTTP_FRP_PORT int = 10804 + TEST_HTTP_NORMAL_STR string = "http normal string: " + TEST_STR + TEST_HTTP_FOO_STR string = "http foo string: " + TEST_STR + TEST_HTTP_BAR_STR string = "http bar string: " + TEST_STR + + TEST_STCP_FRP_PORT int = 10805 + TEST_STCP_EC_FRP_PORT int = 10905 + TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR + + ProxyTcpPortNotAllowed string = "tcp_port_not_allowed" + ProxyTcpPortUnavailable string = "tcp_port_unavailable" + ProxyTcpPortNormal string = "tcp_port_normal" + ProxyTcpRandomPort string = "tcp_random_port" + ProxyUdpPortNotAllowed string = "udp_port_not_allowed" + ProxyUdpPortNormal string = "udp_port_normal" + ProxyUdpRandomPort string = "udp_random_port" + ProxyHttpProxy string = "http_proxy" + + ProxyRangeTcpPrefix string = "range_tcp" +) + +func init() { + if path, err := filepath.Abs(FRPS_BIN_PATH); err != nil { + panic(err) + } else { + FRPS_BIN_PATH = path + } + + if path, err := filepath.Abs(FRPC_BIN_PATH); err != nil { + panic(err) + } else { + FRPC_BIN_PATH = path + } +} diff --git a/tests/func_test.go b/tests/func_test.go index 1d0cd377..a83a6dc0 100644 --- a/tests/func_test.go +++ b/tests/func_test.go @@ -13,194 +13,179 @@ import ( "github.com/fatedier/frp/client" "github.com/fatedier/frp/server/ports" + "github.com/fatedier/frp/tests/consts" + "github.com/fatedier/frp/tests/mock" + "github.com/fatedier/frp/tests/util" gnet "github.com/fatedier/golib/net" ) -var ( - SERVER_ADDR = "127.0.0.1" - ADMIN_ADDR = "127.0.0.1:10600" - ADMIN_USER = "abc" - ADMIN_PWD = "abc" - - TEST_STR = "frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet." - TEST_TCP_PORT int = 10701 - TEST_TCP2_PORT int = 10702 - TEST_TCP_FRP_PORT int = 10801 - TEST_TCP2_FRP_PORT int = 10802 - TEST_TCP_EC_FRP_PORT int = 10901 - TEST_TCP_ECHO_STR string = "tcp type:" + TEST_STR - - TEST_UDP_PORT int = 10702 - TEST_UDP_FRP_PORT int = 10802 - TEST_UDP_EC_FRP_PORT int = 10902 - TEST_UDP_ECHO_STR string = "udp type:" + TEST_STR - - TEST_UNIX_DOMAIN_ADDR string = "/tmp/frp_echo_server.sock" - TEST_UNIX_DOMAIN_FRP_PORT int = 10803 - TEST_UNIX_DOMAIN_STR string = "unix domain type:" + TEST_STR - - TEST_HTTP_PORT int = 10704 - TEST_HTTP_FRP_PORT int = 10804 - TEST_HTTP_NORMAL_STR string = "http normal string: " + TEST_STR - TEST_HTTP_FOO_STR string = "http foo string: " + TEST_STR - TEST_HTTP_BAR_STR string = "http bar string: " + TEST_STR - - TEST_STCP_FRP_PORT int = 10805 - TEST_STCP_EC_FRP_PORT int = 10905 - TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR - - ProxyTcpPortNotAllowed string = "tcp_port_not_allowed" - ProxyTcpPortUnavailable string = "tcp_port_unavailable" - ProxyTcpPortNormal string = "tcp_port_normal" - ProxyTcpRandomPort string = "tcp_random_port" - ProxyUdpPortNotAllowed string = "udp_port_not_allowed" - ProxyUdpPortNormal string = "udp_port_normal" - ProxyUdpRandomPort string = "udp_random_port" - ProxyHttpProxy string = "http_proxy" - - ProxyRangeTcpPrefix string = "range_tcp" -) - func init() { - go StartTcpEchoServer() - go StartTcpEchoServer2() - go StartUdpEchoServer() - go StartUnixDomainServer() - go StartHttpServer() + go mock.StartTcpEchoServer(consts.TEST_TCP_PORT) + go mock.StartTcpEchoServer2(consts.TEST_TCP2_PORT) + go mock.StartUdpEchoServer(consts.TEST_UDP_PORT) + go mock.StartUnixDomainServer(consts.TEST_UNIX_DOMAIN_ADDR) + go mock.StartHttpServer(consts.TEST_HTTP_PORT) + + if err := runFrps(); err != nil { + panic(err) + } + time.Sleep(200 * time.Millisecond) + + if err := runFrpc(); err != nil { + panic(err) + } + if err := runFrpcVisitor(); err != nil { + panic(err) + } time.Sleep(500 * time.Millisecond) } +func runFrps() error { + p := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./config/auto_test_frps.ini"}) + return p.Start() +} + +func runFrpc() error { + p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./config/auto_test_frpc.ini"}) + return p.Start() +} + +func runFrpcVisitor() error { + p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./config/auto_test_frpc_visitor.ini"}) + return p.Start() +} + func TestTcp(t *testing.T) { assert := assert.New(t) // Normal - addr := fmt.Sprintf("127.0.0.1:%d", TEST_TCP_FRP_PORT) - res, err := sendTcpMsg(addr, TEST_TCP_ECHO_STR) + addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_FRP_PORT) + res, err := util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_TCP_ECHO_STR, res) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) // Encrytion and compression - addr = fmt.Sprintf("127.0.0.1:%d", TEST_TCP_EC_FRP_PORT) - res, err = sendTcpMsg(addr, TEST_TCP_ECHO_STR) + addr = fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_EC_FRP_PORT) + res, err = util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_TCP_ECHO_STR, res) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) } func TestUdp(t *testing.T) { assert := assert.New(t) // Normal - addr := fmt.Sprintf("127.0.0.1:%d", TEST_UDP_FRP_PORT) - res, err := sendUdpMsg(addr, TEST_UDP_ECHO_STR) + addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_UDP_FRP_PORT) + res, err := util.SendUdpMsg(addr, consts.TEST_UDP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_UDP_ECHO_STR, res) + assert.Equal(consts.TEST_UDP_ECHO_STR, res) // Encrytion and compression - addr = fmt.Sprintf("127.0.0.1:%d", TEST_UDP_EC_FRP_PORT) - res, err = sendUdpMsg(addr, TEST_UDP_ECHO_STR) + addr = fmt.Sprintf("127.0.0.1:%d", consts.TEST_UDP_EC_FRP_PORT) + res, err = util.SendUdpMsg(addr, consts.TEST_UDP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_UDP_ECHO_STR, res) + assert.Equal(consts.TEST_UDP_ECHO_STR, res) } func TestUnixDomain(t *testing.T) { assert := assert.New(t) // Normal - addr := fmt.Sprintf("127.0.0.1:%d", TEST_UNIX_DOMAIN_FRP_PORT) - res, err := sendTcpMsg(addr, TEST_UNIX_DOMAIN_STR) + addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_UNIX_DOMAIN_FRP_PORT) + res, err := util.SendTcpMsg(addr, consts.TEST_UNIX_DOMAIN_STR) if assert.NoError(err) { - assert.Equal(TEST_UNIX_DOMAIN_STR, res) + assert.Equal(consts.TEST_UNIX_DOMAIN_STR, res) } } func TestStcp(t *testing.T) { assert := assert.New(t) // Normal - addr := fmt.Sprintf("127.0.0.1:%d", TEST_STCP_FRP_PORT) - res, err := sendTcpMsg(addr, TEST_STCP_ECHO_STR) + addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_STCP_FRP_PORT) + res, err := util.SendTcpMsg(addr, consts.TEST_STCP_ECHO_STR) if assert.NoError(err) { - assert.Equal(TEST_STCP_ECHO_STR, res) + assert.Equal(consts.TEST_STCP_ECHO_STR, res) } // Encrytion and compression - addr = fmt.Sprintf("127.0.0.1:%d", TEST_STCP_EC_FRP_PORT) - res, err = sendTcpMsg(addr, TEST_STCP_ECHO_STR) + addr = fmt.Sprintf("127.0.0.1:%d", consts.TEST_STCP_EC_FRP_PORT) + res, err = util.SendTcpMsg(addr, consts.TEST_STCP_ECHO_STR) if assert.NoError(err) { - assert.Equal(TEST_STCP_ECHO_STR, res) + assert.Equal(consts.TEST_STCP_ECHO_STR, res) } } func TestHttp(t *testing.T) { assert := assert.New(t) // web01 - code, body, _, err := sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "", nil, "") + code, body, _, err := util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "", nil, "") if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_NORMAL_STR, body) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // web02 - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test2.frp.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test2.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_NORMAL_STR, body) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // error host header - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "errorhost.frp.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "errorhost.frp.com", nil, "") if assert.NoError(err) { assert.Equal(404, code) } // web03 - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_NORMAL_STR, body) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/foo", TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/foo", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_FOO_STR, body) + assert.Equal(consts.TEST_HTTP_FOO_STR, body) } // web04 - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/bar", TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/bar", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_BAR_STR, body) + assert.Equal(consts.TEST_HTTP_BAR_STR, body) } // web05 - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test5.frp.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", nil, "") if assert.NoError(err) { assert.Equal(401, code) } headers := make(map[string]string) - headers["Authorization"] = basicAuth("test", "test") - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test5.frp.com", headers, "") + headers["Authorization"] = util.BasicAuth("test", "test") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", headers, "") if assert.NoError(err) { assert.Equal(401, code) } // web06 var header http.Header - code, body, header, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test6.frp.com", nil, "") + code, body, header, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test6.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_NORMAL_STR, body) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) assert.Equal("true", header.Get("X-Header-Set")) } // subhost01 - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test01.sub.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test01.sub.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal("test01.sub.com", body) } // subhost02 - code, body, _, err = sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), "test02.sub.com", nil, "") + code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test02.sub.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal("test02.sub.com", body) @@ -210,47 +195,47 @@ func TestHttp(t *testing.T) { func TestWebSocket(t *testing.T) { assert := assert.New(t) - u := url.URL{Scheme: "ws", Host: fmt.Sprintf("%s:%d", "127.0.0.1", TEST_HTTP_FRP_PORT), Path: "/ws"} + u := url.URL{Scheme: "ws", Host: fmt.Sprintf("%s:%d", "127.0.0.1", consts.TEST_HTTP_FRP_PORT), Path: "/ws"} c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) assert.NoError(err) defer c.Close() - err = c.WriteMessage(websocket.TextMessage, []byte(TEST_HTTP_NORMAL_STR)) + err = c.WriteMessage(websocket.TextMessage, []byte(consts.TEST_HTTP_NORMAL_STR)) assert.NoError(err) _, msg, err := c.ReadMessage() assert.NoError(err) - assert.Equal(TEST_HTTP_NORMAL_STR, string(msg)) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, string(msg)) } func TestAllowPorts(t *testing.T) { assert := assert.New(t) // Port not allowed - status, err := getProxyStatus(ProxyTcpPortNotAllowed) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortNotAllowed) if assert.NoError(err) { assert.Equal(client.ProxyStatusStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortNotAllowed.Error())) } - status, err = getProxyStatus(ProxyUdpPortNotAllowed) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpPortNotAllowed) if assert.NoError(err) { assert.Equal(client.ProxyStatusStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortNotAllowed.Error())) } - status, err = getProxyStatus(ProxyTcpPortUnavailable) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortUnavailable) if assert.NoError(err) { assert.Equal(client.ProxyStatusStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortUnAvailable.Error())) } // Port normal - status, err = getProxyStatus(ProxyTcpPortNormal) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortNormal) if assert.NoError(err) { assert.Equal(client.ProxyStatusRunning, status.Status) } - status, err = getProxyStatus(ProxyUdpPortNormal) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpPortNormal) if assert.NoError(err) { assert.Equal(client.ProxyStatusRunning, status.Status) } @@ -259,45 +244,45 @@ func TestAllowPorts(t *testing.T) { func TestRandomPort(t *testing.T) { assert := assert.New(t) // tcp - status, err := getProxyStatus(ProxyTcpRandomPort) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpRandomPort) if assert.NoError(err) { addr := status.RemoteAddr - res, err := sendTcpMsg(addr, TEST_TCP_ECHO_STR) + res, err := util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_TCP_ECHO_STR, res) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) } // udp - status, err = getProxyStatus(ProxyUdpRandomPort) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpRandomPort) if assert.NoError(err) { addr := status.RemoteAddr - res, err := sendUdpMsg(addr, TEST_UDP_ECHO_STR) + res, err := util.SendUdpMsg(addr, consts.TEST_UDP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_UDP_ECHO_STR, res) + assert.Equal(consts.TEST_UDP_ECHO_STR, res) } } func TestPluginHttpProxy(t *testing.T) { assert := assert.New(t) - status, err := getProxyStatus(ProxyHttpProxy) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyHttpProxy) if assert.NoError(err) { assert.Equal(client.ProxyStatusRunning, status.Status) // http proxy addr := status.RemoteAddr - code, body, _, err := sendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", TEST_HTTP_FRP_PORT), + code, body, _, err := util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "", nil, "http://"+addr) if assert.NoError(err) { assert.Equal(200, code) - assert.Equal(TEST_HTTP_NORMAL_STR, body) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // connect method - conn, err := gnet.DialTcpByProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", TEST_TCP_FRP_PORT)) + conn, err := gnet.DialTcpByProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_FRP_PORT)) if assert.NoError(err) { - res, err := sendTcpMsgByConn(conn, TEST_TCP_ECHO_STR) + res, err := util.SendTcpMsgByConn(conn, consts.TEST_TCP_ECHO_STR) assert.NoError(err) - assert.Equal(TEST_TCP_ECHO_STR, res) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) } } } @@ -306,8 +291,8 @@ func TestRangePortsMapping(t *testing.T) { assert := assert.New(t) for i := 0; i < 3; i++ { - name := fmt.Sprintf("%s_%d", ProxyRangeTcpPrefix, i) - status, err := getProxyStatus(name) + name := fmt.Sprintf("%s_%d", consts.ProxyRangeTcpPrefix, i) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, name) if assert.NoError(err) { assert.Equal(client.ProxyStatusRunning, status.Status) } @@ -321,15 +306,15 @@ func TestGroup(t *testing.T) { p1 int p2 int ) - addr := fmt.Sprintf("127.0.0.1:%d", TEST_TCP2_FRP_PORT) + addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP2_FRP_PORT) for i := 0; i < 6; i++ { - res, err := sendTcpMsg(addr, TEST_TCP_ECHO_STR) + res, err := util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) assert.NoError(err) switch res { - case TEST_TCP_ECHO_STR: + case consts.TEST_TCP_ECHO_STR: p1++ - case TEST_TCP_ECHO_STR + TEST_TCP_ECHO_STR: + case consts.TEST_TCP_ECHO_STR + consts.TEST_TCP_ECHO_STR: p2++ } } diff --git a/tests/echo_server.go b/tests/mock/echo_server.go similarity index 83% rename from tests/echo_server.go rename to tests/mock/echo_server.go index 380c0366..a24947f5 100644 --- a/tests/echo_server.go +++ b/tests/mock/echo_server.go @@ -1,4 +1,4 @@ -package tests +package mock import ( "fmt" @@ -10,8 +10,8 @@ import ( frpNet "github.com/fatedier/frp/utils/net" ) -func StartTcpEchoServer() { - l, err := frpNet.ListenTcp("127.0.0.1", TEST_TCP_PORT) +func StartTcpEchoServer(port int) { + l, err := frpNet.ListenTcp("127.0.0.1", port) if err != nil { fmt.Printf("echo server listen error: %v\n", err) return @@ -28,8 +28,8 @@ func StartTcpEchoServer() { } } -func StartTcpEchoServer2() { - l, err := frpNet.ListenTcp("127.0.0.1", TEST_TCP2_PORT) +func StartTcpEchoServer2(port int) { + l, err := frpNet.ListenTcp("127.0.0.1", port) if err != nil { fmt.Printf("echo server2 listen error: %v\n", err) return @@ -46,8 +46,8 @@ func StartTcpEchoServer2() { } } -func StartUdpEchoServer() { - l, err := frpNet.ListenUDP("127.0.0.1", TEST_UDP_PORT) +func StartUdpEchoServer(port int) { + l, err := frpNet.ListenUDP("127.0.0.1", port) if err != nil { fmt.Printf("udp echo server listen error: %v\n", err) return @@ -64,8 +64,7 @@ func StartUdpEchoServer() { } } -func StartUnixDomainServer() { - unixPath := TEST_UNIX_DOMAIN_ADDR +func StartUnixDomainServer(unixPath string) { os.Remove(unixPath) syscall.Umask(0) l, err := net.Listen("unix", unixPath) diff --git a/tests/http_server.go b/tests/mock/http_server.go similarity index 80% rename from tests/http_server.go rename to tests/mock/http_server.go index 640ae9a3..7e97ad61 100644 --- a/tests/http_server.go +++ b/tests/mock/http_server.go @@ -1,4 +1,4 @@ -package tests +package mock import ( "fmt" @@ -7,15 +7,17 @@ import ( "regexp" "strings" + "github.com/fatedier/frp/tests/consts" + "github.com/gorilla/websocket" ) var upgrader = websocket.Upgrader{} -func StartHttpServer() { +func StartHttpServer(port int) { http.HandleFunc("/", handleHttp) http.HandleFunc("/ws", handleWebSocket) - http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", TEST_HTTP_PORT), nil) + http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil) } func handleWebSocket(w http.ResponseWriter, r *http.Request) { @@ -58,15 +60,15 @@ func handleHttp(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Host, "127.0.0.1") || strings.Contains(r.Host, "test2.frp.com") || strings.Contains(r.Host, "test5.frp.com") || strings.Contains(r.Host, "test6.frp.com") { w.WriteHeader(200) - w.Write([]byte(TEST_HTTP_NORMAL_STR)) + w.Write([]byte(consts.TEST_HTTP_NORMAL_STR)) } else if strings.Contains(r.Host, "test3.frp.com") { w.WriteHeader(200) if strings.Contains(r.URL.Path, "foo") { - w.Write([]byte(TEST_HTTP_FOO_STR)) + w.Write([]byte(consts.TEST_HTTP_FOO_STR)) } else if strings.Contains(r.URL.Path, "bar") { - w.Write([]byte(TEST_HTTP_BAR_STR)) + w.Write([]byte(consts.TEST_HTTP_BAR_STR)) } else { - w.Write([]byte(TEST_HTTP_NORMAL_STR)) + w.Write([]byte(consts.TEST_HTTP_NORMAL_STR)) } } else { w.WriteHeader(404) diff --git a/tests/run_test.sh b/tests/run_test.sh deleted file mode 100755 index a852a3d0..00000000 --- a/tests/run_test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -./../bin/frps -c ./conf/auto_test_frps.ini & -sleep 1 -./../bin/frpc -c ./conf/auto_test_frpc.ini & -./../bin/frpc -c ./conf/auto_test_frpc_visitor.ini & - -# wait until proxies are connected -sleep 2 diff --git a/tests/util/process.go b/tests/util/process.go new file mode 100644 index 00000000..1e34040d --- /dev/null +++ b/tests/util/process.go @@ -0,0 +1,29 @@ +package util + +import ( + "context" + "os/exec" +) + +type Process struct { + cmd *exec.Cmd + cancel context.CancelFunc +} + +func NewProcess(path string, params []string) *Process { + ctx, cancel := context.WithCancel(context.Background()) + cmd := exec.CommandContext(ctx, path, params...) + return &Process{ + cmd: cmd, + cancel: cancel, + } +} + +func (p *Process) Start() error { + return p.cmd.Start() +} + +func (p *Process) Stop() error { + p.cancel() + return p.cmd.Wait() +} diff --git a/tests/util.go b/tests/util/util.go similarity index 86% rename from tests/util.go rename to tests/util/util.go index 5bc3e224..ac314ec7 100644 --- a/tests/util.go +++ b/tests/util/util.go @@ -1,4 +1,4 @@ -package tests +package util import ( "encoding/base64" @@ -16,13 +16,13 @@ import ( frpNet "github.com/fatedier/frp/utils/net" ) -func getProxyStatus(name string) (status *client.ProxyStatusResp, err error) { - req, err := http.NewRequest("GET", "http://"+ADMIN_ADDR+"/api/status", nil) +func GetProxyStatus(statusAddr string, user string, passwd string, name string) (status *client.ProxyStatusResp, err error) { + req, err := http.NewRequest("GET", "http://"+statusAddr+"/api/status", nil) if err != nil { return status, err } - authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(ADMIN_USER+":"+ADMIN_PWD)) + authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+passwd)) req.Header.Add("Authorization", authStr) resp, err := http.DefaultClient.Do(req) if err != nil { @@ -75,17 +75,17 @@ func getProxyStatus(name string) (status *client.ProxyStatusResp, err error) { return status, errors.New("no proxy status found") } -func sendTcpMsg(addr string, msg string) (res string, err error) { +func SendTcpMsg(addr string, msg string) (res string, err error) { c, err := frpNet.ConnectTcpServer(addr) if err != nil { err = fmt.Errorf("connect to tcp server error: %v", err) return } defer c.Close() - return sendTcpMsgByConn(c, msg) + return SendTcpMsgByConn(c, msg) } -func sendTcpMsgByConn(c net.Conn, msg string) (res string, err error) { +func SendTcpMsgByConn(c net.Conn, msg string) (res string, err error) { timer := time.Now().Add(5 * time.Second) c.SetDeadline(timer) c.Write([]byte(msg)) @@ -99,7 +99,7 @@ func sendTcpMsgByConn(c net.Conn, msg string) (res string, err error) { return string(buf[:n]), nil } -func sendUdpMsg(addr string, msg string) (res string, err error) { +func SendUdpMsg(addr string, msg string) (res string, err error) { udpAddr, errRet := net.ResolveUDPAddr("udp", addr) if errRet != nil { err = fmt.Errorf("resolve udp addr error: %v", err) @@ -126,7 +126,7 @@ func sendUdpMsg(addr string, msg string) (res string, err error) { return string(buf[:n]), nil } -func sendHttpMsg(method, urlStr string, host string, headers map[string]string, proxy string) (code int, body string, header http.Header, err error) { +func SendHttpMsg(method, urlStr string, host string, headers map[string]string, proxy string) (code int, body string, header http.Header, err error) { req, errRet := http.NewRequest(method, urlStr, nil) if errRet != nil { err = errRet @@ -177,7 +177,7 @@ func sendHttpMsg(method, urlStr string, host string, headers map[string]string, return } -func basicAuth(username, passwd string) string { +func BasicAuth(username, passwd string) string { auth := username + ":" + passwd return "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) } From d74b45be5d037b58d18fec3be05064c3ec68e404 Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 12 Jul 2018 00:31:21 +0800 Subject: [PATCH 04/20] more ci --- tests/{config => ci}/auto_test_frpc.ini | 0 .../{config => ci}/auto_test_frpc_visitor.ini | 0 tests/{config => ci}/auto_test_frps.ini | 0 tests/ci/cmd_test.go | 1 + tests/{func_test.go => ci/normal_test.go} | 8 +- tests/ci/reconnect_test.go | 1 + tests/ci/reload_test.go | 108 ++++++++++++++++++ tests/config/config.go | 10 +- tests/consts/consts.go | 4 +- 9 files changed, 123 insertions(+), 9 deletions(-) rename tests/{config => ci}/auto_test_frpc.ini (100%) rename tests/{config => ci}/auto_test_frpc_visitor.ini (100%) rename tests/{config => ci}/auto_test_frps.ini (100%) create mode 100644 tests/ci/cmd_test.go rename tests/{func_test.go => ci/normal_test.go} (97%) create mode 100644 tests/ci/reconnect_test.go create mode 100644 tests/ci/reload_test.go diff --git a/tests/config/auto_test_frpc.ini b/tests/ci/auto_test_frpc.ini similarity index 100% rename from tests/config/auto_test_frpc.ini rename to tests/ci/auto_test_frpc.ini diff --git a/tests/config/auto_test_frpc_visitor.ini b/tests/ci/auto_test_frpc_visitor.ini similarity index 100% rename from tests/config/auto_test_frpc_visitor.ini rename to tests/ci/auto_test_frpc_visitor.ini diff --git a/tests/config/auto_test_frps.ini b/tests/ci/auto_test_frps.ini similarity index 100% rename from tests/config/auto_test_frps.ini rename to tests/ci/auto_test_frps.ini diff --git a/tests/ci/cmd_test.go b/tests/ci/cmd_test.go new file mode 100644 index 00000000..006e04c2 --- /dev/null +++ b/tests/ci/cmd_test.go @@ -0,0 +1 @@ +package ci diff --git a/tests/func_test.go b/tests/ci/normal_test.go similarity index 97% rename from tests/func_test.go rename to tests/ci/normal_test.go index a83a6dc0..1ee0f58d 100644 --- a/tests/func_test.go +++ b/tests/ci/normal_test.go @@ -1,4 +1,4 @@ -package tests +package ci import ( "fmt" @@ -42,17 +42,17 @@ func init() { } func runFrps() error { - p := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./config/auto_test_frps.ini"}) + p := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./auto_test_frps.ini"}) return p.Start() } func runFrpc() error { - p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./config/auto_test_frpc.ini"}) + p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc.ini"}) return p.Start() } func runFrpcVisitor() error { - p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./config/auto_test_frpc_visitor.ini"}) + p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc_visitor.ini"}) return p.Start() } diff --git a/tests/ci/reconnect_test.go b/tests/ci/reconnect_test.go new file mode 100644 index 00000000..006e04c2 --- /dev/null +++ b/tests/ci/reconnect_test.go @@ -0,0 +1 @@ +package ci diff --git a/tests/ci/reload_test.go b/tests/ci/reload_test.go new file mode 100644 index 00000000..0a89f89d --- /dev/null +++ b/tests/ci/reload_test.go @@ -0,0 +1,108 @@ +package ci + +import ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/fatedier/frp/tests/config" + "github.com/fatedier/frp/tests/consts" + "github.com/fatedier/frp/tests/util" +) + +const FRPS_CONF = ` +[common] +server_addr = 127.0.0.1 +server_port = 10700 +log_file = console +# debug, info, warn, error +log_level = debug +token = 123456 +admin_port = 10600 +admin_user = abc +admin_pwd = abc +` + +const FRPC_CONF_1 = ` +[common] +server_addr = 127.0.0.1 +server_port = 20000 +log_file = console +# debug, info, warn, error +log_level = debug +token = 123456 +admin_port = 21000 +admin_user = abc +admin_pwd = abc + +[tcp] +type = tcp +local_port = 10701 +remote_port = 20801 + +# change remote port +[tcp2] +type = tcp +local_port = 10701 +remote_port = 20802 + +# delete +[tcp3] +type = tcp +local_port = 10701 +remote_port = 20803 +` + +const FRPC_CONF_2 = ` +[common] +server_addr = 127.0.0.1 +server_port = 20000 +log_file = console +# debug, info, warn, error +log_level = debug +token = 123456 +admin_port = 21000 +admin_user = abc +admin_pwd = abc + +[tcp] +type = tcp +local_port = 10701 +remote_port = 20801 + +[tcp2] +type = tcp +local_port = 10701 +remote_port = 20902 +` + +func TestReload(t *testing.T) { + assert := assert.New(t) + frpsCfgPath, err := config.GenerateConfigFile("./auto_test_frps.ini", FRPS_CONF) + if assert.NoError(err) { + defer os.Remove(frpsCfgPath) + } + + frpcCfgPath, err := config.GenerateConfigFile("./auto_test_frpc.ini", FRPC_CONF_1) + if assert.NoError(err) { + defer os.Remove(frpcCfgPath) + } + + frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath}) + err = frpsProcess.Start() + if assert.NoError(err) { + defer frpsProcess.Stop() + } + + time.Sleep(200 * time.Millisecond) + + frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath}) + err = frpcProcess.Start() + if assert.NoError(err) { + defer frpcProcess.Stop() + } + + // TODO +} diff --git a/tests/config/config.go b/tests/config/config.go index 80e096b8..ac094467 100644 --- a/tests/config/config.go +++ b/tests/config/config.go @@ -1,9 +1,13 @@ -package util +package config import ( "io/ioutil" + "os" + "path/filepath" ) -func GenerateConfigFile(path string, content string) error { - return ioutil.WriteFile(path, []byte(content), 0666) +func GenerateConfigFile(path string, content string) (realPath string, err error) { + realPath = filepath.Join(os.TempDir(), path) + err = ioutil.WriteFile(realPath, []byte(content), 0666) + return realPath, err } diff --git a/tests/consts/consts.go b/tests/consts/consts.go index 7e70c89b..9deae3a8 100644 --- a/tests/consts/consts.go +++ b/tests/consts/consts.go @@ -3,8 +3,8 @@ package consts import "path/filepath" var ( - FRPS_BIN_PATH = "../bin/frps" - FRPC_BIN_PATH = "../bin/frpc" + FRPS_BIN_PATH = "../../bin/frps" + FRPC_BIN_PATH = "../../bin/frpc" SERVER_ADDR = "127.0.0.1" ADMIN_ADDR = "127.0.0.1:10600" From 57417c83ae8695e43e8b0cb2f7ba88660a3eb8e5 Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 12 Jul 2018 15:23:34 +0800 Subject: [PATCH 05/20] add ci case of reload and reconnect --- tests/ci/cmd_test.go | 2 + tests/ci/normal_test.go | 35 +++++------ tests/ci/reconnect_test.go | 116 +++++++++++++++++++++++++++++++++++++ tests/ci/reload_test.go | 60 +++++++++++++++---- tests/consts/consts.go | 3 + tests/util/util.go | 22 +++++++ 6 files changed, 208 insertions(+), 30 deletions(-) diff --git a/tests/ci/cmd_test.go b/tests/ci/cmd_test.go index 006e04c2..77803975 100644 --- a/tests/ci/cmd_test.go +++ b/tests/ci/cmd_test.go @@ -1 +1,3 @@ package ci + +// TODO diff --git a/tests/ci/normal_test.go b/tests/ci/normal_test.go index 1ee0f58d..c5283460 100644 --- a/tests/ci/normal_test.go +++ b/tests/ci/normal_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "net/url" + "os" "strings" "testing" "time" @@ -20,40 +21,36 @@ import ( gnet "github.com/fatedier/golib/net" ) -func init() { +func TestMain(m *testing.M) { go mock.StartTcpEchoServer(consts.TEST_TCP_PORT) go mock.StartTcpEchoServer2(consts.TEST_TCP2_PORT) go mock.StartUdpEchoServer(consts.TEST_UDP_PORT) go mock.StartUnixDomainServer(consts.TEST_UNIX_DOMAIN_ADDR) go mock.StartHttpServer(consts.TEST_HTTP_PORT) - if err := runFrps(); err != nil { + var err error + p1 := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./auto_test_frps.ini"}) + if err = p1.Start(); err != nil { panic(err) } - time.Sleep(200 * time.Millisecond) - if err := runFrpc(); err != nil { + time.Sleep(200 * time.Millisecond) + p2 := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc.ini"}) + if err = p2.Start(); err != nil { panic(err) } - if err := runFrpcVisitor(); err != nil { + + p3 := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc_visitor.ini"}) + if err = p3.Start(); err != nil { panic(err) } time.Sleep(500 * time.Millisecond) -} -func runFrps() error { - p := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./auto_test_frps.ini"}) - return p.Start() -} - -func runFrpc() error { - p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc.ini"}) - return p.Start() -} - -func runFrpcVisitor() error { - p := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc_visitor.ini"}) - return p.Start() + exitCode := m.Run() + p1.Stop() + p2.Stop() + p3.Stop() + os.Exit(exitCode) } func TestTcp(t *testing.T) { diff --git a/tests/ci/reconnect_test.go b/tests/ci/reconnect_test.go index 006e04c2..2378a080 100644 --- a/tests/ci/reconnect_test.go +++ b/tests/ci/reconnect_test.go @@ -1 +1,117 @@ package ci + +import ( + "os" + "testing" + "time" + + "github.com/fatedier/frp/tests/config" + "github.com/fatedier/frp/tests/consts" + "github.com/fatedier/frp/tests/util" + + "github.com/stretchr/testify/assert" +) + +const FRPS_RECONNECT_CONF = ` +[common] +bind_addr = 0.0.0.0 +bind_port = 20000 +log_file = console +# debug, info, warn, error +log_level = debug +token = 123456 +` + +const FRPC_RECONNECT_CONF = ` +[common] +server_addr = 127.0.0.1 +server_port = 20000 +log_file = console +# debug, info, warn, error +log_level = debug +token = 123456 +admin_port = 21000 +admin_user = abc +admin_pwd = abc + +[tcp] +type = tcp +local_port = 10701 +remote_port = 20801 +` + +func TestReconnect(t *testing.T) { + assert := assert.New(t) + frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_RECONNECT_CONF) + if assert.NoError(err) { + defer os.Remove(frpsCfgPath) + } + + frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RECONNECT_CONF) + if assert.NoError(err) { + defer os.Remove(frpcCfgPath) + } + + frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath}) + err = frpsProcess.Start() + if assert.NoError(err) { + defer frpsProcess.Stop() + } + + time.Sleep(200 * time.Millisecond) + + frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath}) + err = frpcProcess.Start() + if assert.NoError(err) { + defer frpcProcess.Stop() + } + time.Sleep(250 * time.Millisecond) + + // test tcp + res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // stop frpc + frpcProcess.Stop() + time.Sleep(100 * time.Millisecond) + + // test tcp, expect failed + _, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.Error(err) + + // restart frpc + newFrpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath}) + err = newFrpcProcess.Start() + if assert.NoError(err) { + defer newFrpcProcess.Stop() + } + time.Sleep(250 * time.Millisecond) + + // test tcp + res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // stop frps + frpsProcess.Stop() + time.Sleep(100 * time.Millisecond) + + // test tcp, expect failed + _, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.Error(err) + + // restart frps + newFrpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath}) + err = newFrpsProcess.Start() + if assert.NoError(err) { + defer newFrpsProcess.Stop() + } + + time.Sleep(2 * time.Second) + + // test tcp + res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) +} diff --git a/tests/ci/reload_test.go b/tests/ci/reload_test.go index 0a89f89d..5ba358a3 100644 --- a/tests/ci/reload_test.go +++ b/tests/ci/reload_test.go @@ -12,20 +12,17 @@ import ( "github.com/fatedier/frp/tests/util" ) -const FRPS_CONF = ` +const FRPS_RELOAD_CONF = ` [common] -server_addr = 127.0.0.1 -server_port = 10700 +bind_addr = 0.0.0.0 +bind_port = 20000 log_file = console # debug, info, warn, error log_level = debug token = 123456 -admin_port = 10600 -admin_user = abc -admin_pwd = abc ` -const FRPC_CONF_1 = ` +const FRPC_RELOAD_CONF_1 = ` [common] server_addr = 127.0.0.1 server_port = 20000 @@ -55,7 +52,7 @@ local_port = 10701 remote_port = 20803 ` -const FRPC_CONF_2 = ` +const FRPC_RELOAD_CONF_2 = ` [common] server_addr = 127.0.0.1 server_port = 20000 @@ -80,12 +77,12 @@ remote_port = 20902 func TestReload(t *testing.T) { assert := assert.New(t) - frpsCfgPath, err := config.GenerateConfigFile("./auto_test_frps.ini", FRPS_CONF) + frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_RELOAD_CONF) if assert.NoError(err) { defer os.Remove(frpsCfgPath) } - frpcCfgPath, err := config.GenerateConfigFile("./auto_test_frpc.ini", FRPC_CONF_1) + frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RELOAD_CONF_1) if assert.NoError(err) { defer os.Remove(frpcCfgPath) } @@ -104,5 +101,46 @@ func TestReload(t *testing.T) { defer frpcProcess.Stop() } - // TODO + time.Sleep(250 * time.Millisecond) + + // test tcp1 + res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // test tcp2 + res, err = util.SendTcpMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // test tcp3 + res, err = util.SendTcpMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // reload frpc config + frpcCfgPath, err = config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RELOAD_CONF_2) + assert.NoError(err) + err = util.ReloadConf("127.0.0.1:21000", "abc", "abc") + assert.NoError(err) + + time.Sleep(time.Second) + + // test tcp1 + res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // test origin tcp2, expect failed + res, err = util.SendTcpMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) + assert.Error(err) + + // test new origin tcp2 with different port + res, err = util.SendTcpMsg("127.0.0.1:20902", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) + + // test tcp3, expect failed + res, err = util.SendTcpMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR) + assert.Error(err) } diff --git a/tests/consts/consts.go b/tests/consts/consts.go index 9deae3a8..60dcffee 100644 --- a/tests/consts/consts.go +++ b/tests/consts/consts.go @@ -6,6 +6,9 @@ var ( FRPS_BIN_PATH = "../../bin/frps" FRPC_BIN_PATH = "../../bin/frpc" + FRPS_NORMAL_CONFIG = "./auto_test_frps.ini" + FRPC_NORMAL_CONFIG = "./auto_test_frpc.ini" + SERVER_ADDR = "127.0.0.1" ADMIN_ADDR = "127.0.0.1:10600" ADMIN_USER = "abc" diff --git a/tests/util/util.go b/tests/util/util.go index ac314ec7..2070ce31 100644 --- a/tests/util/util.go +++ b/tests/util/util.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" "net" "net/http" @@ -75,6 +76,27 @@ func GetProxyStatus(statusAddr string, user string, passwd string, name string) return status, errors.New("no proxy status found") } +func ReloadConf(reloadAddr string, user string, passwd string) error { + req, err := http.NewRequest("GET", "http://"+reloadAddr+"/api/reload", nil) + if err != nil { + return err + } + + authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+passwd)) + req.Header.Add("Authorization", authStr) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } else { + if resp.StatusCode != 200 { + return fmt.Errorf("admin api status code [%d]", resp.StatusCode) + } + defer resp.Body.Close() + io.Copy(ioutil.Discard, resp.Body) + } + return nil +} + func SendTcpMsg(addr string, msg string) (res string, err error) { c, err := frpNet.ConnectTcpServer(addr) if err != nil { From cc6486addbafaed64274d898e2d1f3c9e06e124b Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 12 Jul 2018 16:49:16 +0800 Subject: [PATCH 06/20] add more cmd test --- tests/ci/cmd_test.go | 84 +++++++++++++++++++++++++++++++++++++- tests/ci/reconnect_test.go | 2 +- tests/ci/reload_test.go | 2 +- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/tests/ci/cmd_test.go b/tests/ci/cmd_test.go index 77803975..ea76e6de 100644 --- a/tests/ci/cmd_test.go +++ b/tests/ci/cmd_test.go @@ -1,3 +1,85 @@ package ci -// TODO +import ( + "testing" + "time" + + "github.com/fatedier/frp/tests/consts" + "github.com/fatedier/frp/tests/util" + + "github.com/stretchr/testify/assert" +) + +func TestCmdTcp(t *testing.T) { + assert := assert.New(t) + + var err error + s := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-t", "123", "-p", "20000"}) + err = s.Start() + if assert.NoError(err) { + defer s.Stop() + } + time.Sleep(100 * time.Millisecond) + + c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"tcp", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test", + "-l", "10701", "-r", "20801", "-n", "tcp_test"}) + err = c.Start() + if assert.NoError(err) { + defer c.Stop() + } + time.Sleep(250 * time.Millisecond) + + res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) +} + +func TestCmdUdp(t *testing.T) { + assert := assert.New(t) + + var err error + s := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-t", "123", "-p", "20000"}) + err = s.Start() + if assert.NoError(err) { + defer s.Stop() + } + time.Sleep(100 * time.Millisecond) + + c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"udp", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test", + "-l", "10702", "-r", "20802", "-n", "udp_test"}) + err = c.Start() + if assert.NoError(err) { + defer c.Stop() + } + time.Sleep(250 * time.Millisecond) + + res, err := util.SendUdpMsg("127.0.0.1:20802", consts.TEST_UDP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_UDP_ECHO_STR, res) +} + +func TestCmdHttp(t *testing.T) { + assert := assert.New(t) + + var err error + s := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-t", "123", "-p", "20000", "--vhost_http_port", "20001"}) + err = s.Start() + if assert.NoError(err) { + defer s.Stop() + } + time.Sleep(100 * time.Millisecond) + + c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"http", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test", + "-n", "udp_test", "-l", "10704", "--custom_domain", "127.0.0.1"}) + err = c.Start() + if assert.NoError(err) { + defer c.Stop() + } + time.Sleep(250 * time.Millisecond) + + code, body, _, err := util.SendHttpMsg("GET", "http://127.0.0.1:20001", "", nil, "") + if assert.NoError(err) { + assert.Equal(200, code) + assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) + } +} diff --git a/tests/ci/reconnect_test.go b/tests/ci/reconnect_test.go index 2378a080..7974c2c7 100644 --- a/tests/ci/reconnect_test.go +++ b/tests/ci/reconnect_test.go @@ -58,7 +58,7 @@ func TestReconnect(t *testing.T) { defer frpsProcess.Stop() } - time.Sleep(200 * time.Millisecond) + time.Sleep(100 * time.Millisecond) frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath}) err = frpcProcess.Start() diff --git a/tests/ci/reload_test.go b/tests/ci/reload_test.go index 5ba358a3..9811db95 100644 --- a/tests/ci/reload_test.go +++ b/tests/ci/reload_test.go @@ -93,7 +93,7 @@ func TestReload(t *testing.T) { defer frpsProcess.Stop() } - time.Sleep(200 * time.Millisecond) + time.Sleep(100 * time.Millisecond) frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath}) err = frpcProcess.Start() From 082447f5170445e0bc69865fa37d756646c17511 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 16 Jul 2018 01:21:29 +0800 Subject: [PATCH 07/20] frpc: support health check --- client/health.go | 123 +++++++++++++++++++++++++++++++++++++++-- models/config/proxy.go | 2 + 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/client/health.go b/client/health.go index ad58554d..8e84a6f8 100644 --- a/client/health.go +++ b/client/health.go @@ -15,18 +15,133 @@ package client import ( - "github.com/fatedier/frp/models/config" + "context" + "net" + "net/http" + "time" ) type HealthCheckMonitor struct { - cfg config.HealthCheckConf + checkType string + interval time.Duration + timeout time.Duration + maxFailedTimes int + + // For tcp + addr string + + // For http + url string + + failedTimes uint64 + statusOK bool + statusNormalFn func() + statusFailedFn func() + + ctx context.Context + cancel context.CancelFunc } -func NewHealthCheckMonitor(cfg *config.HealthCheckConf) *HealthCheckMonitor { +func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string, + statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor { + + if intervalS <= 0 { + intervalS = 10 + } + if timeoutS <= 0 { + timeoutS = 3 + } + if maxFailedTimes <= 0 { + maxFailedTimes = 1 + } + ctx, cancel := context.WithCancel(context.Background()) return &HealthCheckMonitor{ - cfg: *cfg, + checkType: checkType, + interval: time.Duration(intervalS) * time.Second, + timeout: time.Duration(timeoutS) * time.Second, + maxFailedTimes: maxFailedTimes, + addr: addr, + url: url, + statusOK: false, + statusNormalFn: statusNormalFn, + statusFailedFn: statusFailedFn, + ctx: ctx, + cancel: cancel, } } func (monitor *HealthCheckMonitor) Start() { + go monitor.checkWorker() +} + +func (monitor *HealthCheckMonitor) Stop() { + monitor.cancel() +} + +func (monitor *HealthCheckMonitor) checkWorker() { + for { + ctx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout)) + ok := monitor.doCheck(ctx) + + // check if this monitor has been closed + select { + case <-ctx.Done(): + cancel() + return + default: + cancel() + } + + if ok { + if !monitor.statusOK && monitor.statusNormalFn != nil { + monitor.statusOK = true + monitor.statusNormalFn() + } + } else { + monitor.failedTimes++ + if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil { + monitor.statusOK = false + monitor.statusFailedFn() + } + } + + time.Sleep(monitor.interval) + } +} + +func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) bool { + switch monitor.checkType { + case "tcp": + return monitor.doTcpCheck(ctx) + case "http": + return monitor.doHttpCheck(ctx) + default: + return false + } +} + +func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) bool { + var d net.Dialer + conn, err := d.DialContext(ctx, "tcp", monitor.addr) + if err != nil { + return false + } + conn.Close() + return true +} + +func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) bool { + req, err := http.NewRequest("GET", monitor.url, nil) + if err != nil { + return false + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false + } + + if resp.StatusCode/100 != 2 { + return false + } + return true } diff --git a/models/config/proxy.go b/models/config/proxy.go index b600be5c..9ea680a4 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -381,6 +381,8 @@ func (cfg *LocalSvrConf) checkForCli() (err error) { // Health check info type HealthCheckConf struct { HealthCheckType string `json:"health_check_type"` // tcp | http + HealthCheckTimeout int `json:"health_check_timeout"` + HealthCheckMaxFailed int `json:"health_check_max_failed"` HealthCheckIntervalS int `json:"health_check_interval_s"` HealthCheckUrl string `json:"health_check_url"` From cbeeac06a5273b3eaa10b899ffc40e22ad827f81 Mon Sep 17 00:00:00 2001 From: Yen-chi Chen Date: Thu, 9 Aug 2018 10:12:46 +0800 Subject: [PATCH 08/20] Upgrade golang to 1.10 --- Dockerfile_multiple_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile_multiple_build b/Dockerfile_multiple_build index 288fe55f..e01b21a2 100644 --- a/Dockerfile_multiple_build +++ b/Dockerfile_multiple_build @@ -1,4 +1,4 @@ -FROM golang:1.8 as frpBuild +FROM golang:1.10 as frpBuild COPY . /go/src/github.com/fatedier/frp From 698219b6218897241aef7e148e2e611f9b8c6ccf Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 16 Jul 2018 01:21:29 +0800 Subject: [PATCH 09/20] frpc: support health check --- client/control.go | 1 + client/health.go | 123 ++++++++++++++++++++++++++++++++++++++-- client/proxy_manager.go | 1 - models/config/proxy.go | 2 + 4 files changed, 122 insertions(+), 5 deletions(-) diff --git a/client/control.go b/client/control.go index 04be13ce..90d2d4ab 100644 --- a/client/control.go +++ b/client/control.go @@ -255,6 +255,7 @@ func (ctl *Control) login() (err error) { return nil } +// connectServer return a new connection to frps func (ctl *Control) connectServer() (conn frpNet.Conn, err error) { if g.GlbClientCfg.TcpMux { stream, errRet := ctl.session.OpenStream() diff --git a/client/health.go b/client/health.go index ad58554d..8e84a6f8 100644 --- a/client/health.go +++ b/client/health.go @@ -15,18 +15,133 @@ package client import ( - "github.com/fatedier/frp/models/config" + "context" + "net" + "net/http" + "time" ) type HealthCheckMonitor struct { - cfg config.HealthCheckConf + checkType string + interval time.Duration + timeout time.Duration + maxFailedTimes int + + // For tcp + addr string + + // For http + url string + + failedTimes uint64 + statusOK bool + statusNormalFn func() + statusFailedFn func() + + ctx context.Context + cancel context.CancelFunc } -func NewHealthCheckMonitor(cfg *config.HealthCheckConf) *HealthCheckMonitor { +func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string, + statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor { + + if intervalS <= 0 { + intervalS = 10 + } + if timeoutS <= 0 { + timeoutS = 3 + } + if maxFailedTimes <= 0 { + maxFailedTimes = 1 + } + ctx, cancel := context.WithCancel(context.Background()) return &HealthCheckMonitor{ - cfg: *cfg, + checkType: checkType, + interval: time.Duration(intervalS) * time.Second, + timeout: time.Duration(timeoutS) * time.Second, + maxFailedTimes: maxFailedTimes, + addr: addr, + url: url, + statusOK: false, + statusNormalFn: statusNormalFn, + statusFailedFn: statusFailedFn, + ctx: ctx, + cancel: cancel, } } func (monitor *HealthCheckMonitor) Start() { + go monitor.checkWorker() +} + +func (monitor *HealthCheckMonitor) Stop() { + monitor.cancel() +} + +func (monitor *HealthCheckMonitor) checkWorker() { + for { + ctx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout)) + ok := monitor.doCheck(ctx) + + // check if this monitor has been closed + select { + case <-ctx.Done(): + cancel() + return + default: + cancel() + } + + if ok { + if !monitor.statusOK && monitor.statusNormalFn != nil { + monitor.statusOK = true + monitor.statusNormalFn() + } + } else { + monitor.failedTimes++ + if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil { + monitor.statusOK = false + monitor.statusFailedFn() + } + } + + time.Sleep(monitor.interval) + } +} + +func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) bool { + switch monitor.checkType { + case "tcp": + return monitor.doTcpCheck(ctx) + case "http": + return monitor.doHttpCheck(ctx) + default: + return false + } +} + +func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) bool { + var d net.Dialer + conn, err := d.DialContext(ctx, "tcp", monitor.addr) + if err != nil { + return false + } + conn.Close() + return true +} + +func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) bool { + req, err := http.NewRequest("GET", monitor.url, nil) + if err != nil { + return false + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false + } + + if resp.StatusCode/100 != 2 { + return false + } + return true } diff --git a/client/proxy_manager.go b/client/proxy_manager.go index cfa56fc5..bd193bb8 100644 --- a/client/proxy_manager.go +++ b/client/proxy_manager.go @@ -18,7 +18,6 @@ const ( ProxyStatusWaitStart = "wait start" ProxyStatusRunning = "running" ProxyStatusCheckFailed = "check failed" - ProxyStatusCheckSuccess = "check success" ProxyStatusClosed = "closed" ) diff --git a/models/config/proxy.go b/models/config/proxy.go index b600be5c..9ea680a4 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -381,6 +381,8 @@ func (cfg *LocalSvrConf) checkForCli() (err error) { // Health check info type HealthCheckConf struct { HealthCheckType string `json:"health_check_type"` // tcp | http + HealthCheckTimeout int `json:"health_check_timeout"` + HealthCheckMaxFailed int `json:"health_check_max_failed"` HealthCheckIntervalS int `json:"health_check_interval_s"` HealthCheckUrl string `json:"health_check_url"` From 1a8ac148ca8ff25b72b388e563b37fbbdfa4bffc Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 18 Oct 2018 13:55:51 +0800 Subject: [PATCH 10/20] fix xtcp visitor panic --- client/proxy_manager.go | 12 ++++++------ client/visitor.go | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/client/proxy_manager.go b/client/proxy_manager.go index bd193bb8..fe175a05 100644 --- a/client/proxy_manager.go +++ b/client/proxy_manager.go @@ -13,12 +13,12 @@ import ( ) const ( - ProxyStatusNew = "new" - ProxyStatusStartErr = "start error" - ProxyStatusWaitStart = "wait start" - ProxyStatusRunning = "running" - ProxyStatusCheckFailed = "check failed" - ProxyStatusClosed = "closed" + ProxyStatusNew = "new" + ProxyStatusStartErr = "start error" + ProxyStatusWaitStart = "wait start" + ProxyStatusRunning = "running" + ProxyStatusCheckFailed = "check failed" + ProxyStatusClosed = "closed" ) type ProxyManager struct { diff --git a/client/visitor.go b/client/visitor.go index 6e1e1c8d..66344019 100644 --- a/client/visitor.go +++ b/client/visitor.go @@ -202,7 +202,16 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) { raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort)) + if err != nil { + sv.Error("resolve server UDP addr error") + return + } + visitorConn, err := net.DialUDP("udp", nil, raddr) + if err != nil { + sv.Warn("dial server udp addr error: %v", err) + return + } defer visitorConn.Close() now := time.Now().Unix() From 5c2ab5a74975bd2663ad0115074a588e7de48e2b Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Fri, 19 Oct 2018 21:01:21 +0200 Subject: [PATCH 11/20] Bump Travis versions --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ff656e79..f66096e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: go go: - 1.10.x + - 1.11.x install: - make From bae1ecdc69edbf5de887d3e715b8826cb19ad4f3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 25 Oct 2018 00:57:50 +0200 Subject: [PATCH 12/20] Grammar fixes and improved README I think this reads nicer and more accurately describes the HTTP reverse proxying. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b97b15a3..6879f480 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## What is frp? -frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. Now, it supports tcp, udp, http and https protocol when requests can be forwarded by domains to backward web services. +frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. As of now, it supports tcp & udp, as well as http and https protocols, where requests can be forwarded to internal services by domain name. ## Table of Contents From b33ea9274cdd263dd0a2ff9cf8f068606171d42a Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 6 Nov 2018 18:35:05 +0800 Subject: [PATCH 13/20] client/control: refactor code --- client/admin_api.go | 2 +- client/control.go | 213 ++++++------------------------------ client/proxy.go | 2 +- client/proxy_manager.go | 232 ++++++++++++++++++++-------------------- client/service.go | 185 ++++++++++++++++++++++++++++++-- conf/frpc_full.ini | 2 +- 6 files changed, 323 insertions(+), 313 deletions(-) diff --git a/client/admin_api.go b/client/admin_api.go index 4eafa103..50745406 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -85,7 +85,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { return } - err = svr.ctl.reloadConf(pxyCfgs, visitorCfgs) + err = svr.ctl.ReloadConf(pxyCfgs, visitorCfgs) if err != nil { res.Code = 4 res.Msg = err.Error() diff --git a/client/control.go b/client/control.go index 90d2d4ab..09ca2a02 100644 --- a/client/control.go +++ b/client/control.go @@ -17,8 +17,6 @@ package client import ( "fmt" "io" - "io/ioutil" - "runtime" "runtime/debug" "sync" "time" @@ -28,24 +26,15 @@ import ( "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/log" frpNet "github.com/fatedier/frp/utils/net" - "github.com/fatedier/frp/utils/util" - "github.com/fatedier/frp/utils/version" "github.com/fatedier/golib/control/shutdown" "github.com/fatedier/golib/crypto" fmux "github.com/hashicorp/yamux" ) -const ( - connReadTimeout time.Duration = 10 * time.Second -) - type Control struct { - // frpc service - svr *Service - - // login message to server, only used - loginMsg *msg.Login + // uniq id got from frps, attach it in loginMsg + runId string // manage all proxies pm *ProxyManager @@ -65,14 +54,10 @@ type Control struct { // read from this channel to get the next message sent by server readCh chan (msg.Message) - // run id got from server - runId string - - // if we call close() in control, do not reconnect to server - exit bool - // goroutines can block by reading from this channel, it will be closed only in reader() when control connection is closed - closedCh chan int + closedCh chan struct{} + + closedDoneCh chan struct{} // last time got the Pong message lastPong time.Time @@ -86,50 +71,28 @@ type Control struct { log.Logger } -func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) *Control { - loginMsg := &msg.Login{ - Arch: runtime.GOARCH, - Os: runtime.GOOS, - PoolCount: g.GlbClientCfg.PoolCount, - User: g.GlbClientCfg.User, - Version: version.Full(), - } +func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) *Control { ctl := &Control{ - svr: svr, - loginMsg: loginMsg, + runId: runId, + conn: conn, + session: session, sendCh: make(chan msg.Message, 100), readCh: make(chan msg.Message, 100), - closedCh: make(chan int), + closedCh: make(chan struct{}), + closedDoneCh: make(chan struct{}), readerShutdown: shutdown.New(), writerShutdown: shutdown.New(), msgHandlerShutdown: shutdown.New(), Logger: log.NewPrefixLogger(""), } - ctl.pm = NewProxyManager(ctl, ctl.sendCh, "") + ctl.pm = NewProxyManager(ctl.sendCh, "") ctl.pm.Reload(pxyCfgs, false) ctl.vm = NewVisitorManager(ctl) ctl.vm.Reload(visitorCfgs) return ctl } -func (ctl *Control) Run() (err error) { - for { - err = ctl.login() - if err != nil { - ctl.Warn("login to server failed: %v", err) - - // if login_fail_exit is true, just exit this program - // otherwise sleep a while and continues relogin to server - if g.GlbClientCfg.LoginFailExit { - return - } else { - time.Sleep(10 * time.Second) - } - } else { - break - } - } - +func (ctl *Control) Run() { go ctl.worker() // start all local visitors and send NewProxy message for all configured proxies @@ -137,7 +100,7 @@ func (ctl *Control) Run() (err error) { ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew}) go ctl.vm.Run() - return nil + return } func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) { @@ -179,80 +142,13 @@ func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) { } func (ctl *Control) Close() error { - ctl.mu.Lock() - defer ctl.mu.Unlock() - ctl.exit = true ctl.pm.CloseProxies() return nil } -// login send a login message to server and wait for a loginResp message. -func (ctl *Control) login() (err error) { - if ctl.conn != nil { - ctl.conn.Close() - } - if ctl.session != nil { - ctl.session.Close() - } - - conn, err := frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, - fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort)) - if err != nil { - return err - } - - defer func() { - if err != nil { - conn.Close() - } - }() - - if g.GlbClientCfg.TcpMux { - fmuxCfg := fmux.DefaultConfig() - fmuxCfg.LogOutput = ioutil.Discard - session, errRet := fmux.Client(conn, fmuxCfg) - if errRet != nil { - return errRet - } - stream, errRet := session.OpenStream() - if errRet != nil { - session.Close() - return errRet - } - conn = frpNet.WrapConn(stream) - ctl.session = session - } - - now := time.Now().Unix() - ctl.loginMsg.PrivilegeKey = util.GetAuthKey(g.GlbClientCfg.Token, now) - ctl.loginMsg.Timestamp = now - ctl.loginMsg.RunId = ctl.runId - - if err = msg.WriteMsg(conn, ctl.loginMsg); err != nil { - return err - } - - var loginRespMsg msg.LoginResp - conn.SetReadDeadline(time.Now().Add(connReadTimeout)) - if err = msg.ReadMsgInto(conn, &loginRespMsg); err != nil { - return err - } - conn.SetReadDeadline(time.Time{}) - - if loginRespMsg.Error != "" { - err = fmt.Errorf("%s", loginRespMsg.Error) - ctl.Error("%s", loginRespMsg.Error) - return err - } - - ctl.conn = conn - // update runId got from server - ctl.runId = loginRespMsg.RunId - g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort - ctl.ClearLogPrefix() - ctl.AddLogPrefix(loginRespMsg.RunId) - ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort) - return nil +// ClosedDoneCh returns a channel which will be closed after all resources are released +func (ctl *Control) ClosedDoneCh() <-chan struct{} { + return ctl.closedDoneCh } // connectServer return a new connection to frps @@ -373,87 +269,38 @@ func (ctl *Control) msgHandler() { } } -// controler keep watching closedCh, start a new connection if previous control connection is closed. -// If controler is notified by closedCh, reader and writer and handler will exit, then recall these functions. +// If controler is notified by closedCh, reader and writer and handler will exit func (ctl *Control) worker() { go ctl.msgHandler() go ctl.reader() go ctl.writer() - var err error - maxDelayTime := 20 * time.Second - delayTime := time.Second - checkInterval := 60 * time.Second checkProxyTicker := time.NewTicker(checkInterval) + for { select { case <-checkProxyTicker.C: // check which proxy registered failed and reregister it to server ctl.pm.CheckAndStartProxy([]string{ProxyStatusStartErr, ProxyStatusClosed}) - case _, ok := <-ctl.closedCh: - // we won't get any variable from this channel - if !ok { - // close related channels and wait until other goroutines done - close(ctl.readCh) - ctl.readerShutdown.WaitDone() - ctl.msgHandlerShutdown.WaitDone() + case <-ctl.closedCh: + // close related channels and wait until other goroutines done + close(ctl.readCh) + ctl.readerShutdown.WaitDone() + ctl.msgHandlerShutdown.WaitDone() - close(ctl.sendCh) - ctl.writerShutdown.WaitDone() + close(ctl.sendCh) + ctl.writerShutdown.WaitDone() - ctl.pm.CloseProxies() - // if ctl.exit is true, just exit - ctl.mu.RLock() - exit := ctl.exit - ctl.mu.RUnlock() - if exit { - return - } + ctl.pm.CloseProxies() - // loop util reconnecting to server success - for { - ctl.Info("try to reconnect to server...") - err = ctl.login() - if err != nil { - ctl.Warn("reconnect to server error: %v", err) - time.Sleep(delayTime) - delayTime = delayTime * 2 - if delayTime > maxDelayTime { - delayTime = maxDelayTime - } - continue - } - // reconnect success, init delayTime - delayTime = time.Second - break - } - - // init related channels and variables - ctl.sendCh = make(chan msg.Message, 100) - ctl.readCh = make(chan msg.Message, 100) - ctl.closedCh = make(chan int) - ctl.readerShutdown = shutdown.New() - ctl.writerShutdown = shutdown.New() - ctl.msgHandlerShutdown = shutdown.New() - ctl.pm.Reset(ctl.sendCh, ctl.runId) - - // previous work goroutines should be closed and start them here - go ctl.msgHandler() - go ctl.writer() - go ctl.reader() - - // start all configured proxies - ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew, ProxyStatusClosed}) - - checkProxyTicker.Stop() - checkProxyTicker = time.NewTicker(checkInterval) - } + close(ctl.closedDoneCh) + return } } } -func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error { +func (ctl *Control) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error { ctl.vm.Reload(visitorCfgs) err := ctl.pm.Reload(pxyCfgs, true) return err diff --git a/client/proxy.go b/client/proxy.go index 26c9a66e..a89921d2 100644 --- a/client/proxy.go +++ b/client/proxy.go @@ -35,7 +35,7 @@ import ( "github.com/fatedier/golib/pool" ) -// Proxy defines how to deal with work connections for different proxy type. +// Proxy defines how to handle work connections for different proxy type. type Proxy interface { Run() error diff --git a/client/proxy_manager.go b/client/proxy_manager.go index fe175a05..dc9f350d 100644 --- a/client/proxy_manager.go +++ b/client/proxy_manager.go @@ -22,7 +22,6 @@ const ( ) type ProxyManager struct { - ctl *Control sendCh chan (msg.Message) proxies map[string]*ProxyWrapper closed bool @@ -31,122 +30,8 @@ type ProxyManager struct { log.Logger } -type ProxyWrapper struct { - Name string - Type string - Status string - Err string - Cfg config.ProxyConf - - RemoteAddr string - - pxy Proxy - - mu sync.RWMutex -} - -type ProxyStatus struct { - Name string `json:"name"` - Type string `json:"type"` - Status string `json:"status"` - Err string `json:"err"` - Cfg config.ProxyConf `json:"cfg"` - - // Got from server. - RemoteAddr string `json:"remote_addr"` -} - -func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper { - return &ProxyWrapper{ - Name: cfg.GetBaseInfo().ProxyName, - Type: cfg.GetBaseInfo().ProxyType, - Status: ProxyStatusNew, - Cfg: cfg, - pxy: nil, - } -} - -func (pw *ProxyWrapper) GetStatusStr() string { - pw.mu.RLock() - defer pw.mu.RUnlock() - return pw.Status -} - -func (pw *ProxyWrapper) GetStatus() *ProxyStatus { - pw.mu.RLock() - defer pw.mu.RUnlock() - ps := &ProxyStatus{ - Name: pw.Name, - Type: pw.Type, - Status: pw.Status, - Err: pw.Err, - Cfg: pw.Cfg, - RemoteAddr: pw.RemoteAddr, - } - return ps -} - -func (pw *ProxyWrapper) WaitStart() { - pw.mu.Lock() - defer pw.mu.Unlock() - pw.Status = ProxyStatusWaitStart -} - -func (pw *ProxyWrapper) Start(remoteAddr string, serverRespErr string) error { - if pw.pxy != nil { - pw.pxy.Close() - pw.pxy = nil - } - - if serverRespErr != "" { - pw.mu.Lock() - pw.Status = ProxyStatusStartErr - pw.RemoteAddr = remoteAddr - pw.Err = serverRespErr - pw.mu.Unlock() - return fmt.Errorf(serverRespErr) - } - - pxy := NewProxy(pw.Cfg) - pw.mu.Lock() - defer pw.mu.Unlock() - pw.RemoteAddr = remoteAddr - if err := pxy.Run(); err != nil { - pw.Status = ProxyStatusStartErr - pw.Err = err.Error() - return err - } - pw.Status = ProxyStatusRunning - pw.Err = "" - pw.pxy = pxy - return nil -} - -func (pw *ProxyWrapper) InWorkConn(workConn frpNet.Conn) { - pw.mu.RLock() - pxy := pw.pxy - pw.mu.RUnlock() - if pxy != nil { - workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String()) - go pxy.InWorkConn(workConn) - } else { - workConn.Close() - } -} - -func (pw *ProxyWrapper) Close() { - pw.mu.Lock() - defer pw.mu.Unlock() - if pw.pxy != nil { - pw.pxy.Close() - pw.pxy = nil - } - pw.Status = ProxyStatusClosed -} - -func NewProxyManager(ctl *Control, msgSendCh chan (msg.Message), logPrefix string) *ProxyManager { +func NewProxyManager(msgSendCh chan (msg.Message), logPrefix string) *ProxyManager { return &ProxyManager{ - ctl: ctl, proxies: make(map[string]*ProxyWrapper), sendCh: msgSendCh, closed: false, @@ -309,3 +194,118 @@ func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus { } return ps } + +type ProxyStatus struct { + Name string `json:"name"` + Type string `json:"type"` + Status string `json:"status"` + Err string `json:"err"` + Cfg config.ProxyConf `json:"cfg"` + + // Got from server. + RemoteAddr string `json:"remote_addr"` +} + +// ProxyWrapper is a wrapper of Proxy interface only used in ProxyManager +// Add additional proxy status info +type ProxyWrapper struct { + Name string + Type string + Status string + Err string + Cfg config.ProxyConf + + RemoteAddr string + + pxy Proxy + + mu sync.RWMutex +} + +func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper { + return &ProxyWrapper{ + Name: cfg.GetBaseInfo().ProxyName, + Type: cfg.GetBaseInfo().ProxyType, + Status: ProxyStatusNew, + Cfg: cfg, + pxy: nil, + } +} + +func (pw *ProxyWrapper) GetStatusStr() string { + pw.mu.RLock() + defer pw.mu.RUnlock() + return pw.Status +} + +func (pw *ProxyWrapper) GetStatus() *ProxyStatus { + pw.mu.RLock() + defer pw.mu.RUnlock() + ps := &ProxyStatus{ + Name: pw.Name, + Type: pw.Type, + Status: pw.Status, + Err: pw.Err, + Cfg: pw.Cfg, + RemoteAddr: pw.RemoteAddr, + } + return ps +} + +func (pw *ProxyWrapper) WaitStart() { + pw.mu.Lock() + defer pw.mu.Unlock() + pw.Status = ProxyStatusWaitStart +} + +func (pw *ProxyWrapper) Start(remoteAddr string, serverRespErr string) error { + if pw.pxy != nil { + pw.pxy.Close() + pw.pxy = nil + } + + if serverRespErr != "" { + pw.mu.Lock() + pw.Status = ProxyStatusStartErr + pw.RemoteAddr = remoteAddr + pw.Err = serverRespErr + pw.mu.Unlock() + return fmt.Errorf(serverRespErr) + } + + pxy := NewProxy(pw.Cfg) + pw.mu.Lock() + defer pw.mu.Unlock() + pw.RemoteAddr = remoteAddr + if err := pxy.Run(); err != nil { + pw.Status = ProxyStatusStartErr + pw.Err = err.Error() + return err + } + pw.Status = ProxyStatusRunning + pw.Err = "" + pw.pxy = pxy + return nil +} + +func (pw *ProxyWrapper) InWorkConn(workConn frpNet.Conn) { + pw.mu.RLock() + pxy := pw.pxy + pw.mu.RUnlock() + if pxy != nil { + workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String()) + go pxy.InWorkConn(workConn) + } else { + workConn.Close() + } +} + +func (pw *ProxyWrapper) Close() { + pw.mu.Lock() + defer pw.mu.Unlock() + if pw.pxy != nil { + pw.pxy.Close() + pw.pxy = nil + } + pw.Status = ProxyStatusClosed +} diff --git a/client/service.go b/client/service.go index 2589f520..62cf1518 100644 --- a/client/service.go +++ b/client/service.go @@ -15,35 +15,85 @@ package client import ( + "fmt" + "io/ioutil" + "runtime" + "sync" + "sync/atomic" + "time" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/log" + frpNet "github.com/fatedier/frp/utils/net" + "github.com/fatedier/frp/utils/util" + "github.com/fatedier/frp/utils/version" + + fmux "github.com/hashicorp/yamux" ) type Service struct { - // manager control connection with server - ctl *Control + // uniq id got from frps, attach it in loginMsg + runId string + // manager control connection with server + ctl *Control + ctlMu sync.RWMutex + + pxyCfgs map[string]config.ProxyConf + visitorCfgs map[string]config.VisitorConf + cfgMu sync.RWMutex + + exit uint32 // 0 means not exit closedCh chan int } func NewService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (svr *Service) { svr = &Service{ - closedCh: make(chan int), + pxyCfgs: pxyCfgs, + visitorCfgs: visitorCfgs, + exit: 0, + closedCh: make(chan int), } - ctl := NewControl(svr, pxyCfgs, visitorCfgs) - svr.ctl = ctl return } +func (svr *Service) GetController() *Control { + svr.ctlMu.RLock() + defer svr.ctlMu.RUnlock() + return svr.ctl +} + func (svr *Service) Run() error { - err := svr.ctl.Run() - if err != nil { - return err + // first login + for { + conn, session, err := svr.login() + if err != nil { + log.Warn("login to server failed: %v", err) + + // if login_fail_exit is true, just exit this program + // otherwise sleep a while and try again to connect to server + if g.GlbClientCfg.LoginFailExit { + return err + } else { + time.Sleep(10 * time.Second) + } + } else { + // login success + ctl := NewControl(svr.runId, conn, session, svr.pxyCfgs, svr.visitorCfgs) + ctl.Run() + svr.ctlMu.Lock() + svr.ctl = ctl + svr.ctlMu.Unlock() + break + } } + go svr.keepControllerWorking() + if g.GlbClientCfg.AdminPort != 0 { - err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort) + err := svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort) if err != nil { log.Warn("run admin server error: %v", err) } @@ -54,6 +104,119 @@ func (svr *Service) Run() error { return nil } -func (svr *Service) Close() { - svr.ctl.Close() +func (svr *Service) keepControllerWorking() { + maxDelayTime := 20 * time.Second + delayTime := time.Second + + for { + <-svr.ctl.ClosedDoneCh() + if atomic.LoadUint32(&svr.exit) != 0 { + return + } + + for { + log.Info("try to reconnect to server...") + conn, session, err := svr.login() + if err != nil { + log.Warn("reconnect to server error: %v", err) + time.Sleep(delayTime) + delayTime = delayTime * 2 + if delayTime > maxDelayTime { + delayTime = maxDelayTime + } + continue + } + // reconnect success, init delayTime + delayTime = time.Second + + ctl := NewControl(svr.runId, conn, session, svr.pxyCfgs, svr.visitorCfgs) + ctl.Run() + svr.ctlMu.Lock() + svr.ctl = ctl + svr.ctlMu.Unlock() + break + } + } +} + +// login creates a connection to frps and registers it self as a client +// conn: control connection +// session: if it's not nil, using tcp mux +func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error) { + conn, err = frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, + fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort)) + if err != nil { + return + } + + defer func() { + if err != nil { + conn.Close() + } + }() + + if g.GlbClientCfg.TcpMux { + fmuxCfg := fmux.DefaultConfig() + fmuxCfg.LogOutput = ioutil.Discard + session, err = fmux.Client(conn, fmuxCfg) + if err != nil { + return + } + stream, errRet := session.OpenStream() + if errRet != nil { + session.Close() + err = errRet + return + } + conn = frpNet.WrapConn(stream) + } + + now := time.Now().Unix() + loginMsg := &msg.Login{ + Arch: runtime.GOARCH, + Os: runtime.GOOS, + PoolCount: g.GlbClientCfg.PoolCount, + User: g.GlbClientCfg.User, + Version: version.Full(), + PrivilegeKey: util.GetAuthKey(g.GlbClientCfg.Token, now), + Timestamp: now, + RunId: svr.runId, + } + + if err = msg.WriteMsg(conn, loginMsg); err != nil { + return + } + + var loginRespMsg msg.LoginResp + conn.SetReadDeadline(time.Now().Add(10 * time.Second)) + if err = msg.ReadMsgInto(conn, &loginRespMsg); err != nil { + return + } + conn.SetReadDeadline(time.Time{}) + + if loginRespMsg.Error != "" { + err = fmt.Errorf("%s", loginRespMsg.Error) + log.Error("%s", loginRespMsg.Error) + return + } + + svr.runId = loginRespMsg.RunId + g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort + log.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort) + return +} + +func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error { + svr.cfgMu.Lock() + svr.pxyCfgs = pxyCfgs + svr.visitorCfgs = visitorCfgs + svr.cfgMu.Unlock() + + return svr.ctl.ReloadConf(pxyCfgs, visitorCfgs) +} + +func (svr *Service) Close() { + atomic.StoreUint32(&svr.exit, 1) + svr.ctl.Close() + close(svr.closedCh) } diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 2307eeb3..d9892f54 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -25,7 +25,7 @@ token = 12345678 admin_addr = 127.0.0.1 admin_port = 7400 admin_user = admin -admin_passwd = admin +admin_pwd = admin # connections will be established in advance, default value is zero pool_count = 5 From a1ac0026940ca5d6147a758593de69853a0b6913 Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 7 Nov 2018 21:10:47 +0800 Subject: [PATCH 14/20] update golib, fix #959 --- Gopkg.lock | 83 +- Gopkg.toml | 2 +- vendor/github.com/fatedier/golib/net/proxy.go | 1 + .../spf13/cobra/cobra/cmd/license_agpl.go | 683 ----------------- .../spf13/cobra/cobra/cmd/license_apache_2.go | 238 ------ .../cobra/cobra/cmd/license_bsd_clause_2.go | 71 -- .../cobra/cobra/cmd/license_bsd_clause_3.go | 78 -- .../spf13/cobra/cobra/cmd/license_gpl_2.go | 376 --------- .../spf13/cobra/cobra/cmd/license_gpl_3.go | 711 ------------------ .../spf13/cobra/cobra/cmd/license_lgpl.go | 186 ----- .../spf13/cobra/cobra/cmd/license_mit.go | 63 -- .../spf13/cobra/cobra/cmd/licenses.go | 118 --- 12 files changed, 79 insertions(+), 2531 deletions(-) delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/licenses.go diff --git a/Gopkg.lock b/Gopkg.lock index acff9733..18187aaf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,23 +2,29 @@ [[projects]] + digest = "1:5a91fc342af1f94bce8b760a80d5b709fe53ea10c870a5daf1dc7e9fada8525f" name = "github.com/armon/go-socks5" packages = ["."] + pruneopts = "UT" revision = "e75332964ef517daa070d7c38a9466a0d687e0a5" [[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "UT" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:0f8ca5fa815e8058bfbf5d0e4ad0c2f8334d68cac86e3bfee94b4e3031e9f69f" name = "github.com/fatedier/beego" packages = ["logs"] + pruneopts = "UT" revision = "6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8" [[projects]] - branch = "master" + digest = "1:edb90bd03be19aa95d375ed6eb5d681538e0a3f7d2a057b69bc2ca6e5217477a" name = "github.com/fatedier/golib" packages = [ "control/shutdown", @@ -28,122 +34,162 @@ "msg/json", "net", "net/mux", - "pool" + "pool", ] - revision = "280fa74053dee5311c46094f4bdefbf76d3fcbe2" + pruneopts = "UT" + revision = "ff8cd814b04901d617b7fffaca6fedb81067821d" [[projects]] branch = "frp" + digest = "1:6621826f49b587c0d6f868e1c56d2bbbc1d75597347d97419b3b027e8a753bdb" name = "github.com/fatedier/kcp-go" packages = ["."] + pruneopts = "UT" revision = "cd167d2f15f451b0f33780ce862fca97adc0331e" [[projects]] + digest = "1:29a5ab9fa9e845fd8e8726f31b187d710afd271ef1eb32085fe3d604b7e06382" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "UT" revision = "553a641470496b2327abcac10b36396bd98e45c9" [[projects]] + digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "UT" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "UT" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] + digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" name = "github.com/gorilla/websocket" packages = ["."] + pruneopts = "UT" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] + digest = "1:6074024c54115955afc83ee5064367523bbc55e4eb0e9cf145e43c9c0371918c" name = "github.com/hashicorp/yamux" packages = ["."] + pruneopts = "UT" revision = "2658be15c5f05e76244154714161f17e3e77de2e" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "UT" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "UT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "UT" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:bc91590d3e20673d5e33267fc140e7dadddde0b84f2e9030547ba86859d2d13e" name = "github.com/rakyll/statik" packages = ["fs"] + pruneopts = "UT" revision = "fd36b3595eb2ec8da4b8153b107f7ea08504899d" version = "v0.1.1" [[projects]] + digest = "1:4c01929c0b1665523b469482fc8241a04519bd5bfc97a1c113367cfadebab07b" name = "github.com/rodaine/table" packages = ["."] + pruneopts = "UT" revision = "212a2ad1c462ed4d5b5511ea2b480a573281dbbd" version = "v1.0.0" [[projects]] + digest = "1:645cabccbb4fa8aab25a956cbcbdf6a6845ca736b2c64e197ca7cbb9d210b939" name = "github.com/spf13/cobra" packages = ["."] + pruneopts = "UT" revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" version = "v0.0.3" [[projects]] + digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "UT" revision = "583c0c0531f06d5278b7d917446061adc344b5cd" version = "v1.0.1" [[projects]] + digest = "1:f85e109eda8f6080877185d1c39e98dd8795e1780c08beca28304b87fd855a1c" name = "github.com/stretchr/testify" packages = ["assert"] + pruneopts = "UT" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" [[projects]] branch = "master" + digest = "1:710ccf83337a9ca27abe968c3e58fdf16bd69d76b9870dadafc511e94fc33d7f" name = "github.com/templexxx/cpufeat" packages = ["."] + pruneopts = "UT" revision = "3794dfbfb04749f896b521032f69383f24c3687e" [[projects]] + digest = "1:7bf0e709c5dd92c937e6f59a76056fe0a89cfe2f52ce25493c6337d23781af0a" name = "github.com/templexxx/reedsolomon" packages = ["."] + pruneopts = "UT" revision = "5e06b81a1c7628d9c8d4fb7c3c4e401e37db39b4" version = "0.1.1" [[projects]] + digest = "1:a0a269bea865974fc4d583373c984a5aa60cf98b5aa4f3e1b5de527891d37845" name = "github.com/templexxx/xor" packages = ["."] + pruneopts = "UT" revision = "0af8e873c554da75f37f2049cdffda804533d44c" version = "0.1.2" [[projects]] + digest = "1:97293f3bd0b9f81484da18dba66a20de340307b43835a91157aaaee484c80e9b" name = "github.com/tjfoc/gmsm" packages = ["sm4"] + pruneopts = "UT" revision = "98aa888b79d8de04afe0fccf45ed10594efc858b" version = "v1.1" [[projects]] + digest = "1:8f70510b21fd07eba5bd4e0f84d49d932ea74c8b0ea20a5807e9492cc819928c" name = "github.com/vaughan0/go-ini" packages = ["."] + pruneopts = "UT" revision = "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1" [[projects]] + digest = "1:a14b2b0fb9cc2d9ed073aac9834ff93ded673b94fedee4eead3cd9a65e80a40b" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -153,12 +199,14 @@ "salsa20/salsa", "tea", "twofish", - "xtea" + "xtea", ] + pruneopts = "UT" revision = "4ec37c66abab2c7e02ae775328b2ff001c3f025a" [[projects]] branch = "master" + digest = "1:4781de952463c8e97ab707c03c73f5f53296be672d1bceac9323393a7b6e7e0a" name = "golang.org/x/net" packages = [ "bpf", @@ -168,13 +216,36 @@ "internal/socks", "ipv4", "proxy", - "websocket" + "websocket", ] + pruneopts = "UT" revision = "dfa909b99c79129e1100513e5cd36307665e5723" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "58a51c884de4111bfa3d2f82a7cfe1cb91e9e8786fafdfdc8d306bed24f23e44" + input-imports = [ + "github.com/armon/go-socks5", + "github.com/fatedier/beego/logs", + "github.com/fatedier/golib/control/shutdown", + "github.com/fatedier/golib/crypto", + "github.com/fatedier/golib/errors", + "github.com/fatedier/golib/io", + "github.com/fatedier/golib/msg/json", + "github.com/fatedier/golib/net", + "github.com/fatedier/golib/net/mux", + "github.com/fatedier/golib/pool", + "github.com/fatedier/kcp-go", + "github.com/gorilla/mux", + "github.com/gorilla/websocket", + "github.com/hashicorp/yamux", + "github.com/rakyll/statik/fs", + "github.com/rodaine/table", + "github.com/spf13/cobra", + "github.com/stretchr/testify/assert", + "github.com/vaughan0/go-ini", + "golang.org/x/net/ipv4", + "golang.org/x/net/websocket", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index fb6da235..45dd8893 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -35,7 +35,7 @@ [[constraint]] name = "github.com/fatedier/golib" - branch = "master" + revision = "ff8cd814b04901d617b7fffaca6fedb81067821d" [[constraint]] branch = "frp" diff --git a/vendor/github.com/fatedier/golib/net/proxy.go b/vendor/github.com/fatedier/golib/net/proxy.go index 9a48e9a0..e4b01093 100644 --- a/vendor/github.com/fatedier/golib/net/proxy.go +++ b/vendor/github.com/fatedier/golib/net/proxy.go @@ -43,6 +43,7 @@ func DialTcpByProxy(proxyStr string, addr string) (c net.Conn, err error) { auth := &ProxyAuth{} if proxyUrl.User != nil { + auth.Enable = true auth.Username = proxyUrl.User.Username() auth.Passwd, _ = proxyUrl.User.Password() } diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go deleted file mode 100644 index bc22e973..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go +++ /dev/null @@ -1,683 +0,0 @@ -package cmd - -func initAgpl() { - Licenses["agpl"] = License{ - Name: "GNU Affero General Public License", - PossibleMatches: []string{"agpl", "affero gpl", "gnu agpl"}, - Header: ` -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see .`, - Text: ` GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go deleted file mode 100644 index 38393d54..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -func initApache2() { - Licenses["apache"] = License{ - Name: "Apache 2.0", - PossibleMatches: []string{"apache", "apache20", "apache 2.0", "apache2.0", "apache-2.0"}, - Header: ` -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.`, - Text: ` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go deleted file mode 100644 index 4a847e04..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -func initBsdClause2() { - Licenses["freebsd"] = License{ - Name: "Simplified BSD License", - PossibleMatches: []string{"freebsd", "simpbsd", "simple bsd", "2-clause bsd", - "2 clause bsd", "simplified bsd license"}, - Header: `All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE.`, - Text: `{{ .copyright }} -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go deleted file mode 100644 index c7476b31..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -func initBsdClause3() { - Licenses["bsd"] = License{ - Name: "NewBSD", - PossibleMatches: []string{"bsd", "newbsd", "3 clause bsd", "3-clause bsd"}, - Header: `All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE.`, - Text: `{{ .copyright }} -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go deleted file mode 100644 index 03e05b3a..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -func initGpl2() { - Licenses["gpl2"] = License{ - Name: "GNU General Public License 2.0", - PossibleMatches: []string{"gpl2", "gnu gpl2", "gplv2"}, - Header: ` -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see .`, - Text: ` GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type 'show c' for details. - -The hypothetical commands 'show w' and 'show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than 'show w' and 'show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - 'Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go deleted file mode 100644 index ce07679c..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go +++ /dev/null @@ -1,711 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -func initGpl3() { - Licenses["gpl3"] = License{ - Name: "GNU General Public License 3.0", - PossibleMatches: []string{"gpl3", "gplv3", "gpl", "gnu gpl3", "gnu gpl"}, - Header: ` -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see .`, - Text: ` GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type 'show c' for details. - -The hypothetical commands 'show w' and 'show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go deleted file mode 100644 index 0f8b96ca..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go +++ /dev/null @@ -1,186 +0,0 @@ -package cmd - -func initLgpl() { - Licenses["lgpl"] = License{ - Name: "GNU Lesser General Public License", - PossibleMatches: []string{"lgpl", "lesser gpl", "gnu lgpl"}, - Header: ` -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see .`, - Text: ` GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library.`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go deleted file mode 100644 index bd2d0c4f..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -func initMit() { - Licenses["mit"] = License{ - Name: "MIT License", - PossibleMatches: []string{"mit"}, - Header: ` -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE.`, - Text: `The MIT License (MIT) - -{{ .copyright }} - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -`, - } -} diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go b/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go deleted file mode 100644 index a070134d..00000000 --- a/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// 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. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -import ( - "strings" - "time" - - "github.com/spf13/viper" -) - -// Licenses contains all possible licenses a user can choose from. -var Licenses = make(map[string]License) - -// License represents a software license agreement, containing the Name of -// the license, its possible matches (on the command line as given to cobra), -// the header to be used with each file on the file's creating, and the text -// of the license -type License struct { - Name string // The type of license in use - PossibleMatches []string // Similar names to guess - Text string // License text data - Header string // License header for source files -} - -func init() { - // Allows a user to not use a license. - Licenses["none"] = License{"None", []string{"none", "false"}, "", ""} - - initApache2() - initMit() - initBsdClause3() - initBsdClause2() - initGpl2() - initGpl3() - initLgpl() - initAgpl() -} - -// getLicense returns license specified by user in flag or in config. -// If user didn't specify the license, it returns Apache License 2.0. -// -// TODO: Inspect project for existing license -func getLicense() License { - // If explicitly flagged, use that. - if userLicense != "" { - return findLicense(userLicense) - } - - // If user wants to have custom license, use that. - if viper.IsSet("license.header") || viper.IsSet("license.text") { - return License{Header: viper.GetString("license.header"), - Text: viper.GetString("license.text")} - } - - // If user wants to have built-in license, use that. - if viper.IsSet("license") { - return findLicense(viper.GetString("license")) - } - - // If user didn't set any license, use Apache 2.0 by default. - return Licenses["apache"] -} - -func copyrightLine() string { - author := viper.GetString("author") - - year := viper.GetString("year") // For tests. - if year == "" { - year = time.Now().Format("2006") - } - - return "Copyright © " + year + " " + author -} - -// findLicense looks for License object of built-in licenses. -// If it didn't find license, then the app will be terminated and -// error will be printed. -func findLicense(name string) License { - found := matchLicense(name) - if found == "" { - er("unknown license: " + name) - } - return Licenses[found] -} - -// matchLicense compares the given a license name -// to PossibleMatches of all built-in licenses. -// It returns blank string, if name is blank string or it didn't find -// then appropriate match to name. -func matchLicense(name string) string { - if name == "" { - return "" - } - - for key, lic := range Licenses { - for _, match := range lic.PossibleMatches { - if strings.EqualFold(name, match) { - return key - } - } - } - - return "" -} From 826b9db5f25168bc0bc466fecc05467bc2591b19 Mon Sep 17 00:00:00 2001 From: Adrian Rangel Date: Thu, 15 Nov 2018 22:53:35 -0600 Subject: [PATCH 15/20] fix setting basic auth password doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b97b15a3..e7740f46 100644 --- a/README.md +++ b/README.md @@ -566,7 +566,7 @@ type = http local_port = 80 custom_domains = test.yourdomain.com http_user = abc -http_passwd = abc +http_pwd = abc ``` Visit `http://test.yourdomain.com` and now you need to input username and password. From 5165b0821f507f0accb15dbdc478f2fc290d5a03 Mon Sep 17 00:00:00 2001 From: SpanishBigHam <10262931+bighamx@users.noreply.github.com> Date: Wed, 21 Nov 2018 14:51:33 +0800 Subject: [PATCH 16/20] Update README_zh.md fix typo --- README_zh.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README_zh.md b/README_zh.md index 3a9e67ba..c457b7aa 100644 --- a/README_zh.md +++ b/README_zh.md @@ -518,7 +518,7 @@ tcp_mux = false 目前只支持 tcp 类型的 proxy。 ```ini -# fprc.ini +# frpc.ini [test1] type = tcp local_port = 8080 @@ -619,7 +619,7 @@ local_port = 80 subdomain = test ``` -frps 和 fprc 都启动成功后,通过 `test.frps.com` 就可以访问到内网的 web 服务。 +frps 和 frpc 都启动成功后,通过 `test.frps.com` 就可以访问到内网的 web 服务。 需要注意的是如果 frps 配置了 `subdomain_host`,则 `custom_domains` 中不能是属于 `subdomain_host` 的子域名或者泛域名。 From 08c17c3247b7d0c73b06b98d6b5d442034617ec2 Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 7 Dec 2018 17:05:36 +0800 Subject: [PATCH 17/20] frpc: support health check --- client/control.go | 51 +++---- client/event.go | 28 ++++ client/health.go | 55 +++++-- client/proxy_manager.go | 294 ++++++++------------------------------ client/proxy_wrapper.go | 219 ++++++++++++++++++++++++++++ client/visitor_manager.go | 16 ++- models/config/proxy.go | 27 +++- server/group/group.go | 1 + server/group/tcp.go | 6 +- 9 files changed, 414 insertions(+), 283 deletions(-) create mode 100644 client/event.go create mode 100644 client/proxy_wrapper.go diff --git a/client/control.go b/client/control.go index 09ca2a02..4a588c5d 100644 --- a/client/control.go +++ b/client/control.go @@ -37,7 +37,8 @@ type Control struct { runId string // manage all proxies - pm *ProxyManager + pxyCfgs map[string]config.ProxyConf + pm *ProxyManager // manage all visitors vm *VisitorManager @@ -76,6 +77,7 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs m runId: runId, conn: conn, session: session, + pxyCfgs: pxyCfgs, sendCh: make(chan msg.Message, 100), readCh: make(chan msg.Message, 100), closedCh: make(chan struct{}), @@ -85,8 +87,8 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs m msgHandlerShutdown: shutdown.New(), Logger: log.NewPrefixLogger(""), } - ctl.pm = NewProxyManager(ctl.sendCh, "") - ctl.pm.Reload(pxyCfgs, false) + ctl.pm = NewProxyManager(ctl.sendCh, runId) + ctl.vm = NewVisitorManager(ctl) ctl.vm.Reload(visitorCfgs) return ctl @@ -95,10 +97,10 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs m func (ctl *Control) Run() { go ctl.worker() - // start all local visitors and send NewProxy message for all configured proxies - ctl.pm.Reset(ctl.sendCh, ctl.runId) - ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew}) + // start all proxies + ctl.pm.Reload(ctl.pxyCfgs) + // start all visitors go ctl.vm.Run() return } @@ -142,7 +144,7 @@ func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) { } func (ctl *Control) Close() error { - ctl.pm.CloseProxies() + ctl.conn.Close() return nil } @@ -275,33 +277,26 @@ func (ctl *Control) worker() { go ctl.reader() go ctl.writer() - checkInterval := 60 * time.Second - checkProxyTicker := time.NewTicker(checkInterval) + select { + case <-ctl.closedCh: + // close related channels and wait until other goroutines done + close(ctl.readCh) + ctl.readerShutdown.WaitDone() + ctl.msgHandlerShutdown.WaitDone() - for { - select { - case <-checkProxyTicker.C: - // check which proxy registered failed and reregister it to server - ctl.pm.CheckAndStartProxy([]string{ProxyStatusStartErr, ProxyStatusClosed}) - case <-ctl.closedCh: - // close related channels and wait until other goroutines done - close(ctl.readCh) - ctl.readerShutdown.WaitDone() - ctl.msgHandlerShutdown.WaitDone() + close(ctl.sendCh) + ctl.writerShutdown.WaitDone() - close(ctl.sendCh) - ctl.writerShutdown.WaitDone() + ctl.pm.Close() + ctl.vm.Close() - ctl.pm.CloseProxies() - - close(ctl.closedDoneCh) - return - } + close(ctl.closedDoneCh) + return } } func (ctl *Control) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error { ctl.vm.Reload(visitorCfgs) - err := ctl.pm.Reload(pxyCfgs, true) - return err + ctl.pm.Reload(pxyCfgs) + return nil } diff --git a/client/event.go b/client/event.go new file mode 100644 index 00000000..b10b1e48 --- /dev/null +++ b/client/event.go @@ -0,0 +1,28 @@ +package client + +import ( + "errors" + + "github.com/fatedier/frp/models/msg" +) + +type EventType int + +const ( + EvStartProxy EventType = iota + EvCloseProxy +) + +var ( + ErrPayloadType = errors.New("error payload type") +) + +type EventHandler func(evType EventType, payload interface{}) error + +type StartProxyPayload struct { + NewProxyMsg *msg.NewProxy +} + +type CloseProxyPayload struct { + CloseProxyMsg *msg.CloseProxy +} diff --git a/client/health.go b/client/health.go index 8e84a6f8..7002adee 100644 --- a/client/health.go +++ b/client/health.go @@ -16,9 +16,17 @@ package client import ( "context" + "errors" + "fmt" "net" "net/http" "time" + + "github.com/fatedier/frp/utils/log" +) + +var ( + ErrHealthCheckType = errors.New("error health check type") ) type HealthCheckMonitor struct { @@ -40,6 +48,8 @@ type HealthCheckMonitor struct { ctx context.Context cancel context.CancelFunc + + l log.Logger } func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string, @@ -70,6 +80,10 @@ func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFai } } +func (monitor *HealthCheckMonitor) SetLogger(l log.Logger) { + monitor.l = l +} + func (monitor *HealthCheckMonitor) Start() { go monitor.checkWorker() } @@ -81,7 +95,7 @@ func (monitor *HealthCheckMonitor) Stop() { func (monitor *HealthCheckMonitor) checkWorker() { for { ctx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout)) - ok := monitor.doCheck(ctx) + err := monitor.doCheck(ctx) // check if this monitor has been closed select { @@ -92,14 +106,26 @@ func (monitor *HealthCheckMonitor) checkWorker() { cancel() } - if ok { + if err == nil { + if monitor.l != nil { + monitor.l.Trace("do one health check success") + } if !monitor.statusOK && monitor.statusNormalFn != nil { + if monitor.l != nil { + monitor.l.Info("health check status change to success") + } monitor.statusOK = true monitor.statusNormalFn() } } else { + if monitor.l != nil { + monitor.l.Warn("do one health check failed: %v", err) + } monitor.failedTimes++ if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil { + if monitor.l != nil { + monitor.l.Warn("health check status change to failed") + } monitor.statusOK = false monitor.statusFailedFn() } @@ -109,39 +135,44 @@ func (monitor *HealthCheckMonitor) checkWorker() { } } -func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) bool { +func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) error { switch monitor.checkType { case "tcp": return monitor.doTcpCheck(ctx) case "http": return monitor.doHttpCheck(ctx) default: - return false + return ErrHealthCheckType } } -func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) bool { +func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) error { + // if tcp address is not specified, always return nil + if monitor.addr == "" { + return nil + } + var d net.Dialer conn, err := d.DialContext(ctx, "tcp", monitor.addr) if err != nil { - return false + return err } conn.Close() - return true + return nil } -func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) bool { +func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) error { req, err := http.NewRequest("GET", monitor.url, nil) if err != nil { - return false + return err } resp, err := http.DefaultClient.Do(req) if err != nil { - return false + return err } if resp.StatusCode/100 != 2 { - return false + return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode) } - return true + return nil } diff --git a/client/proxy_manager.go b/client/proxy_manager.go index dc9f350d..42fa9a77 100644 --- a/client/proxy_manager.go +++ b/client/proxy_manager.go @@ -12,126 +12,91 @@ import ( "github.com/fatedier/golib/errors" ) -const ( - ProxyStatusNew = "new" - ProxyStatusStartErr = "start error" - ProxyStatusWaitStart = "wait start" - ProxyStatusRunning = "running" - ProxyStatusCheckFailed = "check failed" - ProxyStatusClosed = "closed" -) - type ProxyManager struct { sendCh chan (msg.Message) proxies map[string]*ProxyWrapper - closed bool - mu sync.RWMutex + closed bool + mu sync.RWMutex + + logPrefix string log.Logger } func NewProxyManager(msgSendCh chan (msg.Message), logPrefix string) *ProxyManager { return &ProxyManager{ - proxies: make(map[string]*ProxyWrapper), - sendCh: msgSendCh, - closed: false, - Logger: log.NewPrefixLogger(logPrefix), + proxies: make(map[string]*ProxyWrapper), + sendCh: msgSendCh, + closed: false, + logPrefix: logPrefix, + Logger: log.NewPrefixLogger(logPrefix), } } -func (pm *ProxyManager) Reset(msgSendCh chan (msg.Message), logPrefix string) { - pm.mu.Lock() - defer pm.mu.Unlock() - pm.closed = false - pm.sendCh = msgSendCh - pm.ClearLogPrefix() - pm.AddLogPrefix(logPrefix) -} - -// Must hold the lock before calling this function. -func (pm *ProxyManager) sendMsg(m msg.Message) error { - err := errors.PanicToError(func() { - pm.sendCh <- m - }) - if err != nil { - pm.closed = true - } - return err -} - func (pm *ProxyManager) StartProxy(name string, remoteAddr string, serverRespErr string) error { - pm.mu.Lock() - defer pm.mu.Unlock() - if pm.closed { - return fmt.Errorf("ProxyManager is closed now") - } - + pm.mu.RLock() pxy, ok := pm.proxies[name] + pm.mu.RUnlock() if !ok { - return fmt.Errorf("no proxy found") + return fmt.Errorf("proxy [%s] not found", name) } - if err := pxy.Start(remoteAddr, serverRespErr); err != nil { - errRet := err - err = pm.sendMsg(&msg.CloseProxy{ - ProxyName: name, - }) - if err != nil { - errRet = fmt.Errorf("send CloseProxy message error") - } - return errRet + err := pxy.SetRunningStatus(remoteAddr, serverRespErr) + if err != nil { + return err } return nil } -func (pm *ProxyManager) CloseProxies() { +func (pm *ProxyManager) Close() { pm.mu.RLock() defer pm.mu.RUnlock() for _, pxy := range pm.proxies { - pxy.Close() + pxy.Stop() } } -// pxyStatus: check and start proxies in which status -func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) { +func (pm *ProxyManager) HandleWorkConn(name string, workConn frpNet.Conn) { + pm.mu.RLock() + pw, ok := pm.proxies[name] + pm.mu.RUnlock() + if ok { + pw.InWorkConn(workConn) + } else { + workConn.Close() + } +} + +func (pm *ProxyManager) HandleEvent(evType EventType, payload interface{}) error { + var m msg.Message + switch event := payload.(type) { + case *StartProxyPayload: + m = event.NewProxyMsg + case *CloseProxyPayload: + m = event.CloseProxyMsg + default: + return ErrPayloadType + } + + err := errors.PanicToError(func() { + pm.sendCh <- m + }) + return err +} + +func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus { + ps := make([]*ProxyStatus, 0) pm.mu.RLock() defer pm.mu.RUnlock() - if pm.closed { - pm.Warn("CheckAndStartProxy error: ProxyManager is closed now") - return - } - for _, pxy := range pm.proxies { - status := pxy.GetStatusStr() - for _, s := range pxyStatus { - if status == s { - var newProxyMsg msg.NewProxy - pxy.Cfg.MarshalToMsg(&newProxyMsg) - err := pm.sendMsg(&newProxyMsg) - if err != nil { - pm.Warn("[%s] proxy send NewProxy message error") - return - } - pxy.WaitStart() - break - } - } + ps = append(ps, pxy.GetStatus()) } + return ps } -func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, startNow bool) error { +func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) { pm.mu.Lock() - defer func() { - pm.mu.Unlock() - if startNow { - go pm.CheckAndStartProxy([]string{ProxyStatusNew}) - } - }() - if pm.closed { - err := fmt.Errorf("Reload error: ProxyManager is closed now") - pm.Warn(err.Error()) - return err - } + defer pm.mu.Unlock() delPxyNames := make([]string, 0) for name, pxy := range pm.proxies { @@ -149,163 +114,24 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, startNow boo delPxyNames = append(delPxyNames, name) delete(pm.proxies, name) - pxy.Close() - err := pm.sendMsg(&msg.CloseProxy{ - ProxyName: name, - }) - if err != nil { - err = fmt.Errorf("Reload error: ProxyManager is closed now") - pm.Warn(err.Error()) - return err - } + pxy.Stop() } } - pm.Info("proxy removed: %v", delPxyNames) + if len(delPxyNames) > 0 { + pm.Info("proxy removed: %v", delPxyNames) + } addPxyNames := make([]string, 0) for name, cfg := range pxyCfgs { if _, ok := pm.proxies[name]; !ok { - pxy := NewProxyWrapper(cfg) + pxy := NewProxyWrapper(cfg, pm.HandleEvent, pm.logPrefix) pm.proxies[name] = pxy addPxyNames = append(addPxyNames, name) + + pxy.Start() } } - pm.Info("proxy added: %v", addPxyNames) - return nil -} - -func (pm *ProxyManager) HandleWorkConn(name string, workConn frpNet.Conn) { - pm.mu.RLock() - pw, ok := pm.proxies[name] - pm.mu.RUnlock() - if ok { - pw.InWorkConn(workConn) - } else { - workConn.Close() + if len(addPxyNames) > 0 { + pm.Info("proxy added: %v", addPxyNames) } } - -func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus { - ps := make([]*ProxyStatus, 0) - pm.mu.RLock() - defer pm.mu.RUnlock() - for _, pxy := range pm.proxies { - ps = append(ps, pxy.GetStatus()) - } - return ps -} - -type ProxyStatus struct { - Name string `json:"name"` - Type string `json:"type"` - Status string `json:"status"` - Err string `json:"err"` - Cfg config.ProxyConf `json:"cfg"` - - // Got from server. - RemoteAddr string `json:"remote_addr"` -} - -// ProxyWrapper is a wrapper of Proxy interface only used in ProxyManager -// Add additional proxy status info -type ProxyWrapper struct { - Name string - Type string - Status string - Err string - Cfg config.ProxyConf - - RemoteAddr string - - pxy Proxy - - mu sync.RWMutex -} - -func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper { - return &ProxyWrapper{ - Name: cfg.GetBaseInfo().ProxyName, - Type: cfg.GetBaseInfo().ProxyType, - Status: ProxyStatusNew, - Cfg: cfg, - pxy: nil, - } -} - -func (pw *ProxyWrapper) GetStatusStr() string { - pw.mu.RLock() - defer pw.mu.RUnlock() - return pw.Status -} - -func (pw *ProxyWrapper) GetStatus() *ProxyStatus { - pw.mu.RLock() - defer pw.mu.RUnlock() - ps := &ProxyStatus{ - Name: pw.Name, - Type: pw.Type, - Status: pw.Status, - Err: pw.Err, - Cfg: pw.Cfg, - RemoteAddr: pw.RemoteAddr, - } - return ps -} - -func (pw *ProxyWrapper) WaitStart() { - pw.mu.Lock() - defer pw.mu.Unlock() - pw.Status = ProxyStatusWaitStart -} - -func (pw *ProxyWrapper) Start(remoteAddr string, serverRespErr string) error { - if pw.pxy != nil { - pw.pxy.Close() - pw.pxy = nil - } - - if serverRespErr != "" { - pw.mu.Lock() - pw.Status = ProxyStatusStartErr - pw.RemoteAddr = remoteAddr - pw.Err = serverRespErr - pw.mu.Unlock() - return fmt.Errorf(serverRespErr) - } - - pxy := NewProxy(pw.Cfg) - pw.mu.Lock() - defer pw.mu.Unlock() - pw.RemoteAddr = remoteAddr - if err := pxy.Run(); err != nil { - pw.Status = ProxyStatusStartErr - pw.Err = err.Error() - return err - } - pw.Status = ProxyStatusRunning - pw.Err = "" - pw.pxy = pxy - return nil -} - -func (pw *ProxyWrapper) InWorkConn(workConn frpNet.Conn) { - pw.mu.RLock() - pxy := pw.pxy - pw.mu.RUnlock() - if pxy != nil { - workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String()) - go pxy.InWorkConn(workConn) - } else { - workConn.Close() - } -} - -func (pw *ProxyWrapper) Close() { - pw.mu.Lock() - defer pw.mu.Unlock() - if pw.pxy != nil { - pw.pxy.Close() - pw.pxy = nil - } - pw.Status = ProxyStatusClosed -} diff --git a/client/proxy_wrapper.go b/client/proxy_wrapper.go new file mode 100644 index 00000000..a19d97b2 --- /dev/null +++ b/client/proxy_wrapper.go @@ -0,0 +1,219 @@ +package client + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/msg" + "github.com/fatedier/frp/utils/log" + frpNet "github.com/fatedier/frp/utils/net" +) + +const ( + ProxyStatusNew = "new" + ProxyStatusWaitStart = "wait start" + ProxyStatusStartErr = "start error" + ProxyStatusRunning = "running" + ProxyStatusCheckFailed = "check failed" + ProxyStatusClosed = "closed" +) + +var ( + statusCheckInterval time.Duration = 3 * time.Second + waitResponseTimeout = 20 * time.Second + startErrTimeout = 30 * time.Second +) + +type ProxyStatus struct { + Name string `json:"name"` + Type string `json:"type"` + Status string `json:"status"` + Err string `json:"err"` + Cfg config.ProxyConf `json:"cfg"` + + // Got from server. + RemoteAddr string `json:"remote_addr"` +} + +type ProxyWrapper struct { + ProxyStatus + + // underlying proxy + pxy Proxy + + // if ProxyConf has healcheck config + // monitor will watch if it is alive + monitor *HealthCheckMonitor + + // event handler + handler EventHandler + + health uint32 + lastSendStartMsg time.Time + lastStartErr time.Time + closeCh chan struct{} + mu sync.RWMutex + + log.Logger +} + +func NewProxyWrapper(cfg config.ProxyConf, eventHandler EventHandler, logPrefix string) *ProxyWrapper { + baseInfo := cfg.GetBaseInfo() + pw := &ProxyWrapper{ + ProxyStatus: ProxyStatus{ + Name: baseInfo.ProxyName, + Type: baseInfo.ProxyType, + Status: ProxyStatusNew, + Cfg: cfg, + }, + closeCh: make(chan struct{}), + handler: eventHandler, + Logger: log.NewPrefixLogger(logPrefix), + } + pw.AddLogPrefix(pw.Name) + + if baseInfo.HealthCheckType != "" { + pw.health = 1 // means failed + pw.monitor = NewHealthCheckMonitor(baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS, + baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr, + baseInfo.HealthCheckUrl, pw.statusNormalCallback, pw.statusFailedCallback) + pw.monitor.SetLogger(pw.Logger) + pw.Trace("enable health check monitor") + } + + pw.pxy = NewProxy(pw.Cfg) + return pw +} + +func (pw *ProxyWrapper) SetRunningStatus(remoteAddr string, respErr string) error { + pw.mu.Lock() + defer pw.mu.Unlock() + if pw.Status != ProxyStatusWaitStart { + return fmt.Errorf("status not wait start, ignore start message") + } + + pw.RemoteAddr = remoteAddr + if respErr != "" { + pw.Status = ProxyStatusStartErr + pw.Err = respErr + pw.lastStartErr = time.Now() + return fmt.Errorf(pw.Err) + } + + if err := pw.pxy.Run(); err != nil { + pw.Status = ProxyStatusStartErr + pw.Err = err.Error() + pw.lastStartErr = time.Now() + return err + } + + pw.Status = ProxyStatusRunning + pw.Err = "" + return nil +} + +func (pw *ProxyWrapper) Start() { + go pw.checkWorker() + if pw.monitor != nil { + go pw.monitor.Start() + } +} + +func (pw *ProxyWrapper) Stop() { + pw.mu.Lock() + defer pw.mu.Unlock() + pw.pxy.Close() + if pw.monitor != nil { + pw.monitor.Stop() + } + pw.Status = ProxyStatusClosed + + pw.handler(EvCloseProxy, &CloseProxyPayload{ + CloseProxyMsg: &msg.CloseProxy{ + ProxyName: pw.Name, + }, + }) +} + +func (pw *ProxyWrapper) checkWorker() { + for { + // check proxy status + now := time.Now() + if atomic.LoadUint32(&pw.health) == 0 { + pw.mu.Lock() + if pw.Status == ProxyStatusNew || + pw.Status == ProxyStatusCheckFailed || + (pw.Status == ProxyStatusWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) || + (pw.Status == ProxyStatusStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) { + + pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusWaitStart) + pw.Status = ProxyStatusWaitStart + + var newProxyMsg msg.NewProxy + pw.Cfg.MarshalToMsg(&newProxyMsg) + pw.lastSendStartMsg = now + pw.handler(EvStartProxy, &StartProxyPayload{ + NewProxyMsg: &newProxyMsg, + }) + } + pw.mu.Unlock() + } else { + pw.mu.Lock() + if pw.Status == ProxyStatusRunning || pw.Status == ProxyStatusWaitStart { + pw.handler(EvCloseProxy, &CloseProxyPayload{ + CloseProxyMsg: &msg.CloseProxy{ + ProxyName: pw.Name, + }, + }) + pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed) + pw.Status = ProxyStatusCheckFailed + } + pw.mu.Unlock() + } + + select { + case <-pw.closeCh: + return + case <-time.After(statusCheckInterval): + } + } +} + +func (pw *ProxyWrapper) statusNormalCallback() { + atomic.StoreUint32(&pw.health, 0) + pw.Info("health check success") +} + +func (pw *ProxyWrapper) statusFailedCallback() { + atomic.StoreUint32(&pw.health, 1) + pw.Info("health check failed") +} + +func (pw *ProxyWrapper) InWorkConn(workConn frpNet.Conn) { + pw.mu.RLock() + pxy := pw.pxy + pw.mu.RUnlock() + if pxy != nil { + workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String()) + go pxy.InWorkConn(workConn) + } else { + workConn.Close() + } +} + +func (pw *ProxyWrapper) GetStatus() *ProxyStatus { + pw.mu.RLock() + defer pw.mu.RUnlock() + ps := &ProxyStatus{ + Name: pw.Name, + Type: pw.Type, + Status: pw.Status, + Err: pw.Err, + Cfg: pw.Cfg, + RemoteAddr: pw.RemoteAddr, + } + return ps +} diff --git a/client/visitor_manager.go b/client/visitor_manager.go index 3e0aa80b..b223d55d 100644 --- a/client/visitor_manager.go +++ b/client/visitor_manager.go @@ -96,7 +96,9 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) { delete(vm.visitors, name) } } - log.Info("visitor removed: %v", delNames) + if len(delNames) > 0 { + log.Info("visitor removed: %v", delNames) + } addNames := make([]string, 0) for name, cfg := range cfgs { @@ -106,6 +108,16 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) { vm.startVisitor(cfg) } } - log.Info("visitor added: %v", addNames) + if len(addNames) > 0 { + log.Info("visitor added: %v", addNames) + } return } + +func (vm *VisitorManager) Close() { + vm.mu.Lock() + defer vm.mu.Unlock() + for _, v := range vm.visitors { + v.Close() + } +} diff --git a/models/config/proxy.go b/models/config/proxy.go index 9ea680a4..39e31946 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -170,6 +170,10 @@ func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section i if err := cfg.HealthCheckConf.UnmarshalFromIni(prefix, name, section); err != nil { return err } + + if cfg.HealthCheckType == "tcp" && cfg.Plugin == "" { + cfg.HealthCheckAddr = cfg.LocalIp + fmt.Sprintf(":%d", cfg.LocalPort) + } return nil } @@ -381,7 +385,7 @@ func (cfg *LocalSvrConf) checkForCli() (err error) { // Health check info type HealthCheckConf struct { HealthCheckType string `json:"health_check_type"` // tcp | http - HealthCheckTimeout int `json:"health_check_timeout"` + HealthCheckTimeoutS int `json:"health_check_timeout_s"` HealthCheckMaxFailed int `json:"health_check_max_failed"` HealthCheckIntervalS int `json:"health_check_interval_s"` HealthCheckUrl string `json:"health_check_url"` @@ -392,8 +396,10 @@ type HealthCheckConf struct { func (cfg *HealthCheckConf) compare(cmp *HealthCheckConf) bool { if cfg.HealthCheckType != cmp.HealthCheckType || - cfg.HealthCheckUrl != cmp.HealthCheckUrl || - cfg.HealthCheckIntervalS != cmp.HealthCheckIntervalS { + cfg.HealthCheckTimeoutS != cmp.HealthCheckTimeoutS || + cfg.HealthCheckMaxFailed != cmp.HealthCheckMaxFailed || + cfg.HealthCheckIntervalS != cmp.HealthCheckIntervalS || + cfg.HealthCheckUrl != cmp.HealthCheckUrl { return false } return true @@ -403,6 +409,18 @@ func (cfg *HealthCheckConf) UnmarshalFromIni(prefix string, name string, section cfg.HealthCheckType = section["health_check_type"] cfg.HealthCheckUrl = section["health_check_url"] + if tmpStr, ok := section["health_check_timeout_s"]; ok { + if cfg.HealthCheckTimeoutS, err = strconv.Atoi(tmpStr); err != nil { + return fmt.Errorf("Parse conf error: proxy [%s] health_check_timeout_s error", name) + } + } + + if tmpStr, ok := section["health_check_max_failed"]; ok { + if cfg.HealthCheckMaxFailed, err = strconv.Atoi(tmpStr); err != nil { + return fmt.Errorf("Parse conf error: proxy [%s] health_check_max_failed error", name) + } + } + if tmpStr, ok := section["health_check_interval_s"]; ok { if cfg.HealthCheckIntervalS, err = strconv.Atoi(tmpStr); err != nil { return fmt.Errorf("Parse conf error: proxy [%s] health_check_interval_s error", name) @@ -419,9 +437,6 @@ func (cfg *HealthCheckConf) checkForCli() error { if cfg.HealthCheckType == "http" && cfg.HealthCheckUrl == "" { return fmt.Errorf("health_check_url is required for health check type 'http'") } - if cfg.HealthCheckIntervalS <= 0 { - return fmt.Errorf("health_check_interval_s is required and should greater than 0") - } } return nil } diff --git a/server/group/group.go b/server/group/group.go index 859239eb..a0dae7cd 100644 --- a/server/group/group.go +++ b/server/group/group.go @@ -22,4 +22,5 @@ var ( ErrGroupAuthFailed = errors.New("group auth failed") ErrGroupParamsInvalid = errors.New("group params invalid") ErrListenerClosed = errors.New("group listener closed") + ErrGroupDifferentPort = errors.New("group should have same remote port") ) diff --git a/server/group/tcp.go b/server/group/tcp.go index 2de05b4c..8c46be65 100644 --- a/server/group/tcp.go +++ b/server/group/tcp.go @@ -114,10 +114,14 @@ func (tg *TcpGroup) Listen(proxyName string, group string, groupKey string, addr } go tg.worker() } else { - if tg.group != group || tg.addr != addr || tg.port != port { + if tg.group != group || tg.addr != addr { err = ErrGroupParamsInvalid return } + if tg.port != port { + err = ErrGroupDifferentPort + return + } if tg.groupKey != groupKey { err = ErrGroupAuthFailed return From aea9f9fbcc8735193c979c684ae360d450d40c3c Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 9 Dec 2018 21:56:46 +0800 Subject: [PATCH 18/20] health: add more ci cases and fix bugs --- Makefile | 2 +- client/proxy_wrapper.go | 29 +++- cmd/frpc/sub/root.go | 5 + cmd/frps/root.go | 9 +- conf/frpc_full.ini | 2 + models/config/proxy.go | 7 + tests/ci/health/health_test.go | 247 +++++++++++++++++++++++++++++++++ tests/ci/normal_test.go | 14 +- tests/ci/reconnect_test.go | 2 - tests/ci/reload_test.go | 8 +- tests/consts/consts.go | 3 + tests/mock/echo_server.go | 92 ++++++------ tests/mock/http_server.go | 31 +++++ tests/util/process.go | 24 +++- 14 files changed, 409 insertions(+), 66 deletions(-) create mode 100644 tests/ci/health/health_test.go diff --git a/Makefile b/Makefile index bc450af9..c217eb88 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ gotest: go test -v --cover ./utils/... ci: - go test -count=1 -v ./tests/... + go test -count=1 -p=1 -v ./tests/... alltest: gotest ci diff --git a/client/proxy_wrapper.go b/client/proxy_wrapper.go index a19d97b2..059d1821 100644 --- a/client/proxy_wrapper.go +++ b/client/proxy_wrapper.go @@ -10,6 +10,8 @@ import ( "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/log" frpNet "github.com/fatedier/frp/utils/net" + + "github.com/fatedier/golib/errors" ) const ( @@ -55,6 +57,7 @@ type ProxyWrapper struct { lastSendStartMsg time.Time lastStartErr time.Time closeCh chan struct{} + healthNotifyCh chan struct{} mu sync.RWMutex log.Logger @@ -69,9 +72,10 @@ func NewProxyWrapper(cfg config.ProxyConf, eventHandler EventHandler, logPrefix Status: ProxyStatusNew, Cfg: cfg, }, - closeCh: make(chan struct{}), - handler: eventHandler, - Logger: log.NewPrefixLogger(logPrefix), + closeCh: make(chan struct{}), + healthNotifyCh: make(chan struct{}), + handler: eventHandler, + Logger: log.NewPrefixLogger(logPrefix), } pw.AddLogPrefix(pw.Name) @@ -125,6 +129,8 @@ func (pw *ProxyWrapper) Start() { func (pw *ProxyWrapper) Stop() { pw.mu.Lock() defer pw.mu.Unlock() + close(pw.closeCh) + close(pw.healthNotifyCh) pw.pxy.Close() if pw.monitor != nil { pw.monitor.Stop() @@ -139,6 +145,10 @@ func (pw *ProxyWrapper) Stop() { } func (pw *ProxyWrapper) checkWorker() { + if pw.monitor != nil { + // let monitor do check request first + time.Sleep(500 * time.Millisecond) + } for { // check proxy status now := time.Now() @@ -178,17 +188,30 @@ func (pw *ProxyWrapper) checkWorker() { case <-pw.closeCh: return case <-time.After(statusCheckInterval): + case <-pw.healthNotifyCh: } } } func (pw *ProxyWrapper) statusNormalCallback() { atomic.StoreUint32(&pw.health, 0) + errors.PanicToError(func() { + select { + case pw.healthNotifyCh <- struct{}{}: + default: + } + }) pw.Info("health check success") } func (pw *ProxyWrapper) statusFailedCallback() { atomic.StoreUint32(&pw.health, 1) + errors.PanicToError(func() { + select { + case pw.healthNotifyCh <- struct{}{}: + default: + } + }) pw.Info("health check failed") } diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index 23b3bf9c..ca3b853e 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -166,6 +166,11 @@ func parseClientCommonCfgFromCmd() (err error) { g.GlbClientCfg.LogLevel = logLevel g.GlbClientCfg.LogFile = logFile g.GlbClientCfg.LogMaxDays = int64(logMaxDays) + if logFile == "console" { + g.GlbClientCfg.LogWay = "console" + } else { + g.GlbClientCfg.LogWay = "file" + } return nil } diff --git a/cmd/frps/root.go b/cmd/frps/root.go index 76a1acd9..b4ccff4f 100644 --- a/cmd/frps/root.go +++ b/cmd/frps/root.go @@ -52,7 +52,6 @@ var ( dashboardPwd string assetsDir string logFile string - logWay string logLevel string logMaxDays int64 token string @@ -81,7 +80,6 @@ func init() { rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user") rootCmd.PersistentFlags().StringVarP(&dashboardPwd, "dashboard_pwd", "", "admin", "dashboard password") rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file") - rootCmd.PersistentFlags().StringVarP(&logWay, "log_way", "", "console", "log way") rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log_max_days") rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") @@ -175,7 +173,6 @@ func parseServerCommonCfgFromCmd() (err error) { g.GlbServerCfg.DashboardUser = dashboardUser g.GlbServerCfg.DashboardPwd = dashboardPwd g.GlbServerCfg.LogFile = logFile - g.GlbServerCfg.LogWay = logWay g.GlbServerCfg.LogLevel = logLevel g.GlbServerCfg.LogMaxDays = logMaxDays g.GlbServerCfg.Token = token @@ -194,6 +191,12 @@ func parseServerCommonCfgFromCmd() (err error) { } } g.GlbServerCfg.MaxPortsPerClient = maxPortsPerClient + + if logFile == "console" { + g.GlbClientCfg.LogWay = "console" + } else { + g.GlbClientCfg.LogWay = "file" + } return } diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 2b5d327b..3f9a69da 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -77,6 +77,8 @@ group_key = 123456 # frpc will connect local service's port to detect it's healthy status health_check_type = tcp health_check_interval_s = 10 +health_check_max_failed = 1 +health_check_timeout_s = 3 [ssh_random] type = tcp diff --git a/models/config/proxy.go b/models/config/proxy.go index 39e31946..0766b5ae 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -174,6 +174,13 @@ func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section i if cfg.HealthCheckType == "tcp" && cfg.Plugin == "" { cfg.HealthCheckAddr = cfg.LocalIp + fmt.Sprintf(":%d", cfg.LocalPort) } + if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckUrl != "" { + s := fmt.Sprintf("http://%s:%d", cfg.LocalIp, cfg.LocalPort) + if !strings.HasPrefix(cfg.HealthCheckUrl, "/") { + s += "/" + } + cfg.HealthCheckUrl = s + cfg.HealthCheckUrl + } return nil } diff --git a/tests/ci/health/health_test.go b/tests/ci/health/health_test.go new file mode 100644 index 00000000..fd1a5286 --- /dev/null +++ b/tests/ci/health/health_test.go @@ -0,0 +1,247 @@ +package health + +import ( + "net/http" + "os" + "strings" + "sync" + "testing" + "time" + + "github.com/fatedier/frp/tests/config" + "github.com/fatedier/frp/tests/consts" + "github.com/fatedier/frp/tests/mock" + "github.com/fatedier/frp/tests/util" + + "github.com/stretchr/testify/assert" +) + +const FRPS_CONF = ` +[common] +bind_addr = 0.0.0.0 +bind_port = 14000 +vhost_http_port = 14000 +log_file = console +log_level = debug +token = 123456 +` + +const FRPC_CONF = ` +[common] +server_addr = 127.0.0.1 +server_port = 14000 +log_file = console +log_level = debug +token = 123456 + +[tcp1] +type = tcp +local_port = 15001 +remote_port = 15000 +group = test +group_key = 123 +health_check_type = tcp +health_check_interval_s = 1 + +[tcp2] +type = tcp +local_port = 15002 +remote_port = 15000 +group = test +group_key = 123 +health_check_type = tcp +health_check_interval_s = 1 + +[http1] +type = http +local_port = 15003 +custom_domains = test1.com +health_check_type = http +health_check_interval_s = 1 +health_check_url = /health + +[http2] +type = http +local_port = 15004 +custom_domains = test2.com +health_check_type = http +health_check_interval_s = 1 +health_check_url = /health +` + +func TestHealthCheck(t *testing.T) { + assert := assert.New(t) + + // ****** start backgroud services ****** + echoSvc1 := mock.NewEchoServer(15001, 1, "echo1") + err := echoSvc1.Start() + if assert.NoError(err) { + defer echoSvc1.Stop() + } + + echoSvc2 := mock.NewEchoServer(15002, 1, "echo2") + err = echoSvc2.Start() + if assert.NoError(err) { + defer echoSvc2.Stop() + } + + var healthMu sync.RWMutex + svc1Health := true + svc2Health := true + httpSvc1 := mock.NewHttpServer(15003, func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.URL.Path, "health") { + healthMu.RLock() + defer healthMu.RUnlock() + if svc1Health { + w.WriteHeader(200) + } else { + w.WriteHeader(500) + } + } else { + w.Write([]byte("http1")) + } + }) + err = httpSvc1.Start() + if assert.NoError(err) { + defer httpSvc1.Stop() + } + + httpSvc2 := mock.NewHttpServer(15004, func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.URL.Path, "health") { + healthMu.RLock() + defer healthMu.RUnlock() + if svc2Health { + w.WriteHeader(200) + } else { + w.WriteHeader(500) + } + } else { + w.Write([]byte("http2")) + } + }) + err = httpSvc2.Start() + if assert.NoError(err) { + defer httpSvc2.Stop() + } + + time.Sleep(200 * time.Millisecond) + + // ****** start frps and frpc ****** + frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_CONF) + if assert.NoError(err) { + defer os.Remove(frpsCfgPath) + } + + frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_CONF) + if assert.NoError(err) { + defer os.Remove(frpcCfgPath) + } + + frpsProcess := util.NewProcess(consts.FRPS_SUB_BIN_PATH, []string{"-c", frpsCfgPath}) + err = frpsProcess.Start() + if assert.NoError(err) { + defer frpsProcess.Stop() + } + + time.Sleep(100 * time.Millisecond) + + frpcProcess := util.NewProcess(consts.FRPC_SUB_BIN_PATH, []string{"-c", frpcCfgPath}) + err = frpcProcess.Start() + if assert.NoError(err) { + defer frpcProcess.Stop() + } + time.Sleep(1000 * time.Millisecond) + + // ****** healcheck type tcp ****** + // echo1 and echo2 is ok + result := make([]string, 0) + res, err := util.SendTcpMsg("127.0.0.1:15000", "echo") + assert.NoError(err) + result = append(result, res) + + res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + assert.NoError(err) + result = append(result, res) + + assert.Contains(result, "echo1") + assert.Contains(result, "echo2") + + // close echo2 server, echo1 is work + echoSvc2.Stop() + time.Sleep(1200 * time.Millisecond) + + result = make([]string, 0) + res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + assert.NoError(err) + result = append(result, res) + + res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + assert.NoError(err) + result = append(result, res) + + assert.NotContains(result, "echo2") + + // resume echo2 server, all services are ok + echoSvc2 = mock.NewEchoServer(15002, 1, "echo2") + err = echoSvc2.Start() + if assert.NoError(err) { + defer echoSvc2.Stop() + } + + time.Sleep(1200 * time.Millisecond) + + result = make([]string, 0) + res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + assert.NoError(err) + result = append(result, res) + + res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + assert.NoError(err) + result = append(result, res) + + assert.Contains(result, "echo1") + assert.Contains(result, "echo2") + + // ****** healcheck type http ****** + // http1 and http2 is ok + code, body, _, err := util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") + assert.NoError(err) + assert.Equal(200, code) + assert.Equal("http1", body) + + code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") + assert.NoError(err) + assert.Equal(200, code) + assert.Equal("http2", body) + + // http2 health check error + healthMu.Lock() + svc2Health = false + healthMu.Unlock() + time.Sleep(1200 * time.Millisecond) + + code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") + assert.NoError(err) + assert.Equal(200, code) + assert.Equal("http1", body) + + code, _, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") + assert.NoError(err) + assert.Equal(404, code) + + // resume http2 service, http1 and http2 are ok + healthMu.Lock() + svc2Health = true + healthMu.Unlock() + time.Sleep(1200 * time.Millisecond) + + code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") + assert.NoError(err) + assert.Equal(200, code) + assert.Equal("http1", body) + + code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") + assert.NoError(err) + assert.Equal(200, code) + assert.Equal("http2", body) +} diff --git a/tests/ci/normal_test.go b/tests/ci/normal_test.go index c5283460..d76cc2df 100644 --- a/tests/ci/normal_test.go +++ b/tests/ci/normal_test.go @@ -22,13 +22,21 @@ import ( ) func TestMain(m *testing.M) { - go mock.StartTcpEchoServer(consts.TEST_TCP_PORT) - go mock.StartTcpEchoServer2(consts.TEST_TCP2_PORT) + var err error + tcpEcho1 := mock.NewEchoServer(consts.TEST_TCP_PORT, 1, "") + tcpEcho2 := mock.NewEchoServer(consts.TEST_TCP2_PORT, 2, "") + + if err = tcpEcho1.Start(); err != nil { + panic(err) + } + if err = tcpEcho2.Start(); err != nil { + panic(err) + } + go mock.StartUdpEchoServer(consts.TEST_UDP_PORT) go mock.StartUnixDomainServer(consts.TEST_UNIX_DOMAIN_ADDR) go mock.StartHttpServer(consts.TEST_HTTP_PORT) - var err error p1 := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./auto_test_frps.ini"}) if err = p1.Start(); err != nil { panic(err) diff --git a/tests/ci/reconnect_test.go b/tests/ci/reconnect_test.go index 7974c2c7..114567b2 100644 --- a/tests/ci/reconnect_test.go +++ b/tests/ci/reconnect_test.go @@ -17,7 +17,6 @@ const FRPS_RECONNECT_CONF = ` bind_addr = 0.0.0.0 bind_port = 20000 log_file = console -# debug, info, warn, error log_level = debug token = 123456 ` @@ -27,7 +26,6 @@ const FRPC_RECONNECT_CONF = ` server_addr = 127.0.0.1 server_port = 20000 log_file = console -# debug, info, warn, error log_level = debug token = 123456 admin_port = 21000 diff --git a/tests/ci/reload_test.go b/tests/ci/reload_test.go index 9811db95..f05f2edb 100644 --- a/tests/ci/reload_test.go +++ b/tests/ci/reload_test.go @@ -84,7 +84,8 @@ func TestReload(t *testing.T) { frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RELOAD_CONF_1) if assert.NoError(err) { - defer os.Remove(frpcCfgPath) + rmFile1 := frpcCfgPath + defer os.Remove(rmFile1) } frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath}) @@ -120,7 +121,10 @@ func TestReload(t *testing.T) { // reload frpc config frpcCfgPath, err = config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RELOAD_CONF_2) - assert.NoError(err) + if assert.NoError(err) { + rmFile2 := frpcCfgPath + defer os.Remove(rmFile2) + } err = util.ReloadConf("127.0.0.1:21000", "abc", "abc") assert.NoError(err) diff --git a/tests/consts/consts.go b/tests/consts/consts.go index 60dcffee..4e1c1a00 100644 --- a/tests/consts/consts.go +++ b/tests/consts/consts.go @@ -6,6 +6,9 @@ var ( FRPS_BIN_PATH = "../../bin/frps" FRPC_BIN_PATH = "../../bin/frpc" + FRPS_SUB_BIN_PATH = "../../../bin/frps" + FRPC_SUB_BIN_PATH = "../../../bin/frpc" + FRPS_NORMAL_CONFIG = "./auto_test_frps.ini" FRPC_NORMAL_CONFIG = "./auto_test_frpc.ini" diff --git a/tests/mock/echo_server.go b/tests/mock/echo_server.go index a24947f5..e029f505 100644 --- a/tests/mock/echo_server.go +++ b/tests/mock/echo_server.go @@ -10,40 +10,48 @@ import ( frpNet "github.com/fatedier/frp/utils/net" ) -func StartTcpEchoServer(port int) { - l, err := frpNet.ListenTcp("127.0.0.1", port) - if err != nil { - fmt.Printf("echo server listen error: %v\n", err) - return +type EchoServer struct { + l frpNet.Listener + + port int + repeatedNum int + specifyStr string +} + +func NewEchoServer(port int, repeatedNum int, specifyStr string) *EchoServer { + if repeatedNum <= 0 { + repeatedNum = 1 } - - for { - c, err := l.Accept() - if err != nil { - fmt.Printf("echo server accept error: %v\n", err) - return - } - - go echoWorker(c) + return &EchoServer{ + port: port, + repeatedNum: repeatedNum, + specifyStr: specifyStr, } } -func StartTcpEchoServer2(port int) { - l, err := frpNet.ListenTcp("127.0.0.1", port) +func (es *EchoServer) Start() error { + l, err := frpNet.ListenTcp("127.0.0.1", es.port) if err != nil { - fmt.Printf("echo server2 listen error: %v\n", err) - return + fmt.Printf("echo server listen error: %v\n", err) + return err } + es.l = l - for { - c, err := l.Accept() - if err != nil { - fmt.Printf("echo server2 accept error: %v\n", err) - return + go func() { + for { + c, err := l.Accept() + if err != nil { + return + } + + go echoWorker(c, es.repeatedNum, es.specifyStr) } + }() + return nil +} - go echoWorker2(c) - } +func (es *EchoServer) Stop() { + es.l.Close() } func StartUdpEchoServer(port int) { @@ -60,7 +68,7 @@ func StartUdpEchoServer(port int) { return } - go echoWorker(c) + go echoWorker(c, 1, "") } } @@ -80,11 +88,11 @@ func StartUnixDomainServer(unixPath string) { return } - go echoWorker(c) + go echoWorker(c, 1, "") } } -func echoWorker(c net.Conn) { +func echoWorker(c net.Conn, repeatedNum int, specifyStr string) { buf := make([]byte, 2048) for { @@ -99,28 +107,14 @@ func echoWorker(c net.Conn) { } } - c.Write(buf[:n]) - } -} - -func echoWorker2(c net.Conn) { - buf := make([]byte, 2048) - - for { - n, err := c.Read(buf) - if err != nil { - if err == io.EOF { - c.Close() - break - } else { - fmt.Printf("echo server read error: %v\n", err) - return + if specifyStr != "" { + c.Write([]byte(specifyStr)) + } else { + var w []byte + for i := 0; i < repeatedNum; i++ { + w = append(w, buf[:n]...) } + c.Write(w) } - - var w []byte - w = append(w, buf[:n]...) - w = append(w, buf[:n]...) - c.Write(w) } } diff --git a/tests/mock/http_server.go b/tests/mock/http_server.go index 7e97ad61..37b2b1e6 100644 --- a/tests/mock/http_server.go +++ b/tests/mock/http_server.go @@ -3,6 +3,7 @@ package mock import ( "fmt" "log" + "net" "net/http" "regexp" "strings" @@ -12,6 +13,36 @@ import ( "github.com/gorilla/websocket" ) +type HttpServer struct { + l net.Listener + + port int + handler http.HandlerFunc +} + +func NewHttpServer(port int, handler http.HandlerFunc) *HttpServer { + return &HttpServer{ + port: port, + handler: handler, + } +} + +func (hs *HttpServer) Start() error { + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", hs.port)) + if err != nil { + fmt.Printf("http server listen error: %v\n", err) + return err + } + hs.l = l + + go http.Serve(l, http.HandlerFunc(hs.handler)) + return nil +} + +func (hs *HttpServer) Stop() { + hs.l.Close() +} + var upgrader = websocket.Upgrader{} func StartHttpServer(port int) { diff --git a/tests/util/process.go b/tests/util/process.go index 1e34040d..e7078468 100644 --- a/tests/util/process.go +++ b/tests/util/process.go @@ -1,22 +1,29 @@ package util import ( + "bytes" "context" "os/exec" ) type Process struct { - cmd *exec.Cmd - cancel context.CancelFunc + cmd *exec.Cmd + cancel context.CancelFunc + errorOutput *bytes.Buffer + + beforeStopHandler func() } func NewProcess(path string, params []string) *Process { ctx, cancel := context.WithCancel(context.Background()) cmd := exec.CommandContext(ctx, path, params...) - return &Process{ + p := &Process{ cmd: cmd, cancel: cancel, } + p.errorOutput = bytes.NewBufferString("") + cmd.Stderr = p.errorOutput + return p } func (p *Process) Start() error { @@ -24,6 +31,17 @@ func (p *Process) Start() error { } func (p *Process) Stop() error { + if p.beforeStopHandler != nil { + p.beforeStopHandler() + } p.cancel() return p.cmd.Wait() } + +func (p *Process) ErrorOutput() string { + return p.errorOutput.String() +} + +func (p *Process) SetBeforeStopHandler(fn func()) { + p.beforeStopHandler = fn +} From 35278ad17fe9d658d9a5463ee94315c8cef7bd81 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 9 Dec 2018 22:06:22 +0800 Subject: [PATCH 19/20] mv folders --- client/admin_api.go | 3 ++- client/control.go | 5 +++-- client/{ => event}/event.go | 2 +- client/{ => health}/health.go | 2 +- client/{ => proxy}/proxy.go | 2 +- client/{ => proxy}/proxy_manager.go | 17 +++++++++-------- client/{ => proxy}/proxy_wrapper.go | 18 ++++++++++-------- tests/ci/normal_test.go | 16 ++++++++-------- 8 files changed, 35 insertions(+), 30 deletions(-) rename client/{ => event}/event.go (96%) rename client/{ => health}/health.go (99%) rename client/{ => proxy}/proxy.go (99%) rename client/{ => proxy}/proxy_manager.go (88%) rename client/{ => proxy}/proxy_wrapper.go (90%) diff --git a/client/admin_api.go b/client/admin_api.go index 50745406..893d127d 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -24,6 +24,7 @@ import ( ini "github.com/vaughan0/go-ini" + "github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/utils/log" @@ -121,7 +122,7 @@ func (a ByProxyStatusResp) Len() int { return len(a) } func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 } -func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp { +func NewProxyStatusResp(status *proxy.ProxyStatus) ProxyStatusResp { psr := ProxyStatusResp{ Name: status.Name, Type: status.Type, diff --git a/client/control.go b/client/control.go index 4a588c5d..376893f7 100644 --- a/client/control.go +++ b/client/control.go @@ -21,6 +21,7 @@ import ( "sync" "time" + "github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" @@ -38,7 +39,7 @@ type Control struct { // manage all proxies pxyCfgs map[string]config.ProxyConf - pm *ProxyManager + pm *proxy.ProxyManager // manage all visitors vm *VisitorManager @@ -87,7 +88,7 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs m msgHandlerShutdown: shutdown.New(), Logger: log.NewPrefixLogger(""), } - ctl.pm = NewProxyManager(ctl.sendCh, runId) + ctl.pm = proxy.NewProxyManager(ctl.sendCh, runId) ctl.vm = NewVisitorManager(ctl) ctl.vm.Reload(visitorCfgs) diff --git a/client/event.go b/client/event/event.go similarity index 96% rename from client/event.go rename to client/event/event.go index b10b1e48..e8ea2e20 100644 --- a/client/event.go +++ b/client/event/event.go @@ -1,4 +1,4 @@ -package client +package event import ( "errors" diff --git a/client/health.go b/client/health/health.go similarity index 99% rename from client/health.go rename to client/health/health.go index 7002adee..16c1a598 100644 --- a/client/health.go +++ b/client/health/health.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package client +package health import ( "context" diff --git a/client/proxy.go b/client/proxy/proxy.go similarity index 99% rename from client/proxy.go rename to client/proxy/proxy.go index a89921d2..610a6f88 100644 --- a/client/proxy.go +++ b/client/proxy/proxy.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package client +package proxy import ( "bytes" diff --git a/client/proxy_manager.go b/client/proxy/proxy_manager.go similarity index 88% rename from client/proxy_manager.go rename to client/proxy/proxy_manager.go index 42fa9a77..b65f37a5 100644 --- a/client/proxy_manager.go +++ b/client/proxy/proxy_manager.go @@ -1,9 +1,10 @@ -package client +package proxy import ( "fmt" "sync" + "github.com/fatedier/frp/client/event" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/log" @@ -67,15 +68,15 @@ func (pm *ProxyManager) HandleWorkConn(name string, workConn frpNet.Conn) { } } -func (pm *ProxyManager) HandleEvent(evType EventType, payload interface{}) error { +func (pm *ProxyManager) HandleEvent(evType event.EventType, payload interface{}) error { var m msg.Message - switch event := payload.(type) { - case *StartProxyPayload: - m = event.NewProxyMsg - case *CloseProxyPayload: - m = event.CloseProxyMsg + switch e := payload.(type) { + case *event.StartProxyPayload: + m = e.NewProxyMsg + case *event.CloseProxyPayload: + m = e.CloseProxyMsg default: - return ErrPayloadType + return event.ErrPayloadType } err := errors.PanicToError(func() { diff --git a/client/proxy_wrapper.go b/client/proxy/proxy_wrapper.go similarity index 90% rename from client/proxy_wrapper.go rename to client/proxy/proxy_wrapper.go index 059d1821..f95144c6 100644 --- a/client/proxy_wrapper.go +++ b/client/proxy/proxy_wrapper.go @@ -1,4 +1,4 @@ -package client +package proxy import ( "fmt" @@ -6,6 +6,8 @@ import ( "sync/atomic" "time" + "github.com/fatedier/frp/client/event" + "github.com/fatedier/frp/client/health" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/log" @@ -48,10 +50,10 @@ type ProxyWrapper struct { // if ProxyConf has healcheck config // monitor will watch if it is alive - monitor *HealthCheckMonitor + monitor *health.HealthCheckMonitor // event handler - handler EventHandler + handler event.EventHandler health uint32 lastSendStartMsg time.Time @@ -63,7 +65,7 @@ type ProxyWrapper struct { log.Logger } -func NewProxyWrapper(cfg config.ProxyConf, eventHandler EventHandler, logPrefix string) *ProxyWrapper { +func NewProxyWrapper(cfg config.ProxyConf, eventHandler event.EventHandler, logPrefix string) *ProxyWrapper { baseInfo := cfg.GetBaseInfo() pw := &ProxyWrapper{ ProxyStatus: ProxyStatus{ @@ -81,7 +83,7 @@ func NewProxyWrapper(cfg config.ProxyConf, eventHandler EventHandler, logPrefix if baseInfo.HealthCheckType != "" { pw.health = 1 // means failed - pw.monitor = NewHealthCheckMonitor(baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS, + pw.monitor = health.NewHealthCheckMonitor(baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS, baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr, baseInfo.HealthCheckUrl, pw.statusNormalCallback, pw.statusFailedCallback) pw.monitor.SetLogger(pw.Logger) @@ -137,7 +139,7 @@ func (pw *ProxyWrapper) Stop() { } pw.Status = ProxyStatusClosed - pw.handler(EvCloseProxy, &CloseProxyPayload{ + pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{ CloseProxyMsg: &msg.CloseProxy{ ProxyName: pw.Name, }, @@ -165,7 +167,7 @@ func (pw *ProxyWrapper) checkWorker() { var newProxyMsg msg.NewProxy pw.Cfg.MarshalToMsg(&newProxyMsg) pw.lastSendStartMsg = now - pw.handler(EvStartProxy, &StartProxyPayload{ + pw.handler(event.EvStartProxy, &event.StartProxyPayload{ NewProxyMsg: &newProxyMsg, }) } @@ -173,7 +175,7 @@ func (pw *ProxyWrapper) checkWorker() { } else { pw.mu.Lock() if pw.Status == ProxyStatusRunning || pw.Status == ProxyStatusWaitStart { - pw.handler(EvCloseProxy, &CloseProxyPayload{ + pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{ CloseProxyMsg: &msg.CloseProxy{ ProxyName: pw.Name, }, diff --git a/tests/ci/normal_test.go b/tests/ci/normal_test.go index d76cc2df..24f5795a 100644 --- a/tests/ci/normal_test.go +++ b/tests/ci/normal_test.go @@ -12,7 +12,7 @@ import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" - "github.com/fatedier/frp/client" + "github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/server/ports" "github.com/fatedier/frp/tests/consts" "github.com/fatedier/frp/tests/mock" @@ -218,31 +218,31 @@ func TestAllowPorts(t *testing.T) { // Port not allowed status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortNotAllowed) if assert.NoError(err) { - assert.Equal(client.ProxyStatusStartErr, status.Status) + assert.Equal(proxy.ProxyStatusStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortNotAllowed.Error())) } status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpPortNotAllowed) if assert.NoError(err) { - assert.Equal(client.ProxyStatusStartErr, status.Status) + assert.Equal(proxy.ProxyStatusStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortNotAllowed.Error())) } status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortUnavailable) if assert.NoError(err) { - assert.Equal(client.ProxyStatusStartErr, status.Status) + assert.Equal(proxy.ProxyStatusStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortUnAvailable.Error())) } // Port normal status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortNormal) if assert.NoError(err) { - assert.Equal(client.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyStatusRunning, status.Status) } status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpPortNormal) if assert.NoError(err) { - assert.Equal(client.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyStatusRunning, status.Status) } } @@ -271,7 +271,7 @@ func TestPluginHttpProxy(t *testing.T) { assert := assert.New(t) status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyHttpProxy) if assert.NoError(err) { - assert.Equal(client.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyStatusRunning, status.Status) // http proxy addr := status.RemoteAddr @@ -299,7 +299,7 @@ func TestRangePortsMapping(t *testing.T) { name := fmt.Sprintf("%s_%d", consts.ProxyRangeTcpPrefix, i) status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, name) if assert.NoError(err) { - assert.Equal(client.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyStatusRunning, status.Status) } } } From 146956ac6eda3e5afa3c0908c9fc36b44d1e2a13 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 9 Dec 2018 22:06:56 +0800 Subject: [PATCH 20/20] bump version to v0.22.0 --- utils/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/version/version.go b/utils/version/version.go index 120a1acd..0e85dc91 100644 --- a/utils/version/version.go +++ b/utils/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version string = "0.21.0" +var version string = "0.22.0" func Full() string { return version