From 7dd28a14aa92581ae00d08a827782b90b27555ec Mon Sep 17 00:00:00 2001 From: toby1991 Date: Wed, 4 Apr 2018 11:06:47 +0800 Subject: [PATCH 01/11] fix https://github.com/fatedier/frp/issues/684 #684 Cannot build from Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d1ed20ba..3497aa49 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.8 +FROM golang:1.10 COPY . /go/src/github.com/fatedier/frp From 3fde9176c94f2129905175ac5115733e486ccb9f Mon Sep 17 00:00:00 2001 From: miwee Date: Wed, 4 Apr 2018 12:07:20 +0530 Subject: [PATCH 02/11] dashboard_api for getting a client status by name --- server/dashboard.go | 4 ++ server/dashboard_api.go | 113 ++++++++++++++++++++++++++++++++++++++++ server/metric.go | 31 +++++++++++ 3 files changed, 148 insertions(+) diff --git a/server/dashboard.go b/server/dashboard.go index 3c77875c..1516f489 100644 --- a/server/dashboard.go +++ b/server/dashboard.go @@ -40,6 +40,10 @@ func RunDashboardServer(addr string, port int) (err error) { // api, see dashboard_api.go router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd)) + router.GET("/api/proxy/tcp/:name", frpNet.HttprouterBasicAuth(apiProxyTcpByName, user, passwd)) + router.GET("/api/proxy/udp/:name", frpNet.HttprouterBasicAuth(apiProxyUdpByName, user, passwd)) + router.GET("/api/proxy/http/:name", frpNet.HttprouterBasicAuth(apiProxyHttpByName, user, passwd)) + router.GET("/api/proxy/https/:name", frpNet.HttprouterBasicAuth(apiProxyHttpsByName, user, passwd)) router.GET("/api/proxy/tcp", frpNet.HttprouterBasicAuth(apiProxyTcp, user, passwd)) router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd)) router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd)) diff --git a/server/dashboard_api.go b/server/dashboard_api.go index 3f9acd0f..967b0176 100644 --- a/server/dashboard_api.go +++ b/server/dashboard_api.go @@ -189,6 +189,119 @@ func getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) { return } +// Get proxy info by name. +type GetProxyStatsResp struct { + GeneralResponse + + Name string `json:"name"` + Conf config.ProxyConf `json:"conf"` + TodayTrafficIn int64 `json:"today_traffic_in"` + TodayTrafficOut int64 `json:"today_traffic_out"` + CurConns int64 `json:"cur_conns"` + LastStartTime string `json:"last_start_time"` + LastCloseTime string `json:"last_close_time"` + Status string `json:"status"` +} + +// api/proxy/tcp/:name +func apiProxyTcpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + var ( + buf []byte + res GetProxyStatsResp + ) + name := params.ByName("name") + + defer func() { + log.Info("Http response [/api/proxy/tcp/:name]: code [%d]", res.Code) + }() + log.Info("Http request: [/api/proxy/tcp/:name]") + + res = getProxyStatsByTypeAndName(consts.TcpProxy, name) + + buf, _ = json.Marshal(&res) + w.Write(buf) +} + +// api/proxy/udp/:name +func apiProxyUdpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + var ( + buf []byte + res GetProxyStatsResp + ) + name := params.ByName("name") + + defer func() { + log.Info("Http response [/api/proxy/udp/:name]: code [%d]", res.Code) + }() + log.Info("Http request: [/api/proxy/udp/:name]") + + res = getProxyStatsByTypeAndName(consts.UdpProxy, name) + + buf, _ = json.Marshal(&res) + w.Write(buf) +} + +// api/proxy/http/:name +func apiProxyHttpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + var ( + buf []byte + res GetProxyStatsResp + ) + name := params.ByName("name") + + defer func() { + log.Info("Http response [/api/proxy/http/:name]: code [%d]", res.Code) + }() + log.Info("Http request: [/api/proxy/http/:name]") + + res = getProxyStatsByTypeAndName(consts.HttpProxy, name) + + buf, _ = json.Marshal(&res) + w.Write(buf) +} + +// api/proxy/https/:name +func apiProxyHttpsByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + var ( + buf []byte + res GetProxyStatsResp + ) + name := params.ByName("name") + + defer func() { + log.Info("Http response [/api/proxy/https/:name]: code [%d]", res.Code) + }() + log.Info("Http request: [/api/proxy/https/:name]") + + res = getProxyStatsByTypeAndName(consts.HttpsProxy, name) + + buf, _ = json.Marshal(&res) + w.Write(buf) +} + +func getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp) { + proxyInfo.Name = proxyName + ps := StatsGetProxiesByTypeAndName(proxyType, proxyName) + if ps == nil { + proxyInfo.Code = 1 + proxyInfo.Msg = "no proxy info found" + } else { + if pxy, ok := ServerService.pxyManager.GetByName(proxyName); ok { + proxyInfo.Conf = pxy.GetConf() + proxyInfo.Status = consts.Online + } else { + proxyInfo.Status = consts.Offline + } + proxyInfo.TodayTrafficIn = ps.TodayTrafficIn + proxyInfo.TodayTrafficOut = ps.TodayTrafficOut + proxyInfo.CurConns = ps.CurConns + proxyInfo.LastStartTime = ps.LastStartTime + proxyInfo.LastCloseTime = ps.LastCloseTime + } + + return +} + // api/proxy/traffic/:name type GetProxyTrafficResp struct { GeneralResponse diff --git a/server/metric.go b/server/metric.go index 0e1680ca..990d26f9 100644 --- a/server/metric.go +++ b/server/metric.go @@ -263,6 +263,37 @@ func StatsGetProxiesByType(proxyType string) []*ProxyStats { return res } +func StatsGetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) { + globalStats.mu.Lock() + defer globalStats.mu.Unlock() + + for name, proxyStats := range globalStats.ProxyStatistics { + if proxyStats.ProxyType != proxyType { + continue + } + + if name != proxyName { + continue + } + + res = &ProxyStats{ + Name: name, + Type: proxyStats.ProxyType, + TodayTrafficIn: proxyStats.TrafficIn.TodayCount(), + TodayTrafficOut: proxyStats.TrafficOut.TodayCount(), + CurConns: proxyStats.CurConns.Count(), + } + if !proxyStats.LastStartTime.IsZero() { + res.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05") + } + if !proxyStats.LastCloseTime.IsZero() { + res.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05") + } + break + } + return +} + type ProxyTrafficInfo struct { Name string TrafficIn []int64 From 0f6f674a648e248fd1d842bc24cca2034db148bf Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 10 Apr 2018 17:46:49 +0800 Subject: [PATCH 03/11] cmd: support more cli command --- Makefile | 2 +- README.md | 4 +- README_zh.md | 4 +- client/admin.go | 4 +- client/admin_api.go | 25 +- client/control.go | 34 +- client/proxy.go | 13 +- client/proxy_manager.go | 13 +- client/service.go | 7 +- client/visitor.go | 7 +- cmd/frpc/main.go | 286 +------------ cmd/frpc/sub/http.go | 96 +++++ cmd/frpc/sub/https.go | 88 ++++ cmd/frpc/sub/reload.go | 92 +++++ cmd/frpc/sub/root.go | 200 +++++++++ cmd/frpc/sub/status.go | 146 +++++++ cmd/frpc/sub/stcp.go | 102 +++++ cmd/frpc/sub/tcp.go | 85 ++++ cmd/frpc/sub/udp.go | 85 ++++ cmd/frpc/sub/xtcp.go | 102 +++++ cmd/frps/main.go | 103 +---- cmd/frps/root.go | 174 ++++++++ conf/frpc_full.ini | 8 +- conf/frps_full.ini | 8 +- g/g.go | 32 ++ models/config/client_common.go | 143 +++---- models/config/proxy.go | 380 +++++++++--------- models/config/server_common.go | 189 ++++----- server/control.go | 19 +- server/dashboard.go | 4 +- server/dashboard_api.go | 3 +- server/metric.go | 18 +- server/proxy.go | 23 +- server/service.go | 12 +- tests/conf/auto_test_frpc.ini | 2 +- tests/conf/auto_test_frpc_visitor.ini | 2 +- tests/conf/auto_test_frps.ini | 2 +- utils/version/version.go | 2 +- .../github.com/fatedier/beego/controller.go | 4 +- .../fatedier/beego/controller_test.go | 4 +- .../fatedier/beego/logs/alils/signature.go | 1 - vendor/github.com/fatedier/beego/orm/db.go | 2 +- vendor/github.com/fatedier/beego/template.go | 16 +- .../fatedier/beego/toolbox/statistics.go | 2 +- .../klauspost/cpuid/private/cpuid.go | 262 ++++++------ .../stretchr/testify/assert/assertions.go | 4 +- .../testify/assert/assertions_test.go | 4 +- vendor/github.com/tjfoc/gmsm/sm4/sm4.go | 2 +- vendor/golang.org/x/crypto/ssh/kex.go | 8 +- 49 files changed, 1823 insertions(+), 1005 deletions(-) create mode 100644 cmd/frpc/sub/http.go create mode 100644 cmd/frpc/sub/https.go create mode 100644 cmd/frpc/sub/reload.go create mode 100644 cmd/frpc/sub/root.go create mode 100644 cmd/frpc/sub/status.go create mode 100644 cmd/frpc/sub/stcp.go create mode 100644 cmd/frpc/sub/tcp.go create mode 100644 cmd/frpc/sub/udp.go create mode 100644 cmd/frpc/sub/xtcp.go create mode 100644 cmd/frps/root.go create mode 100644 g/g.go diff --git a/Makefile b/Makefile index b91b3348..263ab35f 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ ci: go test -v ./tests/... cd ./tests && ./clean_test.sh && cd - -ciclean: +cic: cd ./tests && ./clean_test.sh && cd - alltest: gotest ci diff --git a/README.md b/README.md index 458e0e6b..2ecdd253 100644 --- a/README.md +++ b/README.md @@ -383,7 +383,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa ### Authentication -Since v0.10.0, you only need to set `privilege_token` in frps.ini and frpc.ini. +Since v0.10.0, you only need to set `token` in frps.ini and frpc.ini. Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication. @@ -539,7 +539,7 @@ type = http local_port = 80 custom_domains = test.yourdomain.com http_user = abc -http_pwd = abc +http_passwd = abc ``` Visit `http://test.yourdomain.com` and now you need to input username and password. diff --git a/README_zh.md b/README_zh.md index ebc11a38..76499c8b 100644 --- a/README_zh.md +++ b/README_zh.md @@ -401,7 +401,7 @@ dashboard_pwd = admin ### 身份验证 -从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `privilege_token` 参数一致则身份验证通过。 +从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。 需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。 @@ -565,7 +565,7 @@ type = http local_port = 80 custom_domains = test.yourdomain.com http_user = abc -http_pwd = abc +http_passwd = abc ``` 通过浏览器访问 `http://test.yourdomain.com`,需要输入配置的用户名和密码才能访问。 diff --git a/client/admin.go b/client/admin.go index e34f44d2..06a9f766 100644 --- a/client/admin.go +++ b/client/admin.go @@ -20,7 +20,7 @@ import ( "net/http" "time" - "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/g" frpNet "github.com/fatedier/frp/utils/net" "github.com/julienschmidt/httprouter" @@ -35,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) { // url router router := httprouter.New() - user, passwd := config.ClientCommonCfg.AdminUser, config.ClientCommonCfg.AdminPwd + user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd // api, see dashboard_api.go router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd)) diff --git a/client/admin_api.go b/client/admin_api.go index 1a947521..c9ce1699 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -17,6 +17,7 @@ package client import ( "encoding/json" "fmt" + "io/ioutil" "net/http" "sort" "strings" @@ -24,6 +25,7 @@ import ( "github.com/julienschmidt/httprouter" ini "github.com/vaughan0/go-ini" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/utils/log" ) @@ -51,15 +53,16 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout log.Info("Http request: [/api/reload]") - conf, err := ini.LoadFile(config.ClientCommonCfg.ConfigFile) + b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile) if err != nil { res.Code = 1 res.Msg = err.Error() log.Error("reload frpc config file error: %v", err) return } + content := string(b) - newCommonCfg, err := config.LoadClientCommonConf(conf) + newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content) if err != nil { res.Code = 2 res.Msg = err.Error() @@ -67,7 +70,15 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout return } - pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, newCommonCfg.Start) + conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile) + if err != nil { + res.Code = 1 + res.Msg = err.Error() + log.Error("reload frpc config file error: %v", err) + return + } + + pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start) if err != nil { res.Code = 3 res.Msg = err.Error() @@ -125,18 +136,18 @@ func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp { } psr.Plugin = cfg.Plugin if status.Err != "" { - psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort) + psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort) } else { - psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr + psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr } case *config.UdpProxyConf: if cfg.LocalPort != 0 { psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) } if status.Err != "" { - psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort) + psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort) } else { - psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr + psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr } case *config.HttpProxyConf: if cfg.LocalPort != 0 { diff --git a/client/control.go b/client/control.go index 1b31ace7..9342238f 100644 --- a/client/control.go +++ b/client/control.go @@ -21,6 +21,9 @@ import ( "sync" "time" + "github.com/xtaci/smux" + + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/crypto" @@ -29,7 +32,6 @@ import ( "github.com/fatedier/frp/utils/shutdown" "github.com/fatedier/frp/utils/util" "github.com/fatedier/frp/utils/version" - "github.com/xtaci/smux" ) const ( @@ -82,8 +84,8 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m loginMsg := &msg.Login{ Arch: runtime.GOARCH, Os: runtime.GOOS, - PoolCount: config.ClientCommonCfg.PoolCount, - User: config.ClientCommonCfg.User, + PoolCount: g.GlbClientCfg.PoolCount, + User: g.GlbClientCfg.User, Version: version.Full(), } ctl := &Control{ @@ -110,7 +112,7 @@ func (ctl *Control) Run() (err error) { // if login_fail_exit is true, just exit this program // otherwise sleep a while and continues relogin to server - if config.ClientCommonCfg.LoginFailExit { + if g.GlbClientCfg.LoginFailExit { return } else { time.Sleep(10 * time.Second) @@ -183,8 +185,8 @@ func (ctl *Control) login() (err error) { ctl.session.Close() } - conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol, - fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort)) + conn, err := frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, + fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort)) if err != nil { return err } @@ -195,7 +197,7 @@ func (ctl *Control) login() (err error) { } }() - if config.ClientCommonCfg.TcpMux { + if g.GlbClientCfg.TcpMux { session, errRet := smux.Client(conn, nil) if errRet != nil { return errRet @@ -210,7 +212,7 @@ func (ctl *Control) login() (err error) { } now := time.Now().Unix() - ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now) + ctl.loginMsg.PrivilegeKey = util.GetAuthKey(g.GlbClientCfg.Token, now) ctl.loginMsg.Timestamp = now ctl.loginMsg.RunId = ctl.runId @@ -234,7 +236,7 @@ func (ctl *Control) login() (err error) { ctl.conn = conn // update runId got from server ctl.runId = loginRespMsg.RunId - config.ClientCommonCfg.ServerUdpPort = loginRespMsg.ServerUdpPort + 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) @@ -242,7 +244,7 @@ func (ctl *Control) login() (err error) { } func (ctl *Control) connectServer() (conn frpNet.Conn, err error) { - if config.ClientCommonCfg.TcpMux { + if g.GlbClientCfg.TcpMux { stream, errRet := ctl.session.OpenStream() if errRet != nil { err = errRet @@ -251,8 +253,8 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) { } conn = frpNet.WrapConn(stream) } else { - conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol, - fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort)) + conn, err = frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, + fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort)) if err != nil { ctl.Warn("start new connection to server error: %v", err) return @@ -271,7 +273,7 @@ func (ctl *Control) reader() { defer ctl.readerShutdown.Done() defer close(ctl.closedCh) - encReader := crypto.NewReader(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken)) + encReader := crypto.NewReader(ctl.conn, []byte(g.GlbClientCfg.Token)) for { if m, err := msg.ReadMsg(encReader); err != nil { if err == io.EOF { @@ -290,7 +292,7 @@ func (ctl *Control) reader() { // writer writes messages got from sendCh to frps func (ctl *Control) writer() { defer ctl.writerShutdown.Done() - encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken)) + encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbClientCfg.Token)) if err != nil { ctl.conn.Error("crypto new writer error: %v", err) ctl.conn.Close() @@ -318,7 +320,7 @@ func (ctl *Control) msgHandler() { }() defer ctl.msgHandlerShutdown.Done() - hbSend := time.NewTicker(time.Duration(config.ClientCommonCfg.HeartBeatInterval) * time.Second) + hbSend := time.NewTicker(time.Duration(g.GlbClientCfg.HeartBeatInterval) * time.Second) defer hbSend.Stop() hbCheck := time.NewTicker(time.Second) defer hbCheck.Stop() @@ -332,7 +334,7 @@ func (ctl *Control) msgHandler() { ctl.Debug("send heartbeat to server") ctl.sendCh <- &msg.Ping{} case <-hbCheck.C: - if time.Since(ctl.lastPong) > time.Duration(config.ClientCommonCfg.HeartBeatTimeout)*time.Second { + if time.Since(ctl.lastPong) > time.Duration(g.GlbClientCfg.HeartBeatTimeout)*time.Second { ctl.Warn("heartbeat timeout") // let reader() stop ctl.conn.Close() diff --git a/client/proxy.go b/client/proxy.go index f68d9fba..8342bc79 100644 --- a/client/proxy.go +++ b/client/proxy.go @@ -22,6 +22,7 @@ import ( "sync" "time" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/plugin" @@ -46,7 +47,7 @@ type Proxy interface { func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) { baseProxy := BaseProxy{ - Logger: log.NewPrefixLogger(pxyConf.GetName()), + Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName), } switch cfg := pxyConf.(type) { case *config.TcpProxyConf: @@ -115,7 +116,7 @@ func (pxy *TcpProxy) Close() { func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) { HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, - []byte(config.ClientCommonCfg.PrivilegeToken)) + []byte(g.GlbClientCfg.Token)) } // HTTP @@ -144,7 +145,7 @@ func (pxy *HttpProxy) Close() { func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) { HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, - []byte(config.ClientCommonCfg.PrivilegeToken)) + []byte(g.GlbClientCfg.Token)) } // HTTPS @@ -173,7 +174,7 @@ func (pxy *HttpsProxy) Close() { func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) { HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, - []byte(config.ClientCommonCfg.PrivilegeToken)) + []byte(g.GlbClientCfg.Token)) } // STCP @@ -202,7 +203,7 @@ func (pxy *StcpProxy) Close() { func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) { HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, - []byte(config.ClientCommonCfg.PrivilegeToken)) + []byte(g.GlbClientCfg.Token)) } // XTCP @@ -243,7 +244,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) { Sid: natHoleSidMsg.Sid, } raddr, _ := net.ResolveUDPAddr("udp", - fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort)) + fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort)) clientConn, err := net.DialUDP("udp", nil, raddr) defer clientConn.Close() diff --git a/client/proxy_manager.go b/client/proxy_manager.go index 9d81ee76..b8cd64d8 100644 --- a/client/proxy_manager.go +++ b/client/proxy_manager.go @@ -62,8 +62,8 @@ type ProxyStatus struct { func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper { return &ProxyWrapper{ - Name: cfg.GetName(), - Type: cfg.GetType(), + Name: cfg.GetBaseInfo().ProxyName, + Type: cfg.GetBaseInfo().ProxyType, Status: ProxyStatusNew, Cfg: cfg, pxy: nil, @@ -227,7 +227,7 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) { for _, s := range pxyStatus { if status == s { var newProxyMsg msg.NewProxy - pxy.Cfg.UnMarshalToMsg(&newProxyMsg) + pxy.Cfg.MarshalToMsg(&newProxyMsg) err := pm.sendMsg(&newProxyMsg) if err != nil { pm.Warn("[%s] proxy send NewProxy message error") @@ -240,15 +240,16 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) { } for _, cfg := range pm.visitorCfgs { - if _, exist := pm.visitors[cfg.GetName()]; !exist { - pm.Info("try to start visitor [%s]", cfg.GetName()) + 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[cfg.GetName()] = visitor + pm.visitors[name] = visitor visitor.Info("start visitor success") } } diff --git a/client/service.go b/client/service.go index c5a2f1e4..5fbf33c7 100644 --- a/client/service.go +++ b/client/service.go @@ -15,6 +15,7 @@ package client import ( + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/utils/log" ) @@ -41,12 +42,12 @@ func (svr *Service) Run() error { return err } - if config.ClientCommonCfg.AdminPort != 0 { - err = svr.RunAdminServer(config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort) + if g.GlbClientCfg.AdminPort != 0 { + err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort) if err != nil { log.Warn("run admin server error: %v", err) } - log.Info("admin server listen on %s:%d", config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort) + log.Info("admin server listen on %s:%d", g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort) } <-svr.closedCh diff --git a/client/visitor.go b/client/visitor.go index fd182255..9c16c1d6 100644 --- a/client/visitor.go +++ b/client/visitor.go @@ -26,6 +26,7 @@ import ( "golang.org/x/net/ipv4" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" frpIo "github.com/fatedier/frp/utils/io" @@ -45,7 +46,7 @@ type Visitor interface { func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) { baseVisitor := BaseVisitor{ ctl: ctl, - Logger: log.NewPrefixLogger(pxyConf.GetName()), + Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName), } switch cfg := pxyConf.(type) { case *config.StcpProxyConf: @@ -193,13 +194,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) { defer userConn.Close() sv.Debug("get a new xtcp user connection") - if config.ClientCommonCfg.ServerUdpPort == 0 { + if g.GlbClientCfg.ServerUdpPort == 0 { sv.Error("xtcp is not supported by server") return } raddr, err := net.ResolveUDPAddr("udp", - fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort)) + fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort)) visitorConn, err := net.DialUDP("udp", nil, raddr) defer visitorConn.Close() diff --git a/cmd/frpc/main.go b/cmd/frpc/main.go index 845f4f74..1c6344b2 100644 --- a/cmd/frpc/main.go +++ b/cmd/frpc/main.go @@ -15,291 +15,9 @@ package main import ( - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "os" - "os/signal" - "strconv" - "strings" - "syscall" - "time" - - docopt "github.com/docopt/docopt-go" - "github.com/rodaine/table" - ini "github.com/vaughan0/go-ini" - - "github.com/fatedier/frp/client" - "github.com/fatedier/frp/models/config" - "github.com/fatedier/frp/utils/log" - "github.com/fatedier/frp/utils/version" + "github.com/fatedier/frp/cmd/frpc/sub" ) -var ( - configFile string = "./frpc.ini" -) - -var usage string = `frpc is the client of frp - -Usage: - frpc [-c config_file] [-L log_file] [--log-level=] [--server-addr=] - frpc reload [-c config_file] - frpc status [-c config_file] - frpc -h | --help - frpc -v | --version - -Options: - -c config_file set config file - -L log_file set output log file, including console - --log-level= set log level: debug, info, warn, error - --server-addr= addr which frps is listening for, example: 0.0.0.0:7000 - -h --help show this screen - -v --version show version -` - func main() { - var err error - confFile := "./frpc.ini" - // the configures parsed from file will be replaced by those from command line if exist - args, err := docopt.Parse(usage, nil, true, version.Full(), false) - - if args["-c"] != nil { - confFile = args["-c"].(string) - } - - conf, err := ini.LoadFile(confFile) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - config.ClientCommonCfg, err = config.LoadClientCommonConf(conf) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - config.ClientCommonCfg.ConfigFile = confFile - - // check if reload command - if args["reload"] != nil { - if args["reload"].(bool) { - if err = CmdReload(); err != nil { - fmt.Printf("frpc reload error: %v\n", err) - os.Exit(1) - } else { - fmt.Printf("reload success\n") - os.Exit(0) - } - } - } - - // check if status command - if args["status"] != nil { - if args["status"].(bool) { - if err = CmdStatus(); err != nil { - fmt.Printf("frpc get status error: %v\n", err) - os.Exit(1) - } else { - os.Exit(0) - } - } - } - - if args["-L"] != nil { - if args["-L"].(string) == "console" { - config.ClientCommonCfg.LogWay = "console" - } else { - config.ClientCommonCfg.LogWay = "file" - config.ClientCommonCfg.LogFile = args["-L"].(string) - } - } - - if args["--log-level"] != nil { - config.ClientCommonCfg.LogLevel = args["--log-level"].(string) - } - - if args["--server-addr"] != nil { - addr := strings.Split(args["--server-addr"].(string), ":") - if len(addr) != 2 { - fmt.Println("--server-addr format error: example 0.0.0.0:7000") - os.Exit(1) - } - serverPort, err := strconv.ParseInt(addr[1], 10, 64) - if err != nil { - fmt.Println("--server-addr format error, example 0.0.0.0:7000") - os.Exit(1) - } - config.ClientCommonCfg.ServerAddr = addr[0] - config.ClientCommonCfg.ServerPort = int(serverPort) - } - - if args["-v"] != nil { - if args["-v"].(bool) { - fmt.Println(version.Full()) - os.Exit(0) - } - } - - pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, config.ClientCommonCfg.Start) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - log.InitLog(config.ClientCommonCfg.LogWay, config.ClientCommonCfg.LogFile, - config.ClientCommonCfg.LogLevel, config.ClientCommonCfg.LogMaxDays) - - svr := client.NewService(pxyCfgs, visitorCfgs) - - // Capture the exit signal if we use kcp. - if config.ClientCommonCfg.Protocol == "kcp" { - go HandleSignal(svr) - } - - err = svr.Run() - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -func HandleSignal(svr *client.Service) { - ch := make(chan os.Signal) - signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) - <-ch - svr.Close() - time.Sleep(250 * time.Millisecond) - os.Exit(0) -} - -func CmdReload() error { - if config.ClientCommonCfg.AdminPort == 0 { - return fmt.Errorf("admin_port shoud be set if you want to use reload feature") - } - - req, err := http.NewRequest("GET", "http://"+ - config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/reload", nil) - if err != nil { - return err - } - - authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+ - config.ClientCommonCfg.AdminPwd)) - - 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() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - res := &client.GeneralResponse{} - err = json.Unmarshal(body, &res) - if err != nil { - return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body))) - } else if res.Code != 0 { - return fmt.Errorf(res.Msg) - } - } - return nil -} - -func CmdStatus() error { - if config.ClientCommonCfg.AdminPort == 0 { - return fmt.Errorf("admin_port shoud be set if you want to get proxy status") - } - - req, err := http.NewRequest("GET", "http://"+ - config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/status", nil) - if err != nil { - return err - } - - authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+ - config.ClientCommonCfg.AdminPwd)) - - 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() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - res := &client.StatusResp{} - err = json.Unmarshal(body, &res) - if err != nil { - return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body))) - } - - fmt.Println("Proxy Status...") - if len(res.Tcp) > 0 { - fmt.Printf("TCP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Tcp { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.Udp) > 0 { - fmt.Printf("UDP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Udp { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.Http) > 0 { - fmt.Printf("HTTP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Http { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.Https) > 0 { - fmt.Printf("HTTPS") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Https { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.Stcp) > 0 { - fmt.Printf("STCP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Stcp { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.Xtcp) > 0 { - fmt.Printf("XTCP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Xtcp { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - } - return nil + sub.Execute() } diff --git a/cmd/frpc/sub/http.go b/cmd/frpc/sub/http.go new file mode 100644 index 00000000..3f799720 --- /dev/null +++ b/cmd/frpc/sub/http.go @@ -0,0 +1,96 @@ +// 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 sub + +import ( + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/consts" +) + +func init() { + httpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + httpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + httpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") + httpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + httpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + httpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + + httpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") + httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + httpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") + httpCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain") + httpCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain") + httpCmd.PersistentFlags().StringVarP(&locations, "locations", "", "", "locations") + httpCmd.PersistentFlags().StringVarP(&httpUser, "http_user", "", "admin", "http auth user") + httpCmd.PersistentFlags().StringVarP(&httpPwd, "http_pwd", "", "admin", "http auth password") + httpCmd.PersistentFlags().StringVarP(&hostHeaderRewrite, "host_header_rewrite", "", "", "host header rewrite") + httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") + httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") + + rootCmd.AddCommand(httpCmd) +} + +var httpCmd = &cobra.Command{ + Use: "http", + Short: "Run frpc with a single http proxy", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeCmd, "") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + cfg := &config.HttpProxyConf{} + var prefix string + if user != "" { + prefix = user + "." + } + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.HttpProxy + cfg.LocalIp = localIp + cfg.LocalPort = localPort + cfg.CustomDomains = strings.Split(customDomains, ",") + cfg.SubDomain = subDomain + cfg.Locations = strings.Split(locations, ",") + cfg.HttpUser = httpUser + cfg.HttpPwd = httpPwd + cfg.HostHeaderRewrite = hostHeaderRewrite + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + + err = cfg.CheckForCli() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + proxyConfs := map[string]config.ProxyConf{ + cfg.ProxyName: cfg, + } + err = startService(proxyConfs, nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + }, +} diff --git a/cmd/frpc/sub/https.go b/cmd/frpc/sub/https.go new file mode 100644 index 00000000..5c0b0e74 --- /dev/null +++ b/cmd/frpc/sub/https.go @@ -0,0 +1,88 @@ +// 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 sub + +import ( + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/consts" +) + +func init() { + httpsCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + httpsCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + httpsCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") + httpsCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + httpsCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + httpsCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + + httpsCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") + httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + httpsCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") + httpsCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain") + httpsCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain") + httpsCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") + httpsCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") + + rootCmd.AddCommand(httpsCmd) +} + +var httpsCmd = &cobra.Command{ + Use: "https", + Short: "Run frpc with a single https proxy", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeCmd, "") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + cfg := &config.HttpsProxyConf{} + var prefix string + if user != "" { + prefix = user + "." + } + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.HttpsProxy + cfg.LocalIp = localIp + cfg.LocalPort = localPort + cfg.CustomDomains = strings.Split(customDomains, ",") + cfg.SubDomain = subDomain + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + + err = cfg.CheckForCli() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + proxyConfs := map[string]config.ProxyConf{ + cfg.ProxyName: cfg, + } + err = startService(proxyConfs, nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + }, +} diff --git a/cmd/frpc/sub/reload.go b/cmd/frpc/sub/reload.go new file mode 100644 index 00000000..1e6f2c47 --- /dev/null +++ b/cmd/frpc/sub/reload.go @@ -0,0 +1,92 @@ +// 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 sub + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/client" + "github.com/fatedier/frp/g" +) + +func init() { + rootCmd.AddCommand(reloadCmd) +} + +var reloadCmd = &cobra.Command{ + Use: "reload", + Short: "Hot-Reload frpc configuration", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeIni, cfgFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + err = reload() + if err != nil { + fmt.Printf("frpc reload error: %v\n", err) + os.Exit(1) + } + fmt.Printf("reload success\n") + return nil + }, +} + +func reload() error { + if g.GlbClientCfg.AdminPort == 0 { + return fmt.Errorf("admin_port shoud be set if you want to use reload feature") + } + + req, err := http.NewRequest("GET", "http://"+ + g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/reload", nil) + if err != nil { + return err + } + + authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+ + g.GlbClientCfg.AdminPwd)) + + 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() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + res := &client.GeneralResponse{} + err = json.Unmarshal(body, &res) + if err != nil { + return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body))) + } else if res.Code != 0 { + return fmt.Errorf(res.Msg) + } + } + return nil +} diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go new file mode 100644 index 00000000..bbb4c969 --- /dev/null +++ b/cmd/frpc/sub/root.go @@ -0,0 +1,200 @@ +// 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 sub + +import ( + "fmt" + "io/ioutil" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + "time" + + "github.com/spf13/cobra" + ini "github.com/vaughan0/go-ini" + + "github.com/fatedier/frp/client" + "github.com/fatedier/frp/g" + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/utils/log" + "github.com/fatedier/frp/utils/version" +) + +const ( + CfgFileTypeIni = iota + CfgFileTypeCmd +) + +var ( + cfgFile string + showVersion bool + + serverAddr string + user string + protocol string + token string + logLevel string + logFile string + logMaxDays int + + proxyName string + localIp string + localPort int + remotePort int + useEncryption bool + useCompression bool + customDomains string + subDomain string + httpUser string + httpPwd string + locations string + hostHeaderRewrite string + role string + sk string + serverName string + bindAddr string +) + +func init() { + rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "./frpc.ini", "config file of frpc") + rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") +} + +var rootCmd = &cobra.Command{ + Use: "frpc", + Short: "frpc is the client of frp (https://github.com/fatedier/frp)", + RunE: func(cmd *cobra.Command, args []string) error { + if showVersion { + fmt.Println(version.Full()) + return nil + } + + // Do not show command usage here. + err := runClient(cfgFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func handleSignal(svr *client.Service) { + ch := make(chan os.Signal) + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) + <-ch + svr.Close() + time.Sleep(250 * time.Millisecond) + os.Exit(0) +} + +func parseClientCommonCfg(fileType int, filePath string) (err error) { + if fileType == CfgFileTypeIni { + err = parseClientCommonCfgFromIni(filePath) + } else if fileType == CfgFileTypeCmd { + err = parseClientCommonCfgFromCmd() + } + if err != nil { + return + } + + g.GlbClientCfg.CfgFile = cfgFile + + err = g.GlbClientCfg.ClientCommonConf.Check() + if err != nil { + return + } + return +} + +func parseClientCommonCfgFromIni(filePath string) (err error) { + b, err := ioutil.ReadFile(filePath) + if err != nil { + return err + } + content := string(b) + + cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content) + if err != nil { + return err + } + g.GlbClientCfg.ClientCommonConf = *cfg + return +} + +func parseClientCommonCfgFromCmd() (err error) { + strs := strings.Split(serverAddr, ":") + if len(strs) < 2 { + err = fmt.Errorf("invalid server_addr") + return + } + if strs[0] != "" { + g.GlbClientCfg.ServerAddr = strs[0] + } + g.GlbClientCfg.ServerPort, err = strconv.Atoi(strs[1]) + if err != nil { + err = fmt.Errorf("invalid server_addr") + return + } + + g.GlbClientCfg.User = user + g.GlbClientCfg.Protocol = protocol + g.GlbClientCfg.Token = token + g.GlbClientCfg.LogLevel = logLevel + g.GlbClientCfg.LogFile = logFile + g.GlbClientCfg.LogMaxDays = int64(logMaxDays) + return nil +} + +func runClient(cfgFilePath string) (err error) { + err = parseClientCommonCfg(CfgFileTypeIni, cfgFilePath) + if err != nil { + return + } + + conf, err := ini.LoadFile(cfgFilePath) + if err != nil { + return err + } + + pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start) + if err != nil { + return err + } + + err = startService(pxyCfgs, visitorCfgs) + return +} + +func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) (err error) { + log.InitLog(g.GlbClientCfg.LogWay, g.GlbClientCfg.LogFile, g.GlbClientCfg.LogLevel, g.GlbClientCfg.LogMaxDays) + svr := client.NewService(pxyCfgs, visitorCfgs) + + // Capture the exit signal if we use kcp. + if g.GlbClientCfg.Protocol == "kcp" { + go handleSignal(svr) + } + + err = svr.Run() + return +} diff --git a/cmd/frpc/sub/status.go b/cmd/frpc/sub/status.go new file mode 100644 index 00000000..3a25a4bc --- /dev/null +++ b/cmd/frpc/sub/status.go @@ -0,0 +1,146 @@ +// 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 sub + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" + + "github.com/rodaine/table" + "github.com/spf13/cobra" + + "github.com/fatedier/frp/client" + "github.com/fatedier/frp/g" +) + +func init() { + rootCmd.AddCommand(statusCmd) +} + +var statusCmd = &cobra.Command{ + Use: "status", + Short: "Overview of all proxies status", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeIni, cfgFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + err = status() + if err != nil { + fmt.Printf("frpc get status error: %v\n", err) + os.Exit(1) + } + return nil + }, +} + +func status() error { + if g.GlbClientCfg.AdminPort == 0 { + return fmt.Errorf("admin_port shoud be set if you want to get proxy status") + } + + req, err := http.NewRequest("GET", "http://"+ + g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/status", nil) + if err != nil { + return err + } + + authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+ + g.GlbClientCfg.AdminPwd)) + + 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() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + res := &client.StatusResp{} + err = json.Unmarshal(body, &res) + if err != nil { + return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body))) + } + + fmt.Println("Proxy Status...") + if len(res.Tcp) > 0 { + fmt.Printf("TCP") + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range res.Tcp { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } + if len(res.Udp) > 0 { + fmt.Printf("UDP") + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range res.Udp { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } + if len(res.Http) > 0 { + fmt.Printf("HTTP") + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range res.Http { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } + if len(res.Https) > 0 { + fmt.Printf("HTTPS") + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range res.Https { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } + if len(res.Stcp) > 0 { + fmt.Printf("STCP") + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range res.Stcp { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } + if len(res.Xtcp) > 0 { + fmt.Printf("XTCP") + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range res.Xtcp { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } + } + return nil +} diff --git a/cmd/frpc/sub/stcp.go b/cmd/frpc/sub/stcp.go new file mode 100644 index 00000000..66585ad3 --- /dev/null +++ b/cmd/frpc/sub/stcp.go @@ -0,0 +1,102 @@ +// 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 sub + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/consts" +) + +func init() { + stcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + stcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + stcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") + stcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + stcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + stcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + + stcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") + stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") + stcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key") + stcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name") + stcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + stcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") + stcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr") + stcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") + stcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") + + rootCmd.AddCommand(stcpCmd) +} + +var stcpCmd = &cobra.Command{ + Use: "http", + Short: "Run frpc with a single http proxy", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeCmd, "") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + cfg := &config.StcpProxyConf{} + 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.UseEncryption = useEncryption + cfg.UseCompression = useCompression + + err = cfg.CheckForCli() + 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/tcp.go b/cmd/frpc/sub/tcp.go new file mode 100644 index 00000000..154db151 --- /dev/null +++ b/cmd/frpc/sub/tcp.go @@ -0,0 +1,85 @@ +// 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 sub + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/consts" +) + +func init() { + tcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + tcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + tcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") + tcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + tcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + tcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + + tcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") + tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + tcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") + tcpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port") + tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") + tcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") + + rootCmd.AddCommand(tcpCmd) +} + +var tcpCmd = &cobra.Command{ + Use: "tcp", + Short: "Run frpc with a single tcp proxy", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeCmd, "") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + cfg := &config.TcpProxyConf{} + var prefix string + if user != "" { + prefix = user + "." + } + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.TcpProxy + cfg.LocalIp = localIp + cfg.LocalPort = localPort + cfg.RemotePort = remotePort + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + + err = cfg.CheckForCli() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + proxyConfs := map[string]config.ProxyConf{ + cfg.ProxyName: cfg, + } + err = startService(proxyConfs, nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + }, +} diff --git a/cmd/frpc/sub/udp.go b/cmd/frpc/sub/udp.go new file mode 100644 index 00000000..bbe1936e --- /dev/null +++ b/cmd/frpc/sub/udp.go @@ -0,0 +1,85 @@ +// 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 sub + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/consts" +) + +func init() { + udpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + udpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + udpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") + udpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + udpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + udpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + + udpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") + udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + udpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") + udpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port") + udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") + udpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") + + rootCmd.AddCommand(udpCmd) +} + +var udpCmd = &cobra.Command{ + Use: "udp", + Short: "Run frpc with a single udp proxy", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeCmd, "") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + cfg := &config.UdpProxyConf{} + var prefix string + if user != "" { + prefix = user + "." + } + cfg.ProxyName = prefix + proxyName + cfg.ProxyType = consts.UdpProxy + cfg.LocalIp = localIp + cfg.LocalPort = localPort + cfg.RemotePort = remotePort + cfg.UseEncryption = useEncryption + cfg.UseCompression = useCompression + + err = cfg.CheckForCli() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + proxyConfs := map[string]config.ProxyConf{ + cfg.ProxyName: cfg, + } + err = startService(proxyConfs, nil) + 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 new file mode 100644 index 00000000..53bd1374 --- /dev/null +++ b/cmd/frpc/sub/xtcp.go @@ -0,0 +1,102 @@ +// 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 sub + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/models/consts" +) + +func init() { + xtcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + xtcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + xtcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") + xtcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + xtcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + xtcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + + xtcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") + xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") + xtcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key") + xtcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name") + xtcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + xtcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") + xtcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr") + xtcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") + xtcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") + + rootCmd.AddCommand(xtcpCmd) +} + +var xtcpCmd = &cobra.Command{ + Use: "http", + Short: "Run frpc with a single http proxy", + RunE: func(cmd *cobra.Command, args []string) error { + err := parseClientCommonCfg(CfgFileTypeCmd, "") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + cfg := &config.XtcpProxyConf{} + 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.UseEncryption = useEncryption + cfg.UseCompression = useCompression + + err = cfg.CheckForCli() + 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/frps/main.go b/cmd/frps/main.go index c3b495ad..842bf3ae 100644 --- a/cmd/frps/main.go +++ b/cmd/frps/main.go @@ -1,4 +1,4 @@ -// Copyright 2016 fatedier, fatedier@gmail.com +// 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. @@ -14,105 +14,6 @@ package main -import ( - "fmt" - "os" - "strconv" - "strings" - - docopt "github.com/docopt/docopt-go" - ini "github.com/vaughan0/go-ini" - - "github.com/fatedier/frp/models/config" - "github.com/fatedier/frp/server" - "github.com/fatedier/frp/utils/log" - "github.com/fatedier/frp/utils/version" -) - -var usage string = `frps is the server of frp - -Usage: - frps [-c config_file] [-L log_file] [--log-level=] [--addr=] - frps -h | --help - frps -v | --version - -Options: - -c config_file set config file - -L log_file set output log file, including console - --log-level= set log level: debug, info, warn, error - --addr= listen addr for client, example: 0.0.0.0:7000 - -h --help show this screen - -v --version show version -` - func main() { - var err error - confFile := "./frps.ini" - // the configures parsed from file will be replaced by those from command line if exist - args, err := docopt.Parse(usage, nil, true, version.Full(), false) - - if args["-c"] != nil { - confFile = args["-c"].(string) - } - - conf, err := ini.LoadFile(confFile) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - config.ServerCommonCfg, err = config.LoadServerCommonConf(conf) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if args["-L"] != nil { - if args["-L"].(string) == "console" { - config.ServerCommonCfg.LogWay = "console" - } else { - config.ServerCommonCfg.LogWay = "file" - config.ServerCommonCfg.LogFile = args["-L"].(string) - } - } - - if args["--log-level"] != nil { - config.ServerCommonCfg.LogLevel = args["--log-level"].(string) - } - - if args["--addr"] != nil { - addr := strings.Split(args["--addr"].(string), ":") - if len(addr) != 2 { - fmt.Println("--addr format error: example 0.0.0.0:7000") - os.Exit(1) - } - bindPort, err := strconv.ParseInt(addr[1], 10, 64) - if err != nil { - fmt.Println("--addr format error, example 0.0.0.0:7000") - os.Exit(1) - } - config.ServerCommonCfg.BindAddr = addr[0] - config.ServerCommonCfg.BindPort = int(bindPort) - } - - if args["-v"] != nil { - if args["-v"].(bool) { - fmt.Println(version.Full()) - os.Exit(0) - } - } - - log.InitLog(config.ServerCommonCfg.LogWay, config.ServerCommonCfg.LogFile, - config.ServerCommonCfg.LogLevel, config.ServerCommonCfg.LogMaxDays) - - svr, err := server.NewService() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - log.Info("Start frps success") - if config.ServerCommonCfg.PrivilegeMode == true { - log.Info("PrivilegeMode is enabled, you should pay more attention to security issues") - } - server.ServerService = svr - svr.Run() + Execute() } diff --git a/cmd/frps/root.go b/cmd/frps/root.go new file mode 100644 index 00000000..44a35f31 --- /dev/null +++ b/cmd/frps/root.go @@ -0,0 +1,174 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/spf13/cobra" + + "github.com/fatedier/frp/g" + "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/server" + "github.com/fatedier/frp/utils/log" + "github.com/fatedier/frp/utils/version" +) + +const ( + CfgFileTypeIni = iota + CfgFileTypeCmd +) + +var ( + cfgFile string + showVersion bool + + bindAddr string + bindPort int + bindUdpPort int + kcpBindPort int + proxyBindAddr string + vhostHttpPort int + vhostHttpsPort int + dashboardAddr string + dashboardPort int + dashboardUser string + dashboardPwd string + assetsDir string + logFile string + logWay string + logLevel string + logMaxDays int64 + token string + authTimeout int64 + subDomainHost string + tcpMux bool + allowPorts string + maxPoolCount int64 + maxPortsPerClient int64 +) + +func init() { + rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "", "config file of frps") + rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") + + rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "h", "0.0.0.0", "bind address") + rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port") + rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port") + rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port") + rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address") + rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port") + rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port") + rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address") + rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port") + 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", "", "", "auth token") + rootCmd.PersistentFlags().Int64VarP(&authTimeout, "auth_timeout", "", 900, "auth timeout") + rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host") + rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client") +} + +var rootCmd = &cobra.Command{ + Use: "frps", + Short: "frps is the server of frp (https://github.com/fatedier/frp)", + RunE: func(cmd *cobra.Command, args []string) error { + if showVersion { + fmt.Println(version.Full()) + return nil + } + + if cfgFile != "" { + parseServerCommonCfg(CfgFileTypeIni, cfgFile) + } else { + parseServerCommonCfg(CfgFileTypeCmd, "") + } + + err := runServer() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func parseServerCommonCfg(fileType int, filePath string) (err error) { + if fileType == CfgFileTypeIni { + err = parseServerCommonCfgFromIni(filePath) + } else if fileType == CfgFileTypeCmd { + err = parseServerCommonCfgFromCmd() + } + if err != nil { + return + } + + g.GlbServerCfg.CfgFile = cfgFile + + err = g.GlbServerCfg.ServerCommonConf.Check() + if err != nil { + return + } + return +} + +func parseServerCommonCfgFromIni(filePath string) (err error) { + b, err := ioutil.ReadFile(filePath) + if err != nil { + return err + } + content := string(b) + + cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content) + if err != nil { + return err + } + g.GlbServerCfg.ServerCommonConf = *cfg + return +} + +func parseServerCommonCfgFromCmd() (err error) { + g.GlbServerCfg.BindAddr = bindAddr + g.GlbServerCfg.BindPort = bindPort + g.GlbServerCfg.BindUdpPort = bindUdpPort + g.GlbServerCfg.KcpBindPort = kcpBindPort + g.GlbServerCfg.ProxyBindAddr = proxyBindAddr + g.GlbServerCfg.VhostHttpPort = vhostHttpPort + g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort + g.GlbServerCfg.DashboardAddr = dashboardAddr + g.GlbServerCfg.DashboardPort = dashboardPort + 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 + g.GlbServerCfg.AuthTimeout = authTimeout + g.GlbServerCfg.SubDomainHost = subDomainHost + g.GlbServerCfg.MaxPortsPerClient = maxPortsPerClient + return +} + +func runServer() (err error) { + log.InitLog(g.GlbServerCfg.LogWay, g.GlbServerCfg.LogFile, g.GlbServerCfg.LogLevel, + g.GlbServerCfg.LogMaxDays) + svr, err := server.NewService() + if err != nil { + return err + } + log.Info("Start frps success") + server.ServerService = svr + svr.Run() + return +} diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 4bec160b..844ed0ed 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -7,7 +7,7 @@ server_port = 7000 # if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables # it only works when protocol is tcp -# http_proxy = http://user:pwd@192.168.1.128:8080 +# http_proxy = http://user:passwd@192.168.1.128:8080 # console or real logFile path like ./frpc.log log_file = ./frpc.log @@ -18,13 +18,13 @@ log_level = info log_max_days = 3 # for authentication -privilege_token = 12345678 +token = 12345678 # set admin address for control frpc's action by http api such as reload admin_addr = 127.0.0.1 admin_port = 7400 admin_user = admin -admin_pwd = admin +admin_passwd = admin # connections will be established in advance, default value is zero pool_count = 5 @@ -109,7 +109,7 @@ use_compression = true # http username and password are safety certification for http protocol # if not set, you can access this custom_domains without certification http_user = admin -http_pwd = admin +http_passwd = admin # if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com subdomain = web01 custom_domains = web02.yourdomain.com diff --git a/conf/frps_full.ini b/conf/frps_full.ini index e2a7c3f0..a808cc36 100644 --- a/conf/frps_full.ini +++ b/conf/frps_full.ini @@ -25,9 +25,9 @@ vhost_https_port = 443 dashboard_addr = 0.0.0.0 dashboard_port = 7500 -# dashboard user and pwd for basic auth protect, if not set, both default value is admin +# dashboard user and passwd for basic auth protect, if not set, both default value is admin dashboard_user = admin -dashboard_pwd = admin +dashboard_passwd = admin # dashboard assets directory(only for debug mode) # assets_dir = ./static @@ -39,8 +39,8 @@ log_level = info log_max_days = 3 -# privilege mode is the only supported mode since v0.10.0 -privilege_token = 12345678 +# auth token +token = 12345678 # heartbeat configure, it's not recommended to modify the default value # the default value of heartbeat_timeout is 90 diff --git a/g/g.go b/g/g.go new file mode 100644 index 00000000..3c7385f1 --- /dev/null +++ b/g/g.go @@ -0,0 +1,32 @@ +package g + +import ( + "github.com/fatedier/frp/models/config" +) + +var ( + GlbClientCfg *ClientCfg + GlbServerCfg *ServerCfg +) + +func init() { + GlbClientCfg = &ClientCfg{ + ClientCommonConf: *config.GetDefaultClientConf(), + } + GlbServerCfg = &ServerCfg{ + ServerCommonConf: *config.GetDefaultServerConf(), + } +} + +type ClientCfg struct { + config.ClientCommonConf + + CfgFile string + ServerUdpPort int // this is configured by login response from frps +} + +type ServerCfg struct { + config.ServerCommonConf + + CfgFile string +} diff --git a/models/config/client_common.go b/models/config/client_common.go index 4b9ede72..726352dd 100644 --- a/models/config/client_common.go +++ b/models/config/client_common.go @@ -23,46 +23,40 @@ import ( ini "github.com/vaughan0/go-ini" ) -var ClientCommonCfg *ClientCommonConf - // client common config type ClientCommonConf struct { - ConfigFile string - ServerAddr string - ServerPort int - ServerUdpPort int // this is specified by login response message from frps - HttpProxy string - LogFile string - LogWay string - LogLevel string - LogMaxDays int64 - PrivilegeToken string - AdminAddr string - AdminPort int - AdminUser string - AdminPwd string - PoolCount int - TcpMux bool - User string - LoginFailExit bool - Start map[string]struct{} - Protocol string - HeartBeatInterval int64 - HeartBeatTimeout int64 + ServerAddr string `json:"server_addr"` + ServerPort int `json:"server_port"` + HttpProxy string `json:"http_proxy"` + LogFile string `json:"log_file"` + LogWay string `json:"log_way"` + LogLevel string `json:"log_level"` + LogMaxDays int64 `json:"log_max_days"` + Token string `json:"token"` + AdminAddr string `json:"admin_addr"` + AdminPort int `json:"admin_port"` + AdminUser string `json:"admin_user"` + AdminPwd string `json:"admin_pwd"` + PoolCount int `json:"pool_count"` + TcpMux bool `json:"tcp_mux"` + User string `json:"user"` + LoginFailExit bool `json:"login_fail_exit"` + Start map[string]struct{} `json:"start"` + Protocol string `json:"protocol"` + HeartBeatInterval int64 `json:"heartbeat_interval"` + HeartBeatTimeout int64 `json:"heartbeat_timeout"` } -func GetDeaultClientCommonConf() *ClientCommonConf { +func GetDefaultClientConf() *ClientCommonConf { return &ClientCommonConf{ - ConfigFile: "./frpc.ini", ServerAddr: "0.0.0.0", ServerPort: 7000, - ServerUdpPort: 0, - HttpProxy: "", + HttpProxy: os.Getenv("http_proxy"), LogFile: "console", LogWay: "console", LogLevel: "info", LogMaxDays: 3, - PrivilegeToken: "", + Token: "", AdminAddr: "127.0.0.1", AdminPort: 0, AdminUser: "", @@ -78,21 +72,28 @@ func GetDeaultClientCommonConf() *ClientCommonConf { } } -func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) { +func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (cfg *ClientCommonConf, err error) { + cfg = defaultCfg + if cfg == nil { + cfg = GetDefaultClientConf() + } + + conf, err := ini.Load(strings.NewReader(content)) + if err != nil { + err = fmt.Errorf("parse ini conf file error: %v", err) + return nil, err + } + var ( tmpStr string ok bool v int64 ) - cfg = GetDeaultClientCommonConf() - - tmpStr, ok = conf.Get("common", "server_addr") - if ok { + if tmpStr, ok = conf.Get("common", "server_addr"); ok { cfg.ServerAddr = tmpStr } - tmpStr, ok = conf.Get("common", "server_port") - if ok { + if tmpStr, ok = conf.Get("common", "server_port"); ok { v, err = strconv.ParseInt(tmpStr, 10, 64) if err != nil { err = fmt.Errorf("Parse conf error: invalid server_port") @@ -101,16 +102,11 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) { cfg.ServerPort = int(v) } - tmpStr, ok = conf.Get("common", "http_proxy") - if ok { + if tmpStr, ok = conf.Get("common", "http_proxy"); ok { cfg.HttpProxy = tmpStr - } else { - // get http_proxy from env - cfg.HttpProxy = os.Getenv("http_proxy") } - tmpStr, ok = conf.Get("common", "log_file") - if ok { + if tmpStr, ok = conf.Get("common", "log_file"); ok { cfg.LogFile = tmpStr if cfg.LogFile == "console" { cfg.LogWay = "console" @@ -119,30 +115,25 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "log_level") - if ok { + if tmpStr, ok = conf.Get("common", "log_level"); ok { cfg.LogLevel = tmpStr } - tmpStr, ok = conf.Get("common", "log_max_days") - if ok { + if tmpStr, ok = conf.Get("common", "log_max_days"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil { cfg.LogMaxDays = v } } - tmpStr, ok = conf.Get("common", "privilege_token") - if ok { - cfg.PrivilegeToken = tmpStr + if tmpStr, ok = conf.Get("common", "token"); ok { + cfg.Token = tmpStr } - tmpStr, ok = conf.Get("common", "admin_addr") - if ok { + if tmpStr, ok = conf.Get("common", "admin_addr"); ok { cfg.AdminAddr = tmpStr } - tmpStr, ok = conf.Get("common", "admin_port") - if ok { + if tmpStr, ok = conf.Get("common", "admin_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil { cfg.AdminPort = int(v) } else { @@ -151,55 +142,44 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "admin_user") - if ok { + if tmpStr, ok = conf.Get("common", "admin_user"); ok { cfg.AdminUser = tmpStr } - tmpStr, ok = conf.Get("common", "admin_pwd") - if ok { + if tmpStr, ok = conf.Get("common", "admin_pwd"); ok { cfg.AdminPwd = tmpStr } - tmpStr, ok = conf.Get("common", "pool_count") - if ok { - v, err = strconv.ParseInt(tmpStr, 10, 64) - if err != nil { - cfg.PoolCount = 1 - } else { + if tmpStr, ok = conf.Get("common", "pool_count"); ok { + if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil { cfg.PoolCount = int(v) } } - tmpStr, ok = conf.Get("common", "tcp_mux") - if ok && tmpStr == "false" { + if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" { cfg.TcpMux = false } else { cfg.TcpMux = true } - tmpStr, ok = conf.Get("common", "user") - if ok { + if tmpStr, ok = conf.Get("common", "user"); ok { cfg.User = tmpStr } - tmpStr, ok = conf.Get("common", "start") - if ok { + if tmpStr, ok = conf.Get("common", "start"); ok { proxyNames := strings.Split(tmpStr, ",") for _, name := range proxyNames { cfg.Start[strings.TrimSpace(name)] = struct{}{} } } - tmpStr, ok = conf.Get("common", "login_fail_exit") - if ok && tmpStr == "false" { + if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" { cfg.LoginFailExit = false } else { cfg.LoginFailExit = true } - tmpStr, ok = conf.Get("common", "protocol") - if ok { + if tmpStr, ok = conf.Get("common", "protocol"); ok { // Now it only support tcp and kcp. if tmpStr != "kcp" { tmpStr = "tcp" @@ -207,10 +187,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) { cfg.Protocol = tmpStr } - tmpStr, ok = conf.Get("common", "heartbeat_timeout") - if ok { - v, err = strconv.ParseInt(tmpStr, 10, 64) - if err != nil { + if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok { + if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout") return } else { @@ -218,17 +196,18 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "heartbeat_interval") - if ok { - v, err = strconv.ParseInt(tmpStr, 10, 64) - if err != nil { + if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok { + if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid heartbeat_interval") return } else { cfg.HeartBeatInterval = v } } + return +} +func (cfg *ClientCommonConf) Check() (err error) { if cfg.HeartBeatInterval <= 0 { err = fmt.Errorf("Parse conf error: invalid heartbeat_interval") return diff --git a/models/config/proxy.go b/models/config/proxy.go index 49835c3f..e6022b68 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -27,7 +27,9 @@ import ( ini "github.com/vaughan0/go-ini" ) -var proxyConfTypeMap map[string]reflect.Type +var ( + proxyConfTypeMap map[string]reflect.Type +) func init() { proxyConfTypeMap = make(map[string]reflect.Type) @@ -51,17 +53,16 @@ func NewConfByType(proxyType string) ProxyConf { } type ProxyConf interface { - GetName() string - GetType() string GetBaseInfo() *BaseProxyConf - LoadFromMsg(pMsg *msg.NewProxy) - LoadFromFile(name string, conf ini.Section) error - UnMarshalToMsg(pMsg *msg.NewProxy) - Check() error + UnmarshalFromMsg(pMsg *msg.NewProxy) + UnmarshalFromIni(prefix string, name string, conf ini.Section) error + MarshalToMsg(pMsg *msg.NewProxy) + CheckForCli() error + CheckForSvr() error Compare(conf ProxyConf) bool } -func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) { +func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) { if pMsg.ProxyType == "" { pMsg.ProxyType = consts.TcpProxy } @@ -71,12 +72,12 @@ func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) { err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType) return } - cfg.LoadFromMsg(pMsg) - err = cfg.Check() + cfg.UnmarshalFromMsg(pMsg) + err = cfg.CheckForSvr() return } -func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err error) { +func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg ProxyConf, err error) { proxyType := section["type"] if proxyType == "" { proxyType = consts.TcpProxy @@ -87,7 +88,10 @@ func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType) return } - err = cfg.LoadFromFile(name, section) + if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil { + return + } + err = cfg.CheckForCli() return } @@ -100,14 +104,6 @@ type BaseProxyConf struct { UseCompression bool `json:"use_compression"` } -func (cfg *BaseProxyConf) GetName() string { - return cfg.ProxyName -} - -func (cfg *BaseProxyConf) GetType() string { - return cfg.ProxyType -} - func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf { return cfg } @@ -122,23 +118,19 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool { return true } -func (cfg *BaseProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { +func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.ProxyName = pMsg.ProxyName cfg.ProxyType = pMsg.ProxyType cfg.UseEncryption = pMsg.UseEncryption cfg.UseCompression = pMsg.UseCompression } -func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error { +func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) error { var ( tmpStr string ok bool ) - if ClientCommonCfg.User != "" { - cfg.ProxyName = ClientCommonCfg.User + "." + name - } else { - cfg.ProxyName = name - } + cfg.ProxyName = prefix + name cfg.ProxyType = section["type"] tmpStr, ok = section["use_encryption"] @@ -153,7 +145,7 @@ func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error { return nil } -func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { pMsg.ProxyName = cfg.ProxyName pMsg.ProxyType = cfg.ProxyType pMsg.UseEncryption = cfg.UseEncryption @@ -162,24 +154,21 @@ func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { // Bind info type BindInfoConf struct { - BindAddr string `json:"bind_addr"` - RemotePort int `json:"remote_port"` + RemotePort int `json:"remote_port"` } func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool { - if cfg.BindAddr != cmp.BindAddr || - cfg.RemotePort != cmp.RemotePort { + if cfg.RemotePort != cmp.RemotePort { return false } return true } -func (cfg *BindInfoConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BindAddr = ServerCommonCfg.ProxyBindAddr +func (cfg *BindInfoConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.RemotePort = pMsg.RemotePort } -func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err error) { +func (cfg *BindInfoConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { var ( tmpStr string ok bool @@ -197,14 +186,10 @@ func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err err return nil } -func (cfg *BindInfoConf) UnMarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *BindInfoConf) MarshalToMsg(pMsg *msg.NewProxy) { pMsg.RemotePort = cfg.RemotePort } -func (cfg *BindInfoConf) check() (err error) { - return nil -} - // Domain info type DomainConf struct { CustomDomains []string `json:"custom_domains"` @@ -219,12 +204,12 @@ func (cfg *DomainConf) compare(cmp *DomainConf) bool { return true } -func (cfg *DomainConf) LoadFromMsg(pMsg *msg.NewProxy) { +func (cfg *DomainConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.CustomDomains = pMsg.CustomDomains cfg.SubDomain = pMsg.SubDomain } -func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error) { +func (cfg *DomainConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { var ( tmpStr string ok bool @@ -239,42 +224,60 @@ func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error if tmpStr, ok = section["subdomain"]; ok { cfg.SubDomain = tmpStr } - - if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" { - return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them", name) - } return } -func (cfg *DomainConf) UnMarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *DomainConf) MarshalToMsg(pMsg *msg.NewProxy) { pMsg.CustomDomains = cfg.CustomDomains pMsg.SubDomain = cfg.SubDomain } func (cfg *DomainConf) check() (err error) { + if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" { + err = fmt.Errorf("custom_domains and subdomain should set at least one of them") + return + } + return +} + +func (cfg *DomainConf) checkForCli() (err error) { + if err = cfg.check(); err != nil { + return + } + return +} + +func (cfg *DomainConf) checkForSvr() (err error) { + if err = cfg.check(); err != nil { + return + } + for _, domain := range cfg.CustomDomains { - if ServerCommonCfg.SubDomainHost != "" && len(strings.Split(ServerCommonCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) { - if strings.Contains(domain, ServerCommonCfg.SubDomainHost) { - return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, ServerCommonCfg.SubDomainHost) + if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) { + if strings.Contains(domain, subDomainHost) { + return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost) } } } if cfg.SubDomain != "" { - if ServerCommonCfg.SubDomainHost == "" { + if subDomainHost == "" { return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps") } if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") { return fmt.Errorf("'.' and '*' is not supported in subdomain") } } - return nil + return } // Local service info type LocalSvrConf struct { - LocalIp string `json:"-"` - LocalPort int `json:"-"` + LocalIp string `json:"local_ip"` + LocalPort int `json:"local_port"` + + Plugin string `json:"plugin"` + PluginParams map[string]string `json:"plugin_params"` } func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool { @@ -282,30 +285,6 @@ func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool { cfg.LocalPort != cmp.LocalPort { return false } - return true -} - -func (cfg *LocalSvrConf) LoadFromFile(name string, section ini.Section) (err error) { - if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" { - cfg.LocalIp = "127.0.0.1" - } - - if tmpStr, ok := section["local_port"]; ok { - if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil { - return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name) - } - } else { - return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name) - } - return nil -} - -type PluginConf struct { - Plugin string `json:"-"` - PluginParams map[string]string `json:"-"` -} - -func (cfg *PluginConf) compare(cmp *PluginConf) bool { if cfg.Plugin != cmp.Plugin || len(cfg.PluginParams) != len(cmp.PluginParams) { return false @@ -319,7 +298,7 @@ func (cfg *PluginConf) compare(cmp *PluginConf) bool { return true } -func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error) { +func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { cfg.Plugin = section["plugin"] cfg.PluginParams = make(map[string]string) if cfg.Plugin != "" { @@ -330,7 +309,17 @@ func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error } } } else { - return fmt.Errorf("Parse conf error: proxy [%s] no plugin info found", name) + if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" { + cfg.LocalIp = "127.0.0.1" + } + + if tmpStr, ok := section["local_port"]; ok { + if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil { + return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name) + } + } else { + return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name) + } } return } @@ -341,7 +330,6 @@ type TcpProxyConf struct { BindInfoConf LocalSvrConf - PluginConf } func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool { @@ -352,43 +340,38 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || - !cfg.PluginConf.compare(&cmpConf.PluginConf) { + !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) { return false } return true } -func (cfg *TcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.LoadFromMsg(pMsg) - cfg.BindInfoConf.LoadFromMsg(pMsg) +func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) + cfg.BindInfoConf.UnmarshalFromMsg(pMsg) } -func (cfg *TcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) { - if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil { +func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil { + if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - - if err = cfg.PluginConf.LoadFromFile(name, section); err != nil { - if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } return } -func (cfg *TcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.UnMarshalToMsg(pMsg) - cfg.BindInfoConf.UnMarshalToMsg(pMsg) +func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.MarshalToMsg(pMsg) + cfg.BindInfoConf.MarshalToMsg(pMsg) } -func (cfg *TcpProxyConf) Check() (err error) { - err = cfg.BindInfoConf.check() - return -} +func (cfg *TcpProxyConf) CheckForCli() error { return nil } + +func (cfg *TcpProxyConf) CheckForSvr() error { return nil } // UDP type UdpProxyConf struct { @@ -412,33 +395,32 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *UdpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.LoadFromMsg(pMsg) - cfg.BindInfoConf.LoadFromMsg(pMsg) +func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) + cfg.BindInfoConf.UnmarshalFromMsg(pMsg) } -func (cfg *UdpProxyConf) LoadFromFile(name string, section ini.Section) (err error) { - if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil { +func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil { + if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil { + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { return } return } -func (cfg *UdpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.UnMarshalToMsg(pMsg) - cfg.BindInfoConf.UnMarshalToMsg(pMsg) +func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.MarshalToMsg(pMsg) + cfg.BindInfoConf.MarshalToMsg(pMsg) } -func (cfg *UdpProxyConf) Check() (err error) { - err = cfg.BindInfoConf.check() - return -} +func (cfg *UdpProxyConf) CheckForCli() error { return nil } + +func (cfg *UdpProxyConf) CheckForSvr() error { return nil } // HTTP type HttpProxyConf struct { @@ -446,12 +428,11 @@ type HttpProxyConf struct { DomainConf LocalSvrConf - PluginConf Locations []string `json:"locations"` HostHeaderRewrite string `json:"host_header_rewrite"` - HttpUser string `json:"-"` - HttpPwd string `json:"-"` + HttpUser string `json:"http_user"` + HttpPwd string `json:"http_pwd"` } func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool { @@ -463,7 +444,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.DomainConf.compare(&cmpConf.DomainConf) || !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || - !cfg.PluginConf.compare(&cmpConf.PluginConf) || strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") || cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite || cfg.HttpUser != cmpConf.HttpUser || @@ -473,9 +453,9 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.LoadFromMsg(pMsg) - cfg.DomainConf.LoadFromMsg(pMsg) +func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) + cfg.DomainConf.UnmarshalFromMsg(pMsg) cfg.Locations = pMsg.Locations cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite @@ -483,17 +463,15 @@ func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { cfg.HttpPwd = pMsg.HttpPwd } -func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err error) { - if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil { +func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.DomainConf.LoadFromFile(name, section); err != nil { + if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.PluginConf.LoadFromFile(name, section); err != nil { - if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } var ( @@ -512,9 +490,9 @@ func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err er return } -func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.UnMarshalToMsg(pMsg) - cfg.DomainConf.UnMarshalToMsg(pMsg) +func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.MarshalToMsg(pMsg) + cfg.DomainConf.MarshalToMsg(pMsg) pMsg.Locations = cfg.Locations pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite @@ -522,11 +500,20 @@ func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { pMsg.HttpPwd = cfg.HttpPwd } -func (cfg *HttpProxyConf) Check() (err error) { - if ServerCommonCfg.VhostHttpPort == 0 { - return fmt.Errorf("type [http] not support when vhost_http_port is not set") +func (cfg *HttpProxyConf) CheckForCli() (err error) { + if err = cfg.DomainConf.checkForCli(); err != nil { + return + } + return +} + +func (cfg *HttpProxyConf) CheckForSvr() (err error) { + if vhostHttpPort == 0 { + err = fmt.Errorf("type [http] not support when vhost_http_port is not set") + } + if err = cfg.DomainConf.checkForSvr(); err != nil { + return } - err = cfg.DomainConf.check() return } @@ -536,7 +523,6 @@ type HttpsProxyConf struct { DomainConf LocalSvrConf - PluginConf } func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool { @@ -547,43 +533,49 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.DomainConf.compare(&cmpConf.DomainConf) || - !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || - !cfg.PluginConf.compare(&cmpConf.PluginConf) { + !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) { return false } return true } -func (cfg *HttpsProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.LoadFromMsg(pMsg) - cfg.DomainConf.LoadFromMsg(pMsg) +func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) + cfg.DomainConf.UnmarshalFromMsg(pMsg) } -func (cfg *HttpsProxyConf) LoadFromFile(name string, section ini.Section) (err error) { - if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil { +func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.DomainConf.LoadFromFile(name, section); err != nil { + if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil { return } - if err = cfg.PluginConf.LoadFromFile(name, section); err != nil { - if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } return } -func (cfg *HttpsProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.UnMarshalToMsg(pMsg) - cfg.DomainConf.UnMarshalToMsg(pMsg) +func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.MarshalToMsg(pMsg) + cfg.DomainConf.MarshalToMsg(pMsg) } -func (cfg *HttpsProxyConf) Check() (err error) { - if ServerCommonCfg.VhostHttpsPort == 0 { +func (cfg *HttpsProxyConf) CheckForCli() (err error) { + if err = cfg.DomainConf.checkForCli(); err != nil { + return + } + return +} + +func (cfg *HttpsProxyConf) CheckForSvr() (err error) { + if vhostHttpsPort == 0 { return fmt.Errorf("type [https] not support when vhost_https_port is not set") } - err = cfg.DomainConf.check() + if err = cfg.DomainConf.checkForSvr(); err != nil { + return + } return } @@ -596,7 +588,6 @@ type StcpProxyConf struct { // used in role server LocalSvrConf - PluginConf // used in role visitor ServerName string `json:"server_name"` @@ -612,7 +603,6 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || - !cfg.PluginConf.compare(&cmpConf.PluginConf) || cfg.Role != cmpConf.Role || cfg.Sk != cmpConf.Sk || cfg.ServerName != cmpConf.ServerName || @@ -624,13 +614,13 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool { } // Only for role server. -func (cfg *StcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.LoadFromMsg(pMsg) +func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.Sk = pMsg.Sk } -func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) { - if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil { +func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -661,21 +651,33 @@ func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err er return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name) } } else { - if err = cfg.PluginConf.LoadFromFile(name, section); err != nil { - if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } } return } -func (cfg *StcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.UnMarshalToMsg(pMsg) +func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.MarshalToMsg(pMsg) pMsg.Sk = cfg.Sk } -func (cfg *StcpProxyConf) Check() (err error) { +func (cfg *StcpProxyConf) CheckForCli() (err error) { + if cfg.Role != "server" && cfg.Role != "visitor" { + err = fmt.Errorf("role should be 'server' or 'visitor'") + return + } + if cfg.Role == "visitor" { + if cfg.BindAddr == "" { + err = fmt.Errorf("bind_addr shouldn't be empty") + return + } + } + return +} + +func (cfg *StcpProxyConf) CheckForSvr() (err error) { return } @@ -688,7 +690,6 @@ type XtcpProxyConf struct { // used in role server LocalSvrConf - PluginConf // used in role visitor ServerName string `json:"server_name"` @@ -704,7 +705,6 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool { if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) || !cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) || - !cfg.PluginConf.compare(&cmpConf.PluginConf) || cfg.Role != cmpConf.Role || cfg.Sk != cmpConf.Sk || cfg.ServerName != cmpConf.ServerName || @@ -716,13 +716,13 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool { } // Only for role server. -func (cfg *XtcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.LoadFromMsg(pMsg) +func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.Sk = pMsg.Sk } -func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) { - if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil { +func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { + if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -753,21 +753,33 @@ func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err er return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name) } } else { - if err = cfg.PluginConf.LoadFromFile(name, section); err != nil { - if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil { - return - } + if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil { + return } } return } -func (cfg *XtcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) { - cfg.BaseProxyConf.UnMarshalToMsg(pMsg) +func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { + cfg.BaseProxyConf.MarshalToMsg(pMsg) pMsg.Sk = cfg.Sk } -func (cfg *XtcpProxyConf) Check() (err error) { +func (cfg *XtcpProxyConf) CheckForCli() (err error) { + if cfg.Role != "server" && cfg.Role != "visitor" { + err = fmt.Errorf("role should be 'server' or 'visitor'") + return + } + if cfg.Role == "visitor" { + if cfg.BindAddr == "" { + err = fmt.Errorf("bind_addr shouldn't be empty") + return + } + } + return +} + +func (cfg *XtcpProxyConf) CheckForSvr() (err error) { return } @@ -805,7 +817,7 @@ 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 LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]struct{}) ( +func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) ( proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) { if prefix != "" { @@ -842,9 +854,7 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s } for subName, subSection := range subSections { - // some proxy or visotr configure may be used this prefix - subSection["prefix"] = prefix - cfg, err := NewProxyConfFromFile(subName, subSection) + cfg, err := NewProxyConfFromIni(prefix, subName, subSection) if err != nil { return proxyConfs, visitorConfs, err } diff --git a/models/config/server_common.go b/models/config/server_common.go index 51880a70..20b37d8a 100644 --- a/models/config/server_common.go +++ b/models/config/server_common.go @@ -24,49 +24,59 @@ import ( "github.com/fatedier/frp/utils/util" ) -var ServerCommonCfg *ServerCommonConf +var ( + // server global configure used for generate proxy conf used in frps + proxyBindAddr string + subDomainHost string + vhostHttpPort int + vhostHttpsPort int +) + +func InitServerCfg(cfg *ServerCommonConf) { + proxyBindAddr = cfg.ProxyBindAddr + subDomainHost = cfg.SubDomainHost + vhostHttpPort = cfg.VhostHttpPort + vhostHttpsPort = cfg.VhostHttpsPort +} // common config type ServerCommonConf struct { - ConfigFile string - BindAddr string - BindPort int - BindUdpPort int - KcpBindPort int - ProxyBindAddr string + BindAddr string `json:"bind_addr"` + BindPort int `json:"bind_port"` + BindUdpPort int `json:"bind_udp_port"` + KcpBindPort int `json:"kcp_bind_port"` + ProxyBindAddr string `json:"proxy_bind_addr"` // If VhostHttpPort equals 0, don't listen a public port for http protocol. - VhostHttpPort int + VhostHttpPort int `json:"vhost_http_port"` // if VhostHttpsPort equals 0, don't listen a public port for https protocol - VhostHttpsPort int - DashboardAddr string + VhostHttpsPort int `json:"vhost_http_port"` + DashboardAddr string `json:"dashboard_addr"` // if DashboardPort equals 0, dashboard is not available - DashboardPort int - DashboardUser string - DashboardPwd string - AssetsDir string - LogFile string - LogWay string // console or file - LogLevel string - LogMaxDays int64 - PrivilegeMode bool - PrivilegeToken string - AuthTimeout int64 - SubDomainHost string - TcpMux bool + DashboardPort int `json:"dashboard_port"` + DashboardUser string `json:"dashboard_user"` + DashboardPwd string `json:"dashboard_pwd"` + AssetsDir string `json:"asserts_dir"` + LogFile string `json:"log_file"` + LogWay string `json:"log_way"` // console or file + LogLevel string `json:"log_level"` + LogMaxDays int64 `json:"log_max_days"` + Token string `json:"token"` + AuthTimeout int64 `json:"auth_timeout"` + SubDomainHost string `json:"subdomain_host"` + TcpMux bool `json:"tcp_mux"` PrivilegeAllowPorts map[int]struct{} - MaxPoolCount int64 - MaxPortsPerClient int64 - HeartBeatTimeout int64 - UserConnTimeout int64 + MaxPoolCount int64 `json:"max_pool_count"` + MaxPortsPerClient int64 `json:"max_ports_per_client"` + HeartBeatTimeout int64 `json:"heart_beat_timeout"` + UserConnTimeout int64 `json:"user_conn_timeout"` } -func GetDefaultServerCommonConf() *ServerCommonConf { +func GetDefaultServerConf() *ServerCommonConf { return &ServerCommonConf{ - ConfigFile: "./frps.ini", BindAddr: "0.0.0.0", BindPort: 7000, BindUdpPort: 0, @@ -83,8 +93,7 @@ func GetDefaultServerCommonConf() *ServerCommonConf { LogWay: "console", LogLevel: "info", LogMaxDays: 3, - PrivilegeMode: true, - PrivilegeToken: "", + Token: "", AuthTimeout: 900, SubDomainHost: "", TcpMux: true, @@ -96,22 +105,28 @@ func GetDefaultServerCommonConf() *ServerCommonConf { } } -// Load server common configure. -func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { +func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) { + cfg = defaultCfg + if cfg == nil { + cfg = GetDefaultServerConf() + } + + conf, err := ini.Load(strings.NewReader(content)) + if err != nil { + err = fmt.Errorf("parse ini conf file error: %v", err) + return nil, err + } + var ( tmpStr string ok bool v int64 ) - cfg = GetDefaultServerCommonConf() - - tmpStr, ok = conf.Get("common", "bind_addr") - if ok { + if tmpStr, ok = conf.Get("common", "bind_addr"); ok { cfg.BindAddr = tmpStr } - tmpStr, ok = conf.Get("common", "bind_port") - if ok { + if tmpStr, ok = conf.Get("common", "bind_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid bind_port") return @@ -120,8 +135,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "bind_udp_port") - if ok { + if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid bind_udp_port") return @@ -130,8 +144,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "kcp_bind_port") - if ok { + if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid kcp_bind_port") return @@ -140,15 +153,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "proxy_bind_addr") - if ok { + if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok { cfg.ProxyBindAddr = tmpStr } else { cfg.ProxyBindAddr = cfg.BindAddr } - tmpStr, ok = conf.Get("common", "vhost_http_port") - if ok { + if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid vhost_http_port") return @@ -159,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { cfg.VhostHttpPort = 0 } - tmpStr, ok = conf.Get("common", "vhost_https_port") - if ok { + if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid vhost_https_port") return @@ -171,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { cfg.VhostHttpsPort = 0 } - tmpStr, ok = conf.Get("common", "dashboard_addr") - if ok { + if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok { cfg.DashboardAddr = tmpStr } else { cfg.DashboardAddr = cfg.BindAddr } - tmpStr, ok = conf.Get("common", "dashboard_port") - if ok { + if tmpStr, ok = conf.Get("common", "dashboard_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid dashboard_port") return @@ -190,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { cfg.DashboardPort = 0 } - tmpStr, ok = conf.Get("common", "dashboard_user") - if ok { + if tmpStr, ok = conf.Get("common", "dashboard_user"); ok { cfg.DashboardUser = tmpStr } - tmpStr, ok = conf.Get("common", "dashboard_pwd") - if ok { + if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok { cfg.DashboardPwd = tmpStr } - tmpStr, ok = conf.Get("common", "assets_dir") - if ok { + if tmpStr, ok = conf.Get("common", "assets_dir"); ok { cfg.AssetsDir = tmpStr } - tmpStr, ok = conf.Get("common", "log_file") - if ok { + if tmpStr, ok = conf.Get("common", "log_file"); ok { cfg.LogFile = tmpStr if cfg.LogFile == "console" { cfg.LogWay = "console" @@ -215,47 +219,33 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "log_level") - if ok { + if tmpStr, ok = conf.Get("common", "log_level"); ok { cfg.LogLevel = tmpStr } - tmpStr, ok = conf.Get("common", "log_max_days") - if ok { + if tmpStr, ok = conf.Get("common", "log_max_days"); ok { v, err = strconv.ParseInt(tmpStr, 10, 64) if err == nil { cfg.LogMaxDays = v } } - tmpStr, ok = conf.Get("common", "privilege_mode") - if ok { - if tmpStr == "true" { - cfg.PrivilegeMode = true + cfg.Token, _ = conf.Get("common", "token") + + if allowPortsStr, ok := conf.Get("common", "privilege_allow_ports"); ok { + // e.g. 1000-2000,2001,2002,3000-4000 + ports, errRet := util.ParseRangeNumbers(allowPortsStr) + if errRet != nil { + err = fmt.Errorf("Parse conf error: privilege_allow_ports: %v", errRet) + return + } + + for _, port := range ports { + cfg.PrivilegeAllowPorts[int(port)] = struct{}{} } } - // PrivilegeMode configure - if cfg.PrivilegeMode == true { - cfg.PrivilegeToken, _ = conf.Get("common", "privilege_token") - - allowPortsStr, ok := conf.Get("common", "privilege_allow_ports") - if ok { - // e.g. 1000-2000,2001,2002,3000-4000 - ports, errRet := util.ParseRangeNumbers(allowPortsStr) - if errRet != nil { - err = fmt.Errorf("Parse conf error: privilege_allow_ports: %v", errRet) - return - } - - for _, port := range ports { - cfg.PrivilegeAllowPorts[int(port)] = struct{}{} - } - } - } - - tmpStr, ok = conf.Get("common", "max_pool_count") - if ok { + if tmpStr, ok = conf.Get("common", "max_pool_count"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid max_pool_count") return @@ -268,8 +258,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "max_ports_per_client") - if ok { + if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid max_ports_per_client") return @@ -282,8 +271,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "authentication_timeout") - if ok { + if tmpStr, ok = conf.Get("common", "authentication_timeout"); ok { v, errRet := strconv.ParseInt(tmpStr, 10, 64) if errRet != nil { err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect") @@ -293,20 +281,17 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } } - tmpStr, ok = conf.Get("common", "subdomain_host") - if ok { + if tmpStr, ok = conf.Get("common", "subdomain_host"); ok { cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr)) } - tmpStr, ok = conf.Get("common", "tcp_mux") - if ok && tmpStr == "false" { + if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" { cfg.TcpMux = false } else { cfg.TcpMux = true } - tmpStr, ok = conf.Get("common", "heartbeat_timeout") - if ok { + if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok { v, errRet := strconv.ParseInt(tmpStr, 10, 64) if errRet != nil { err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect") @@ -317,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { } return } + +func (cfg *ServerCommonConf) Check() (err error) { + return +} diff --git a/server/control.go b/server/control.go index d7938e7a..0739f951 100644 --- a/server/control.go +++ b/server/control.go @@ -20,6 +20,7 @@ import ( "sync" "time" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/consts" "github.com/fatedier/frp/models/msg" @@ -103,7 +104,7 @@ func (ctl *Control) Start() { loginRespMsg := &msg.LoginResp{ Version: version.Full(), RunId: ctl.runId, - ServerUdpPort: config.ServerCommonCfg.BindUdpPort, + ServerUdpPort: g.GlbServerCfg.BindUdpPort, Error: "", } msg.WriteMsg(ctl.conn, loginRespMsg) @@ -172,7 +173,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) { return } - case <-time.After(time.Duration(config.ServerCommonCfg.UserConnTimeout) * time.Second): + case <-time.After(time.Duration(g.GlbServerCfg.UserConnTimeout) * time.Second): err = fmt.Errorf("timeout trying to get work connection") ctl.conn.Warn("%v", err) return @@ -202,7 +203,7 @@ func (ctl *Control) writer() { defer ctl.allShutdown.Start() defer ctl.writerShutdown.Done() - encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken)) + encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbServerCfg.Token)) if err != nil { ctl.conn.Error("crypto new writer error: %v", err) ctl.allShutdown.Start() @@ -231,7 +232,7 @@ func (ctl *Control) reader() { defer ctl.allShutdown.Start() defer ctl.readerShutdown.Done() - encReader := crypto.NewReader(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken)) + encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token)) for { if m, err := msg.ReadMsg(encReader); err != nil { if err == io.EOF { @@ -301,7 +302,7 @@ func (ctl *Control) manager() { for { select { case <-heartbeat.C: - if time.Since(ctl.lastPing) > time.Duration(config.ServerCommonCfg.HeartBeatTimeout)*time.Second { + if time.Since(ctl.lastPing) > time.Duration(g.GlbServerCfg.HeartBeatTimeout)*time.Second { ctl.conn.Warn("heartbeat timeout") ctl.allShutdown.Start() return @@ -342,7 +343,7 @@ func (ctl *Control) manager() { func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) { var pxyConf config.ProxyConf // Load configures from NewProxy message and check. - pxyConf, err = config.NewProxyConf(pxyMsg) + pxyConf, err = config.NewProxyConfFromMsg(pxyMsg) if err != nil { return } @@ -355,9 +356,9 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err } // Check ports used number in each client - if config.ServerCommonCfg.MaxPortsPerClient > 0 { + if g.GlbServerCfg.MaxPortsPerClient > 0 { ctl.mu.Lock() - if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(config.ServerCommonCfg.MaxPortsPerClient) { + if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) { ctl.mu.Unlock() err = fmt.Errorf("exceed the max_ports_per_client") return @@ -404,7 +405,7 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) { return } - if config.ServerCommonCfg.MaxPortsPerClient > 0 { + if g.GlbServerCfg.MaxPortsPerClient > 0 { ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum() } pxy.Close() diff --git a/server/dashboard.go b/server/dashboard.go index 3c77875c..75fd5d75 100644 --- a/server/dashboard.go +++ b/server/dashboard.go @@ -21,7 +21,7 @@ import ( "time" "github.com/fatedier/frp/assets" - "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/g" frpNet "github.com/fatedier/frp/utils/net" "github.com/julienschmidt/httprouter" @@ -36,7 +36,7 @@ func RunDashboardServer(addr string, port int) (err error) { // url router router := httprouter.New() - user, passwd := config.ServerCommonCfg.DashboardUser, config.ServerCommonCfg.DashboardPwd + user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd // api, see dashboard_api.go router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd)) diff --git a/server/dashboard_api.go b/server/dashboard_api.go index 3f9acd0f..99aa62d1 100644 --- a/server/dashboard_api.go +++ b/server/dashboard_api.go @@ -18,6 +18,7 @@ import ( "encoding/json" "net/http" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/consts" "github.com/fatedier/frp/utils/log" @@ -60,7 +61,7 @@ func apiServerInfo(w http.ResponseWriter, r *http.Request, _ httprouter.Params) }() log.Info("Http request: [/api/serverinfo]") - cfg := config.ServerCommonCfg + cfg := &g.GlbServerCfg.ServerCommonConf serverStats := StatsGetServer() res = ServerInfoResp{ Version: version.Full(), diff --git a/server/metric.go b/server/metric.go index 0e1680ca..3c135e70 100644 --- a/server/metric.go +++ b/server/metric.go @@ -18,7 +18,7 @@ import ( "sync" "time" - "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/metric" ) @@ -92,19 +92,19 @@ func StatsClearUselessInfo() { } func StatsNewClient() { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.ClientCounts.Inc(1) } } func StatsCloseClient() { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.ClientCounts.Dec(1) } } func StatsNewProxy(name string, proxyType string) { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.mu.Lock() defer globalStats.mu.Unlock() counter, ok := globalStats.ProxyTypeCounts[proxyType] @@ -130,7 +130,7 @@ func StatsNewProxy(name string, proxyType string) { } func StatsCloseProxy(proxyName string, proxyType string) { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.mu.Lock() defer globalStats.mu.Unlock() if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok { @@ -143,7 +143,7 @@ func StatsCloseProxy(proxyName string, proxyType string) { } func StatsOpenConnection(name string) { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.CurConns.Inc(1) globalStats.mu.Lock() @@ -157,7 +157,7 @@ func StatsOpenConnection(name string) { } func StatsCloseConnection(name string) { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.CurConns.Dec(1) globalStats.mu.Lock() @@ -171,7 +171,7 @@ func StatsCloseConnection(name string) { } func StatsAddTrafficIn(name string, trafficIn int64) { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.TotalTrafficIn.Inc(trafficIn) globalStats.mu.Lock() @@ -186,7 +186,7 @@ func StatsAddTrafficIn(name string, trafficIn int64) { } func StatsAddTrafficOut(name string, trafficOut int64) { - if config.ServerCommonCfg.DashboardPort != 0 { + if g.GlbServerCfg.DashboardPort != 0 { globalStats.TotalTrafficOut.Inc(trafficOut) globalStats.mu.Lock() diff --git a/server/proxy.go b/server/proxy.go index 715bf0c0..a0620c98 100644 --- a/server/proxy.go +++ b/server/proxy.go @@ -23,6 +23,7 @@ import ( "sync" "time" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/proto/udp" @@ -126,7 +127,7 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Con func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) { basePxy := BaseProxy{ - name: pxyConf.GetName(), + name: pxyConf.GetBaseInfo().ProxyName, ctl: ctl, listeners: make([]frpNet.Listener, 0), Logger: log.NewPrefixLogger(ctl.runId), @@ -191,7 +192,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) { remoteAddr = fmt.Sprintf(":%d", pxy.realPort) pxy.cfg.RemotePort = pxy.realPort - listener, errRet := frpNet.ListenTcp(config.ServerCommonCfg.ProxyBindAddr, pxy.realPort) + listener, errRet := frpNet.ListenTcp(g.GlbServerCfg.ProxyBindAddr, pxy.realPort) if errRet != nil { err = errRet return @@ -244,7 +245,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) { } tmpDomain := routeConfig.Domain tmpLocation := routeConfig.Location - addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort))) + addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(g.GlbServerCfg.VhostHttpPort))) pxy.closeFuncs = append(pxy.closeFuncs, func() { pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation) }) @@ -253,7 +254,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) { } if pxy.cfg.SubDomain != "" { - routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost + routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost for _, location := range locations { routeConfig.Location = location err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig) @@ -262,7 +263,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) { } tmpDomain := routeConfig.Domain tmpLocation := routeConfig.Location - addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort))) + addrs = append(addrs, util.CanonicalAddr(tmpDomain, g.GlbServerCfg.VhostHttpPort)) pxy.closeFuncs = append(pxy.closeFuncs, func() { pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation) }) @@ -286,7 +287,7 @@ func (pxy *HttpProxy) GetRealConn() (workConn frpNet.Conn, err error) { var rwc io.ReadWriteCloser = tmpConn if pxy.cfg.UseEncryption { - rwc, err = frpIo.WithEncryption(rwc, []byte(config.ServerCommonCfg.PrivilegeToken)) + rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token)) if err != nil { pxy.Error("create encryption stream error: %v", err) return @@ -334,11 +335,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) { l.AddLogPrefix(pxy.name) pxy.Info("https proxy listen for host [%s]", routeConfig.Domain) pxy.listeners = append(pxy.listeners, l) - addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort))) + addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, g.GlbServerCfg.VhostHttpsPort)) } if pxy.cfg.SubDomain != "" { - routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost + routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig) if errRet != nil { err = errRet @@ -347,7 +348,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) { l.AddLogPrefix(pxy.name) pxy.Info("https proxy listen for host [%s]", routeConfig.Domain) pxy.listeners = append(pxy.listeners, l) - addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort))) + addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpsPort))) } pxy.startListenHandler(pxy, HandleUserTcpConnection) @@ -478,7 +479,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) { remoteAddr = fmt.Sprintf(":%d", pxy.realPort) pxy.cfg.RemotePort = pxy.realPort - addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.ServerCommonCfg.ProxyBindAddr, pxy.realPort)) + addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbServerCfg.ProxyBindAddr, pxy.realPort)) if errRet != nil { err = errRet return @@ -644,7 +645,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) { var local io.ReadWriteCloser = workConn cfg := pxy.GetConf().GetBaseInfo() if cfg.UseEncryption { - local, err = frpIo.WithEncryption(local, []byte(config.ServerCommonCfg.PrivilegeToken)) + local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token)) if err != nil { pxy.Error("create encryption stream error: %v", err) return diff --git a/server/service.go b/server/service.go index e976658a..82b1a35b 100644 --- a/server/service.go +++ b/server/service.go @@ -21,7 +21,7 @@ import ( "time" "github.com/fatedier/frp/assets" - "github.com/fatedier/frp/models/config" + "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/utils/log" frpNet "github.com/fatedier/frp/utils/net" @@ -71,7 +71,7 @@ type Service struct { } func NewService() (svr *Service, err error) { - cfg := config.ServerCommonCfg + cfg := &g.GlbServerCfg.ServerCommonConf svr = &Service{ ctlManager: NewControlManager(), pxyManager: NewProxyManager(), @@ -170,7 +170,7 @@ func (svr *Service) Run() { if svr.natHoleController != nil { go svr.natHoleController.Run() } - if config.ServerCommonCfg.KcpBindPort > 0 { + if g.GlbServerCfg.KcpBindPort > 0 { go svr.HandleListener(svr.kcpListener) } svr.HandleListener(svr.listener) @@ -233,7 +233,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) { } } - if config.ServerCommonCfg.TcpMux { + if g.GlbServerCfg.TcpMux { session, err := smux.Server(frpConn, nil) if err != nil { log.Warn("Failed to create mux connection: %v", err) @@ -270,11 +270,11 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e // Check auth. nowTime := time.Now().Unix() - if config.ServerCommonCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > config.ServerCommonCfg.AuthTimeout { + if g.GlbServerCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > g.GlbServerCfg.AuthTimeout { err = fmt.Errorf("authorization timeout") return } - if util.GetAuthKey(config.ServerCommonCfg.PrivilegeToken, loginMsg.Timestamp) != loginMsg.PrivilegeKey { + if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey { err = fmt.Errorf("authorization failed") return } diff --git a/tests/conf/auto_test_frpc.ini b/tests/conf/auto_test_frpc.ini index 93769900..0ada3257 100644 --- a/tests/conf/auto_test_frpc.ini +++ b/tests/conf/auto_test_frpc.ini @@ -4,7 +4,7 @@ server_port = 10700 log_file = ./frpc.log # debug, info, warn, error log_level = debug -privilege_token = 123456 +token = 123456 admin_port = 10600 admin_user = abc admin_pwd = abc diff --git a/tests/conf/auto_test_frpc_visitor.ini b/tests/conf/auto_test_frpc_visitor.ini index cc524deb..e06d1604 100644 --- a/tests/conf/auto_test_frpc_visitor.ini +++ b/tests/conf/auto_test_frpc_visitor.ini @@ -4,7 +4,7 @@ server_port = 10700 log_file = ./frpc_visitor.log # debug, info, warn, error log_level = debug -privilege_token = 123456 +token = 123456 [stcp_visitor] type = stcp diff --git a/tests/conf/auto_test_frps.ini b/tests/conf/auto_test_frps.ini index f59b8a3c..193a1087 100644 --- a/tests/conf/auto_test_frps.ini +++ b/tests/conf/auto_test_frps.ini @@ -4,6 +4,6 @@ bind_port = 10700 vhost_http_port = 10804 log_file = ./frps.log log_level = debug -privilege_token = 123456 +token = 123456 privilege_allow_ports = 10000-20000,20002,30000-50000 subdomain_host = sub.com diff --git a/utils/version/version.go b/utils/version/version.go index 5c901e7d..8e468030 100644 --- a/utils/version/version.go +++ b/utils/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version string = "0.16.1" +var version string = "0.17.0" func Full() string { return version diff --git a/vendor/github.com/fatedier/beego/controller.go b/vendor/github.com/fatedier/beego/controller.go index 488ffcda..d341ea82 100644 --- a/vendor/github.com/fatedier/beego/controller.go +++ b/vendor/github.com/fatedier/beego/controller.go @@ -223,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { } buf.Reset() - ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data) + ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data) } return buf.Bytes(), err } @@ -249,7 +249,7 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) { } } } - BuildTemplate(c.viewPath() , buildFiles...) + BuildTemplate(c.viewPath(), buildFiles...) } return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data) } diff --git a/vendor/github.com/fatedier/beego/controller_test.go b/vendor/github.com/fatedier/beego/controller_test.go index c2025860..1e53416d 100644 --- a/vendor/github.com/fatedier/beego/controller_test.go +++ b/vendor/github.com/fatedier/beego/controller_test.go @@ -172,10 +172,10 @@ func TestAdditionalViewPaths(t *testing.T) { t.Fatal("TestAdditionalViewPaths expected error") } }() - ctrl.RenderString(); + ctrl.RenderString() }() ctrl.TplName = "file2.tpl" ctrl.ViewPath = dir2 - ctrl.RenderString(); + ctrl.RenderString() } diff --git a/vendor/github.com/fatedier/beego/logs/alils/signature.go b/vendor/github.com/fatedier/beego/logs/alils/signature.go index e0e4b3f7..c96aeb93 100755 --- a/vendor/github.com/fatedier/beego/logs/alils/signature.go +++ b/vendor/github.com/fatedier/beego/logs/alils/signature.go @@ -109,4 +109,3 @@ func signature(project *LogProject, method, uri string, digest = base64.StdEncoding.EncodeToString(mac.Sum(nil)) return } - diff --git a/vendor/github.com/fatedier/beego/orm/db.go b/vendor/github.com/fatedier/beego/orm/db.go index bca6071d..6e97ab18 100644 --- a/vendor/github.com/fatedier/beego/orm/db.go +++ b/vendor/github.com/fatedier/beego/orm/db.go @@ -48,7 +48,7 @@ var ( "lte": true, "eq": true, "nq": true, - "ne": true, + "ne": true, "startswith": true, "endswith": true, "istartswith": true, diff --git a/vendor/github.com/fatedier/beego/template.go b/vendor/github.com/fatedier/beego/template.go index 17c18591..ffd4df45 100644 --- a/vendor/github.com/fatedier/beego/template.go +++ b/vendor/github.com/fatedier/beego/template.go @@ -31,11 +31,11 @@ import ( ) var ( - beegoTplFuncMap = make(template.FuncMap) + beegoTplFuncMap = make(template.FuncMap) beeViewPathTemplateLocked = false // beeViewPathTemplates caching map and supported template file extensions per view - beeViewPathTemplates = make(map[string]map[string]*template.Template) - templatesLock sync.RWMutex + beeViewPathTemplates = make(map[string]map[string]*template.Template) + templatesLock sync.RWMutex // beeTemplateExt stores the template extension which will build beeTemplateExt = []string{"tpl", "html"} // beeTemplatePreprocessors stores associations of extension -> preprocessor handler @@ -46,7 +46,7 @@ var ( // writing the output to wr. // A template will be executed safely in parallel. func ExecuteTemplate(wr io.Writer, name string, data interface{}) error { - return ExecuteViewPathTemplate(wr,name, BConfig.WebConfig.ViewsPath, data) + return ExecuteViewPathTemplate(wr, name, BConfig.WebConfig.ViewsPath, data) } // ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object, @@ -57,7 +57,7 @@ func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data in templatesLock.RLock() defer templatesLock.RUnlock() } - if beeTemplates,ok := beeViewPathTemplates[viewPath]; ok { + if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok { if t, ok := beeTemplates[name]; ok { var err error if t.Lookup(name) != nil { @@ -160,9 +160,9 @@ func AddTemplateExt(ext string) { beeTemplateExt = append(beeTemplateExt, ext) } -// AddViewPath adds a new path to the supported view paths. +// AddViewPath adds a new path to the supported view paths. //Can later be used by setting a controller ViewPath to this folder -//will panic if called after beego.Run() +//will panic if called after beego.Run() func AddViewPath(viewPath string) error { if beeViewPathTemplateLocked { panic("Can not add new view paths after beego.Run()") @@ -184,7 +184,7 @@ func BuildTemplate(dir string, files ...string) error { } return errors.New("dir open err") } - beeTemplates,ok := beeViewPathTemplates[dir]; + beeTemplates, ok := beeViewPathTemplates[dir] if !ok { panic("Unknown view path: " + dir) } diff --git a/vendor/github.com/fatedier/beego/toolbox/statistics.go b/vendor/github.com/fatedier/beego/toolbox/statistics.go index c6a9489f..d014544c 100644 --- a/vendor/github.com/fatedier/beego/toolbox/statistics.go +++ b/vendor/github.com/fatedier/beego/toolbox/statistics.go @@ -119,7 +119,7 @@ func (m *URLMap) GetMap() map[string]interface{} { func (m *URLMap) GetMapData() []map[string]interface{} { m.lock.Lock() defer m.lock.Unlock() - + var resultLists []map[string]interface{} for k, v := range m.urlmap { diff --git a/vendor/github.com/klauspost/cpuid/private/cpuid.go b/vendor/github.com/klauspost/cpuid/private/cpuid.go index be99cb0b..56c26c40 100644 --- a/vendor/github.com/klauspost/cpuid/private/cpuid.go +++ b/vendor/github.com/klauspost/cpuid/private/cpuid.go @@ -12,146 +12,146 @@ import ( type vendor int const ( - other vendor = iota + other vendor = iota intel amd via transmeta nsc - kvm // Kernel-based Virtual Machine - msvm // Microsoft Hyper-V or Windows Virtual PC + kvm // Kernel-based Virtual Machine + msvm // Microsoft Hyper-V or Windows Virtual PC vmware xenhvm ) const ( - cmov = 1 << iota // i686 CMOV - nx // NX (No-Execute) bit - amd3dnow // AMD 3DNOW - amd3dnowext // AMD 3DNowExt - mmx // standard MMX - mmxext // SSE integer functions or AMD MMX ext - sse // SSE functions - sse2 // P4 SSE functions - sse3 // Prescott SSE3 functions - ssse3 // Conroe SSSE3 functions - sse4 // Penryn SSE4.1 functions - sse4a // AMD Barcelona microarchitecture SSE4a instructions - sse42 // Nehalem SSE4.2 functions - avx // AVX functions - avx2 // AVX2 functions - fma3 // Intel FMA 3 - fma4 // Bulldozer FMA4 functions - xop // Bulldozer XOP functions - f16c // Half-precision floating-point conversion - bmi1 // Bit Manipulation Instruction Set 1 - bmi2 // Bit Manipulation Instruction Set 2 - tbm // AMD Trailing Bit Manipulation - lzcnt // LZCNT instruction - popcnt // POPCNT instruction - aesni // Advanced Encryption Standard New Instructions - clmul // Carry-less Multiplication - htt // Hyperthreading (enabled) - hle // Hardware Lock Elision - rtm // Restricted Transactional Memory - rdrand // RDRAND instruction is available - rdseed // RDSEED instruction is available - adx // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - sha // Intel SHA Extensions - avx512f // AVX-512 Foundation - avx512dq // AVX-512 Doubleword and Quadword Instructions - avx512ifma // AVX-512 Integer Fused Multiply-Add Instructions - avx512pf // AVX-512 Prefetch Instructions - avx512er // AVX-512 Exponential and Reciprocal Instructions - avx512cd // AVX-512 Conflict Detection Instructions - avx512bw // AVX-512 Byte and Word Instructions - avx512vl // AVX-512 Vector Length Extensions - avx512vbmi // AVX-512 Vector Bit Manipulation Instructions - mpx // Intel MPX (Memory Protection Extensions) - erms // Enhanced REP MOVSB/STOSB - rdtscp // RDTSCP Instruction - cx16 // CMPXCHG16B Instruction + cmov = 1 << iota // i686 CMOV + nx // NX (No-Execute) bit + amd3dnow // AMD 3DNOW + amd3dnowext // AMD 3DNowExt + mmx // standard MMX + mmxext // SSE integer functions or AMD MMX ext + sse // SSE functions + sse2 // P4 SSE functions + sse3 // Prescott SSE3 functions + ssse3 // Conroe SSSE3 functions + sse4 // Penryn SSE4.1 functions + sse4a // AMD Barcelona microarchitecture SSE4a instructions + sse42 // Nehalem SSE4.2 functions + avx // AVX functions + avx2 // AVX2 functions + fma3 // Intel FMA 3 + fma4 // Bulldozer FMA4 functions + xop // Bulldozer XOP functions + f16c // Half-precision floating-point conversion + bmi1 // Bit Manipulation Instruction Set 1 + bmi2 // Bit Manipulation Instruction Set 2 + tbm // AMD Trailing Bit Manipulation + lzcnt // LZCNT instruction + popcnt // POPCNT instruction + aesni // Advanced Encryption Standard New Instructions + clmul // Carry-less Multiplication + htt // Hyperthreading (enabled) + hle // Hardware Lock Elision + rtm // Restricted Transactional Memory + rdrand // RDRAND instruction is available + rdseed // RDSEED instruction is available + adx // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + sha // Intel SHA Extensions + avx512f // AVX-512 Foundation + avx512dq // AVX-512 Doubleword and Quadword Instructions + avx512ifma // AVX-512 Integer Fused Multiply-Add Instructions + avx512pf // AVX-512 Prefetch Instructions + avx512er // AVX-512 Exponential and Reciprocal Instructions + avx512cd // AVX-512 Conflict Detection Instructions + avx512bw // AVX-512 Byte and Word Instructions + avx512vl // AVX-512 Vector Length Extensions + avx512vbmi // AVX-512 Vector Bit Manipulation Instructions + mpx // Intel MPX (Memory Protection Extensions) + erms // Enhanced REP MOVSB/STOSB + rdtscp // RDTSCP Instruction + cx16 // CMPXCHG16B Instruction // Performance indicators - sse2slow // SSE2 is supported, but usually not faster - sse3slow // SSE3 is supported, but usually not faster - atom // Atom processor, some SSSE3 instructions are slower + sse2slow // SSE2 is supported, but usually not faster + sse3slow // SSE3 is supported, but usually not faster + atom // Atom processor, some SSSE3 instructions are slower ) var flagNames = map[flags]string{ - cmov: "CMOV", // i686 CMOV - nx: "NX", // NX (No-Execute) bit - amd3dnow: "AMD3DNOW", // AMD 3DNOW - amd3dnowext: "AMD3DNOWEXT", // AMD 3DNowExt - mmx: "MMX", // Standard MMX - mmxext: "MMXEXT", // SSE integer functions or AMD MMX ext - sse: "SSE", // SSE functions - sse2: "SSE2", // P4 SSE2 functions - sse3: "SSE3", // Prescott SSE3 functions - ssse3: "SSSE3", // Conroe SSSE3 functions - sse4: "SSE4.1", // Penryn SSE4.1 functions - sse4a: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions - sse42: "SSE4.2", // Nehalem SSE4.2 functions - avx: "AVX", // AVX functions - avx2: "AVX2", // AVX functions - fma3: "FMA3", // Intel FMA 3 - fma4: "FMA4", // Bulldozer FMA4 functions - xop: "XOP", // Bulldozer XOP functions - f16c: "F16C", // Half-precision floating-point conversion - bmi1: "BMI1", // Bit Manipulation Instruction Set 1 - bmi2: "BMI2", // Bit Manipulation Instruction Set 2 - tbm: "TBM", // AMD Trailing Bit Manipulation - lzcnt: "LZCNT", // LZCNT instruction - popcnt: "POPCNT", // POPCNT instruction - aesni: "AESNI", // Advanced Encryption Standard New Instructions - clmul: "CLMUL", // Carry-less Multiplication - htt: "HTT", // Hyperthreading (enabled) - hle: "HLE", // Hardware Lock Elision - rtm: "RTM", // Restricted Transactional Memory - rdrand: "RDRAND", // RDRAND instruction is available - rdseed: "RDSEED", // RDSEED instruction is available - adx: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - sha: "SHA", // Intel SHA Extensions - avx512f: "AVX512F", // AVX-512 Foundation - avx512dq: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions - avx512ifma: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions - avx512pf: "AVX512PF", // AVX-512 Prefetch Instructions - avx512er: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions - avx512cd: "AVX512CD", // AVX-512 Conflict Detection Instructions - avx512bw: "AVX512BW", // AVX-512 Byte and Word Instructions - avx512vl: "AVX512VL", // AVX-512 Vector Length Extensions - avx512vbmi: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions - mpx: "MPX", // Intel MPX (Memory Protection Extensions) - erms: "ERMS", // Enhanced REP MOVSB/STOSB - rdtscp: "RDTSCP", // RDTSCP Instruction - cx16: "CX16", // CMPXCHG16B Instruction + cmov: "CMOV", // i686 CMOV + nx: "NX", // NX (No-Execute) bit + amd3dnow: "AMD3DNOW", // AMD 3DNOW + amd3dnowext: "AMD3DNOWEXT", // AMD 3DNowExt + mmx: "MMX", // Standard MMX + mmxext: "MMXEXT", // SSE integer functions or AMD MMX ext + sse: "SSE", // SSE functions + sse2: "SSE2", // P4 SSE2 functions + sse3: "SSE3", // Prescott SSE3 functions + ssse3: "SSSE3", // Conroe SSSE3 functions + sse4: "SSE4.1", // Penryn SSE4.1 functions + sse4a: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions + sse42: "SSE4.2", // Nehalem SSE4.2 functions + avx: "AVX", // AVX functions + avx2: "AVX2", // AVX functions + fma3: "FMA3", // Intel FMA 3 + fma4: "FMA4", // Bulldozer FMA4 functions + xop: "XOP", // Bulldozer XOP functions + f16c: "F16C", // Half-precision floating-point conversion + bmi1: "BMI1", // Bit Manipulation Instruction Set 1 + bmi2: "BMI2", // Bit Manipulation Instruction Set 2 + tbm: "TBM", // AMD Trailing Bit Manipulation + lzcnt: "LZCNT", // LZCNT instruction + popcnt: "POPCNT", // POPCNT instruction + aesni: "AESNI", // Advanced Encryption Standard New Instructions + clmul: "CLMUL", // Carry-less Multiplication + htt: "HTT", // Hyperthreading (enabled) + hle: "HLE", // Hardware Lock Elision + rtm: "RTM", // Restricted Transactional Memory + rdrand: "RDRAND", // RDRAND instruction is available + rdseed: "RDSEED", // RDSEED instruction is available + adx: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + sha: "SHA", // Intel SHA Extensions + avx512f: "AVX512F", // AVX-512 Foundation + avx512dq: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions + avx512ifma: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions + avx512pf: "AVX512PF", // AVX-512 Prefetch Instructions + avx512er: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions + avx512cd: "AVX512CD", // AVX-512 Conflict Detection Instructions + avx512bw: "AVX512BW", // AVX-512 Byte and Word Instructions + avx512vl: "AVX512VL", // AVX-512 Vector Length Extensions + avx512vbmi: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions + mpx: "MPX", // Intel MPX (Memory Protection Extensions) + erms: "ERMS", // Enhanced REP MOVSB/STOSB + rdtscp: "RDTSCP", // RDTSCP Instruction + cx16: "CX16", // CMPXCHG16B Instruction // Performance indicators - sse2slow: "SSE2SLOW", // SSE2 supported, but usually not faster - sse3slow: "SSE3SLOW", // SSE3 supported, but usually not faster - atom: "ATOM", // Atom processor, some SSSE3 instructions are slower + sse2slow: "SSE2SLOW", // SSE2 supported, but usually not faster + sse3slow: "SSE3SLOW", // SSE3 supported, but usually not faster + atom: "ATOM", // Atom processor, some SSSE3 instructions are slower } // CPUInfo contains information about the detected system CPU. type cpuInfo struct { - brandname string // Brand name reported by the CPU - vendorid vendor // Comparable CPU vendor ID - features flags // Features of the CPU - physicalcores int // Number of physical processor cores in your CPU. Will be 0 if undetectable. - threadspercore int // Number of threads per physical core. Will be 1 if undetectable. - logicalcores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable. - family int // CPU family number - model int // CPU model number - cacheline int // Cache line size in bytes. Will be 0 if undetectable. - cache struct { - l1i int // L1 Instruction Cache (per core or shared). Will be -1 if undetected - l1d int // L1 Data Cache (per core or shared). Will be -1 if undetected - l2 int // L2 Cache (per core or shared). Will be -1 if undetected - l3 int // L3 Instruction Cache (per core or shared). Will be -1 if undetected + brandname string // Brand name reported by the CPU + vendorid vendor // Comparable CPU vendor ID + features flags // Features of the CPU + physicalcores int // Number of physical processor cores in your CPU. Will be 0 if undetectable. + threadspercore int // Number of threads per physical core. Will be 1 if undetectable. + logicalcores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable. + family int // CPU family number + model int // CPU model number + cacheline int // Cache line size in bytes. Will be 0 if undetectable. + cache struct { + l1i int // L1 Instruction Cache (per core or shared). Will be -1 if undetected + l1d int // L1 Data Cache (per core or shared). Will be -1 if undetected + l2 int // L2 Cache (per core or shared). Will be -1 if undetected + l3 int // L3 Instruction Cache (per core or shared). Will be -1 if undetected } - maxFunc uint32 - maxExFunc uint32 + maxFunc uint32 + maxExFunc uint32 } var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) @@ -638,18 +638,18 @@ func physicalCores() int { // Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID var vendorMapping = map[string]vendor{ - "AMDisbetter!": amd, - "AuthenticAMD": amd, - "CentaurHauls": via, - "GenuineIntel": intel, - "TransmetaCPU": transmeta, - "GenuineTMx86": transmeta, - "Geode by NSC": nsc, - "VIA VIA VIA ": via, - "KVMKVMKVMKVM": kvm, - "Microsoft Hv": msvm, - "VMwareVMware": vmware, - "XenVMMXenVMM": xenhvm, + "AMDisbetter!": amd, + "AuthenticAMD": amd, + "CentaurHauls": via, + "GenuineIntel": intel, + "TransmetaCPU": transmeta, + "GenuineTMx86": transmeta, + "Geode by NSC": nsc, + "VIA VIA VIA ": via, + "KVMKVMKVMKVM": kvm, + "Microsoft Hv": msvm, + "VMwareVMware": vmware, + "XenVMMXenVMM": xenhvm, } func vendorID() vendor { @@ -668,10 +668,10 @@ func cacheLine() int { } _, ebx, _, _ := cpuid(1) - cache := (ebx & 0xff00) >> 5 // cflush size + cache := (ebx & 0xff00) >> 5 // cflush size if cache == 0 && maxExtendedFunction() >= 0x80000006 { _, _, ecx, _ := cpuid(0x80000006) - cache = ecx & 0xff // cacheline size + cache = ecx & 0xff // cacheline size } // TODO: Read from Cache and TLB Information return int(cache) diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index d1552e5e..b33045de 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -181,7 +181,7 @@ func indentMessageLines(message string, longestLabelLen int) string { // no need to align first line because it starts at the correct location (after the label) if i != 0 { // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab - outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen +1) + "\t") + outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") } outBuf.WriteString(scanner.Text()) } @@ -229,7 +229,7 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { } type labeledContent struct { - label string + label string content string } diff --git a/vendor/github.com/stretchr/testify/assert/assertions_test.go b/vendor/github.com/stretchr/testify/assert/assertions_test.go index fb235059..3e3861ba 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions_test.go +++ b/vendor/github.com/stretchr/testify/assert/assertions_test.go @@ -250,8 +250,8 @@ func TestEqualFormatting(t *testing.T) { msgAndArgs []interface{} want string }{ - {equalWant:"want", equalGot: "got", want: "\tassertions.go:[0-9]+: \r \r\tError Trace:\t\n\t\t\r\tError: \tNot equal: \n\t\t\r\t \texpected: \"want\"\n\t\t\r\t \treceived: \"got\"\n"}, - {equalWant:"want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \r \r\tError Trace:\t\n\t\t\r\tError: \tNot equal: \n\t\t\r\t \texpected: \"want\"\n\t\t\r\t \treceived: \"got\"\n\t\t\r\tMessages: \thello, world!\n"}, + {equalWant: "want", equalGot: "got", want: "\tassertions.go:[0-9]+: \r \r\tError Trace:\t\n\t\t\r\tError: \tNot equal: \n\t\t\r\t \texpected: \"want\"\n\t\t\r\t \treceived: \"got\"\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \r \r\tError Trace:\t\n\t\t\r\tError: \tNot equal: \n\t\t\r\t \texpected: \"want\"\n\t\t\r\t \treceived: \"got\"\n\t\t\r\tMessages: \thello, world!\n"}, } { mockT := &bufferT{} Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) diff --git a/vendor/github.com/tjfoc/gmsm/sm4/sm4.go b/vendor/github.com/tjfoc/gmsm/sm4/sm4.go index a839332d..76415d7a 100644 --- a/vendor/github.com/tjfoc/gmsm/sm4/sm4.go +++ b/vendor/github.com/tjfoc/gmsm/sm4/sm4.go @@ -149,7 +149,7 @@ func cryptBlock(subkeys []uint32, b []uint32, r []byte, dst, src []byte, decrypt permuteInitialBlock(b, src) for i := 0; i < 32; i++ { if decrypt { - tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[31 - i])) + tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[31-i])) // tm = feistel1(b[0], b[1], b[2], b[3], subkeys[31-i]) } else { tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[i])) diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index c87fbebf..f91c2770 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -383,8 +383,8 @@ func init() { // 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } @@ -393,8 +393,8 @@ func init() { p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } From fe187eb8ecabeaef0a91eb2eed82658fc12bfff5 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 23 Apr 2018 02:15:01 +0800 Subject: [PATCH 04/11] remove package github.com/docopt/docopt-go --- glide.lock | 6 +- glide.yaml | 2 - vendor/github.com/docopt/docopt-go/.gitignore | 25 - .../github.com/docopt/docopt-go/.travis.yml | 31 - vendor/github.com/docopt/docopt-go/LICENSE | 20 - vendor/github.com/docopt/docopt-go/README.md | 88 - vendor/github.com/docopt/docopt-go/docopt.go | 1239 ------------- .../docopt/docopt-go/docopt_test.go | 1536 ----------------- .../docopt/docopt-go/example_test.go | 37 - .../examples/arguments/arguments_example.go | 29 - .../examples/calculator/calculator_example.go | 26 - .../config_file/config_file_example.go | 76 - .../examples/counted/counted_example.go | 22 - .../examples/git/branch/git_branch.go | 38 - .../examples/git/checkout/git_checkout.go | 30 - .../docopt-go/examples/git/clone/git_clone.go | 37 - .../docopt/docopt-go/examples/git/git.go | 108 -- .../docopt-go/examples/git/push/git_push.go | 34 - .../examples/git/remote/git_remote.go | 28 - .../examples/naval_fate/naval_fate.go | 28 - .../examples/odd_even/odd_even_example.go | 19 - .../examples/options/options_example.go | 43 - .../options_shortcut_example.go | 24 - .../docopt-go/examples/quick/quick_example.go | 16 - .../type_assert/type_assert_example.go | 31 - .../docopt/docopt-go/test_golang.docopt | 9 - .../docopt/docopt-go/testcases.docopt | 957 ---------- .../github.com/fatedier/beego/controller.go | 4 +- .../fatedier/beego/controller_test.go | 4 +- .../fatedier/beego/logs/alils/signature.go | 1 + vendor/github.com/fatedier/beego/orm/db.go | 2 +- vendor/github.com/fatedier/beego/template.go | 16 +- .../fatedier/beego/toolbox/statistics.go | 2 +- .../klauspost/cpuid/private/cpuid.go | 262 +-- .../stretchr/testify/assert/assertions.go | 4 +- .../testify/assert/assertions_test.go | 4 +- vendor/github.com/tjfoc/gmsm/sm4/sm4.go | 2 +- vendor/golang.org/x/crypto/ssh/kex.go | 8 +- 38 files changed, 157 insertions(+), 4691 deletions(-) delete mode 100644 vendor/github.com/docopt/docopt-go/.gitignore delete mode 100644 vendor/github.com/docopt/docopt-go/.travis.yml delete mode 100644 vendor/github.com/docopt/docopt-go/LICENSE delete mode 100644 vendor/github.com/docopt/docopt-go/README.md delete mode 100644 vendor/github.com/docopt/docopt-go/docopt.go delete mode 100644 vendor/github.com/docopt/docopt-go/docopt_test.go delete mode 100644 vendor/github.com/docopt/docopt-go/example_test.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/git/clone/git_clone.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/git/git.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/git/push/git_push.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/git/remote/git_remote.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/naval_fate/naval_fate.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/odd_even/odd_even_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/options/options_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/options_shortcut/options_shortcut_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/quick/quick_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/examples/type_assert/type_assert_example.go delete mode 100644 vendor/github.com/docopt/docopt-go/test_golang.docopt delete mode 100644 vendor/github.com/docopt/docopt-go/testcases.docopt diff --git a/glide.lock b/glide.lock index 331dfac9..c980a991 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 4095d78a15bf0e7ffdd63331ce75d7199d663cc8710dcd08b9dcd09ba3183eac -updated: 2018-01-23T14:48:38.764359+08:00 +hash: 4826a83c4ef4490fd09c560e6a8c8737a7586a97f1beb72294123db65be5ac38 +updated: 2018-04-23T02:10:12.581595+08:00 imports: - name: github.com/armon/go-socks5 version: e75332964ef517daa070d7c38a9466a0d687e0a5 @@ -7,8 +7,6 @@ imports: version: 346938d642f2ec3594ed81d874461961cd0faa76 subpackages: - spew -- name: github.com/docopt/docopt-go - version: 784ddc588536785e7299f7272f39101f7faccc3f - name: github.com/fatedier/beego version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8 subpackages: diff --git a/glide.yaml b/glide.yaml index 47109952..0e3ffdb7 100644 --- a/glide.yaml +++ b/glide.yaml @@ -6,8 +6,6 @@ import: version: v1.1.0 subpackages: - spew -- package: github.com/docopt/docopt-go - version: 0.6.2 - package: github.com/fatedier/beego version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8 subpackages: diff --git a/vendor/github.com/docopt/docopt-go/.gitignore b/vendor/github.com/docopt/docopt-go/.gitignore deleted file mode 100644 index 49ad16c6..00000000 --- a/vendor/github.com/docopt/docopt-go/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -# coverage droppings -profile.cov diff --git a/vendor/github.com/docopt/docopt-go/.travis.yml b/vendor/github.com/docopt/docopt-go/.travis.yml deleted file mode 100644 index 65fad599..00000000 --- a/vendor/github.com/docopt/docopt-go/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Travis CI (http://travis-ci.org/) is a continuous integration -# service for open source projects. This file configures it -# to run unit tests for docopt-go. - -language: go - -go: - - 1.4 - - 1.5 - - tip - -matrix: - fast_finish: true - -before_install: - - go get golang.org/x/tools/cmd/vet - - go get golang.org/x/tools/cmd/cover - - go get github.com/golang/lint/golint - - go get github.com/mattn/goveralls - -install: - - go get -d -v ./... && go build -v ./... - -script: - - go vet -x ./... - - $HOME/gopath/bin/golint ./... - - go test -v ./... - - go test -covermode=count -coverprofile=profile.cov . - -after_script: - - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/vendor/github.com/docopt/docopt-go/LICENSE b/vendor/github.com/docopt/docopt-go/LICENSE deleted file mode 100644 index 8841af16..00000000 --- a/vendor/github.com/docopt/docopt-go/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Keith Batten - -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/docopt/docopt-go/README.md b/vendor/github.com/docopt/docopt-go/README.md deleted file mode 100644 index 71c92aae..00000000 --- a/vendor/github.com/docopt/docopt-go/README.md +++ /dev/null @@ -1,88 +0,0 @@ -docopt-go -========= - -[![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go) -[![Coverage Status](https://coveralls.io/repos/docopt/docopt.go/badge.png)](https://coveralls.io/r/docopt/docopt.go) -[![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.png)](https://godoc.org/github.com/docopt/docopt.go) - -An implementation of [docopt](http://docopt.org/) in the -[Go](http://golang.org/) programming language. - -**docopt** helps you create *beautiful* command-line interfaces easily: - -```go -package main - -import ( - "fmt" - "github.com/docopt/docopt-go" -) - -func main() { - usage := `Naval Fate. - -Usage: - naval_fate ship new ... - naval_fate ship move [--speed=] - naval_fate ship shoot - naval_fate mine (set|remove) [--moored|--drifting] - naval_fate -h | --help - naval_fate --version - -Options: - -h --help Show this screen. - --version Show version. - --speed= Speed in knots [default: 10]. - --moored Moored (anchored) mine. - --drifting Drifting mine.` - - arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false) - fmt.Println(arguments) -} -``` - -**docopt** parses command-line arguments based on a help message. Don't -write parser code: a good help message already has all the necessary -information in it. - -## Installation - -⚠ Use the alias “docopt-go”. To use docopt in your Go code: - -```go -import "github.com/docopt/docopt-go" -``` - -To install docopt according to your `$GOPATH`: - -```console -$ go get github.com/docopt/docopt-go -``` - -## API - -```go -func Parse(doc string, argv []string, help bool, version string, - optionsFirst bool, exit ...bool) (map[string]interface{}, error) -``` -Parse `argv` based on the command-line interface described in `doc`. - -Given a conventional command-line help message, docopt creates a parser and -processes the arguments. See -https://github.com/docopt/docopt#help-message-format for a description of the -help message format. If `argv` is `nil`, `os.Args[1:]` is used. - -docopt returns a map of option names to the values parsed from `argv`, and an -error or `nil`. - -More documentation for docopt is available at -[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go). - -## Testing - -All tests from the Python version are implemented and passing -at [Travis CI](https://travis-ci.org/docopt/docopt.go). New -language-agnostic tests have been added -to [test_golang.docopt](test_golang.docopt). - -To run tests for docopt-go, use `go test`. diff --git a/vendor/github.com/docopt/docopt-go/docopt.go b/vendor/github.com/docopt/docopt-go/docopt.go deleted file mode 100644 index d929fc39..00000000 --- a/vendor/github.com/docopt/docopt-go/docopt.go +++ /dev/null @@ -1,1239 +0,0 @@ -// Licensed under terms of MIT license (see LICENSE-MIT) -// Copyright (c) 2013 Keith Batten, kbatten@gmail.com - -/* -Package docopt parses command-line arguments based on a help message. - -⚠ Use the alias “docopt-go”: - import "github.com/docopt/docopt-go" -or - $ go get github.com/docopt/docopt-go -*/ -package docopt - -import ( - "fmt" - "os" - "reflect" - "regexp" - "strings" - "unicode" -) - -/* -Parse `argv` based on the command-line interface described in `doc`. - -Given a conventional command-line help message, docopt creates a parser and -processes the arguments. See -https://github.com/docopt/docopt#help-message-format for a description of the -help message format. If `argv` is `nil`, `os.Args[1:]` is used. - -docopt returns a map of option names to the values parsed from `argv`, and an -error or `nil`. - -Set `help` to `false` to disable automatic help messages on `-h` or `--help`. -If `version` is a non-empty string, it will be printed when `--version` is -specified. Set `optionsFirst` to `true` to require that options always come -before positional arguments; otherwise they can overlap. - -By default, docopt calls `os.Exit(0)` if it handled a built-in option such as -`-h` or `--version`. If the user errored with a wrong command or options, -docopt exits with a return code of 1. To stop docopt from calling `os.Exit()` -and to handle your own return codes, pass an optional last parameter of `false` -for `exit`. -*/ -func Parse(doc string, argv []string, help bool, version string, - optionsFirst bool, exit ...bool) (map[string]interface{}, error) { - // if "false" was the (optional) last arg, don't call os.Exit() - exitOk := true - if len(exit) > 0 { - exitOk = exit[0] - } - args, output, err := parse(doc, argv, help, version, optionsFirst) - if _, ok := err.(*UserError); ok { - // the user gave us bad input - fmt.Fprintln(os.Stderr, output) - if exitOk { - os.Exit(1) - } - } else if len(output) > 0 && err == nil { - // the user asked for help or `--version` - fmt.Println(output) - if exitOk { - os.Exit(0) - } - } - return args, err -} - -// parse and return a map of args, output and all errors -func parse(doc string, argv []string, help bool, version string, optionsFirst bool) (args map[string]interface{}, output string, err error) { - if argv == nil && len(os.Args) > 1 { - argv = os.Args[1:] - } - - usageSections := parseSection("usage:", doc) - - if len(usageSections) == 0 { - err = newLanguageError("\"usage:\" (case-insensitive) not found.") - return - } - if len(usageSections) > 1 { - err = newLanguageError("More than one \"usage:\" (case-insensitive).") - return - } - usage := usageSections[0] - - options := parseDefaults(doc) - formal, err := formalUsage(usage) - if err != nil { - output = handleError(err, usage) - return - } - - pat, err := parsePattern(formal, &options) - if err != nil { - output = handleError(err, usage) - return - } - - patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst) - if err != nil { - output = handleError(err, usage) - return - } - patFlat, err := pat.flat(patternOption) - if err != nil { - output = handleError(err, usage) - return - } - patternOptions := patFlat.unique() - - patFlat, err = pat.flat(patternOptionSSHORTCUT) - if err != nil { - output = handleError(err, usage) - return - } - for _, optionsShortcut := range patFlat { - docOptions := parseDefaults(doc) - optionsShortcut.children = docOptions.unique().diff(patternOptions) - } - - if output = extras(help, version, patternArgv, doc); len(output) > 0 { - return - } - - err = pat.fix() - if err != nil { - output = handleError(err, usage) - return - } - matched, left, collected := pat.match(&patternArgv, nil) - if matched && len(*left) == 0 { - patFlat, err = pat.flat(patternDefault) - if err != nil { - output = handleError(err, usage) - return - } - args = append(patFlat, *collected...).dictionary() - return - } - - err = newUserError("") - output = handleError(err, usage) - return -} - -func handleError(err error, usage string) string { - if _, ok := err.(*UserError); ok { - return strings.TrimSpace(fmt.Sprintf("%s\n%s", err, usage)) - } - return "" -} - -func parseSection(name, source string) []string { - p := regexp.MustCompile(`(?im)^([^\n]*` + name + `[^\n]*\n?(?:[ \t].*?(?:\n|$))*)`) - s := p.FindAllString(source, -1) - if s == nil { - s = []string{} - } - for i, v := range s { - s[i] = strings.TrimSpace(v) - } - return s -} - -func parseDefaults(doc string) patternList { - defaults := patternList{} - p := regexp.MustCompile(`\n[ \t]*(-\S+?)`) - for _, s := range parseSection("options:", doc) { - // FIXME corner case "bla: options: --foo" - _, _, s = stringPartition(s, ":") // get rid of "options:" - split := p.Split("\n"+s, -1)[1:] - match := p.FindAllStringSubmatch("\n"+s, -1) - for i := range split { - optionDescription := match[i][1] + split[i] - if strings.HasPrefix(optionDescription, "-") { - defaults = append(defaults, parseOption(optionDescription)) - } - } - } - return defaults -} - -func parsePattern(source string, options *patternList) (*pattern, error) { - tokens := tokenListFromPattern(source) - result, err := parseExpr(tokens, options) - if err != nil { - return nil, err - } - if tokens.current() != nil { - return nil, tokens.errorFunc("unexpected ending: %s" + strings.Join(tokens.tokens, " ")) - } - return newRequired(result...), nil -} - -func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool) (patternList, error) { - /* - Parse command-line argument vector. - - If options_first: - argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; - else: - argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; - */ - parsed := patternList{} - for tokens.current() != nil { - if tokens.current().eq("--") { - for _, v := range tokens.tokens { - parsed = append(parsed, newArgument("", v)) - } - return parsed, nil - } else if tokens.current().hasPrefix("--") { - pl, err := parseLong(tokens, options) - if err != nil { - return nil, err - } - parsed = append(parsed, pl...) - } else if tokens.current().hasPrefix("-") && !tokens.current().eq("-") { - ps, err := parseShorts(tokens, options) - if err != nil { - return nil, err - } - parsed = append(parsed, ps...) - } else if optionsFirst { - for _, v := range tokens.tokens { - parsed = append(parsed, newArgument("", v)) - } - return parsed, nil - } else { - parsed = append(parsed, newArgument("", tokens.move().String())) - } - } - return parsed, nil -} - -func parseOption(optionDescription string) *pattern { - optionDescription = strings.TrimSpace(optionDescription) - options, _, description := stringPartition(optionDescription, " ") - options = strings.Replace(options, ",", " ", -1) - options = strings.Replace(options, "=", " ", -1) - - short := "" - long := "" - argcount := 0 - var value interface{} - value = false - - reDefault := regexp.MustCompile(`(?i)\[default: (.*)\]`) - for _, s := range strings.Fields(options) { - if strings.HasPrefix(s, "--") { - long = s - } else if strings.HasPrefix(s, "-") { - short = s - } else { - argcount = 1 - } - if argcount > 0 { - matched := reDefault.FindAllStringSubmatch(description, -1) - if len(matched) > 0 { - value = matched[0][1] - } else { - value = nil - } - } - } - return newOption(short, long, argcount, value) -} - -func parseExpr(tokens *tokenList, options *patternList) (patternList, error) { - // expr ::= seq ( '|' seq )* ; - seq, err := parseSeq(tokens, options) - if err != nil { - return nil, err - } - if !tokens.current().eq("|") { - return seq, nil - } - var result patternList - if len(seq) > 1 { - result = patternList{newRequired(seq...)} - } else { - result = seq - } - for tokens.current().eq("|") { - tokens.move() - seq, err = parseSeq(tokens, options) - if err != nil { - return nil, err - } - if len(seq) > 1 { - result = append(result, newRequired(seq...)) - } else { - result = append(result, seq...) - } - } - if len(result) > 1 { - return patternList{newEither(result...)}, nil - } - return result, nil -} - -func parseSeq(tokens *tokenList, options *patternList) (patternList, error) { - // seq ::= ( atom [ '...' ] )* ; - result := patternList{} - for !tokens.current().match(true, "]", ")", "|") { - atom, err := parseAtom(tokens, options) - if err != nil { - return nil, err - } - if tokens.current().eq("...") { - atom = patternList{newOneOrMore(atom...)} - tokens.move() - } - result = append(result, atom...) - } - return result, nil -} - -func parseAtom(tokens *tokenList, options *patternList) (patternList, error) { - // atom ::= '(' expr ')' | '[' expr ']' | 'options' | long | shorts | argument | command ; - tok := tokens.current() - result := patternList{} - if tokens.current().match(false, "(", "[") { - tokens.move() - var matching string - pl, err := parseExpr(tokens, options) - if err != nil { - return nil, err - } - if tok.eq("(") { - matching = ")" - result = patternList{newRequired(pl...)} - } else if tok.eq("[") { - matching = "]" - result = patternList{newOptional(pl...)} - } - moved := tokens.move() - if !moved.eq(matching) { - return nil, tokens.errorFunc("unmatched '%s', expected: '%s' got: '%s'", tok, matching, moved) - } - return result, nil - } else if tok.eq("options") { - tokens.move() - return patternList{newOptionsShortcut()}, nil - } else if tok.hasPrefix("--") && !tok.eq("--") { - return parseLong(tokens, options) - } else if tok.hasPrefix("-") && !tok.eq("-") && !tok.eq("--") { - return parseShorts(tokens, options) - } else if tok.hasPrefix("<") && tok.hasSuffix(">") || tok.isUpper() { - return patternList{newArgument(tokens.move().String(), nil)}, nil - } - return patternList{newCommand(tokens.move().String(), false)}, nil -} - -func parseLong(tokens *tokenList, options *patternList) (patternList, error) { - // long ::= '--' chars [ ( ' ' | '=' ) chars ] ; - long, eq, v := stringPartition(tokens.move().String(), "=") - var value interface{} - var opt *pattern - if eq == "" && v == "" { - value = nil - } else { - value = v - } - - if !strings.HasPrefix(long, "--") { - return nil, newError("long option '%s' doesn't start with --", long) - } - similar := patternList{} - for _, o := range *options { - if o.long == long { - similar = append(similar, o) - } - } - if tokens.err == errorUser && len(similar) == 0 { // if no exact match - similar = patternList{} - for _, o := range *options { - if strings.HasPrefix(o.long, long) { - similar = append(similar, o) - } - } - } - if len(similar) > 1 { // might be simply specified ambiguously 2+ times? - similarLong := make([]string, len(similar)) - for i, s := range similar { - similarLong[i] = s.long - } - return nil, tokens.errorFunc("%s is not a unique prefix: %s?", long, strings.Join(similarLong, ", ")) - } else if len(similar) < 1 { - argcount := 0 - if eq == "=" { - argcount = 1 - } - opt = newOption("", long, argcount, false) - *options = append(*options, opt) - if tokens.err == errorUser { - var val interface{} - if argcount > 0 { - val = value - } else { - val = true - } - opt = newOption("", long, argcount, val) - } - } else { - opt = newOption(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value) - if opt.argcount == 0 { - if value != nil { - return nil, tokens.errorFunc("%s must not have an argument", opt.long) - } - } else { - if value == nil { - if tokens.current().match(true, "--") { - return nil, tokens.errorFunc("%s requires argument", opt.long) - } - moved := tokens.move() - if moved != nil { - value = moved.String() // only set as string if not nil - } - } - } - if tokens.err == errorUser { - if value != nil { - opt.value = value - } else { - opt.value = true - } - } - } - - return patternList{opt}, nil -} - -func parseShorts(tokens *tokenList, options *patternList) (patternList, error) { - // shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ; - tok := tokens.move() - if !tok.hasPrefix("-") || tok.hasPrefix("--") { - return nil, newError("short option '%s' doesn't start with -", tok) - } - left := strings.TrimLeft(tok.String(), "-") - parsed := patternList{} - for left != "" { - var opt *pattern - short := "-" + left[0:1] - left = left[1:] - similar := patternList{} - for _, o := range *options { - if o.short == short { - similar = append(similar, o) - } - } - if len(similar) > 1 { - return nil, tokens.errorFunc("%s is specified ambiguously %d times", short, len(similar)) - } else if len(similar) < 1 { - opt = newOption(short, "", 0, false) - *options = append(*options, opt) - if tokens.err == errorUser { - opt = newOption(short, "", 0, true) - } - } else { // why copying is necessary here? - opt = newOption(short, similar[0].long, similar[0].argcount, similar[0].value) - var value interface{} - if opt.argcount > 0 { - if left == "" { - if tokens.current().match(true, "--") { - return nil, tokens.errorFunc("%s requires argument", short) - } - value = tokens.move().String() - } else { - value = left - left = "" - } - } - if tokens.err == errorUser { - if value != nil { - opt.value = value - } else { - opt.value = true - } - } - } - parsed = append(parsed, opt) - } - return parsed, nil -} - -func newTokenList(source []string, err errorType) *tokenList { - errorFunc := newError - if err == errorUser { - errorFunc = newUserError - } else if err == errorLanguage { - errorFunc = newLanguageError - } - return &tokenList{source, errorFunc, err} -} - -func tokenListFromString(source string) *tokenList { - return newTokenList(strings.Fields(source), errorUser) -} - -func tokenListFromPattern(source string) *tokenList { - p := regexp.MustCompile(`([\[\]\(\)\|]|\.\.\.)`) - source = p.ReplaceAllString(source, ` $1 `) - p = regexp.MustCompile(`\s+|(\S*<.*?>)`) - split := p.Split(source, -1) - match := p.FindAllStringSubmatch(source, -1) - var result []string - l := len(split) - for i := 0; i < l; i++ { - if len(split[i]) > 0 { - result = append(result, split[i]) - } - if i < l-1 && len(match[i][1]) > 0 { - result = append(result, match[i][1]) - } - } - return newTokenList(result, errorLanguage) -} - -func formalUsage(section string) (string, error) { - _, _, section = stringPartition(section, ":") // drop "usage:" - pu := strings.Fields(section) - - if len(pu) == 0 { - return "", newLanguageError("no fields found in usage (perhaps a spacing error).") - } - - result := "( " - for _, s := range pu[1:] { - if s == pu[0] { - result += ") | ( " - } else { - result += s + " " - } - } - result += ")" - - return result, nil -} - -func extras(help bool, version string, options patternList, doc string) string { - if help { - for _, o := range options { - if (o.name == "-h" || o.name == "--help") && o.value == true { - return strings.Trim(doc, "\n") - } - } - } - if version != "" { - for _, o := range options { - if (o.name == "--version") && o.value == true { - return version - } - } - } - return "" -} - -type errorType int - -const ( - errorUser errorType = iota - errorLanguage -) - -func (e errorType) String() string { - switch e { - case errorUser: - return "errorUser" - case errorLanguage: - return "errorLanguage" - } - return "" -} - -// UserError records an error with program arguments. -type UserError struct { - msg string - Usage string -} - -func (e UserError) Error() string { - return e.msg -} -func newUserError(msg string, f ...interface{}) error { - return &UserError{fmt.Sprintf(msg, f...), ""} -} - -// LanguageError records an error with the doc string. -type LanguageError struct { - msg string -} - -func (e LanguageError) Error() string { - return e.msg -} -func newLanguageError(msg string, f ...interface{}) error { - return &LanguageError{fmt.Sprintf(msg, f...)} -} - -var newError = fmt.Errorf - -type tokenList struct { - tokens []string - errorFunc func(string, ...interface{}) error - err errorType -} -type token string - -func (t *token) eq(s string) bool { - if t == nil { - return false - } - return string(*t) == s -} -func (t *token) match(matchNil bool, tokenStrings ...string) bool { - if t == nil && matchNil { - return true - } else if t == nil && !matchNil { - return false - } - - for _, tok := range tokenStrings { - if tok == string(*t) { - return true - } - } - return false -} -func (t *token) hasPrefix(prefix string) bool { - if t == nil { - return false - } - return strings.HasPrefix(string(*t), prefix) -} -func (t *token) hasSuffix(suffix string) bool { - if t == nil { - return false - } - return strings.HasSuffix(string(*t), suffix) -} -func (t *token) isUpper() bool { - if t == nil { - return false - } - return isStringUppercase(string(*t)) -} -func (t *token) String() string { - if t == nil { - return "" - } - return string(*t) -} - -func (tl *tokenList) current() *token { - if len(tl.tokens) > 0 { - return (*token)(&(tl.tokens[0])) - } - return nil -} - -func (tl *tokenList) length() int { - return len(tl.tokens) -} - -func (tl *tokenList) move() *token { - if len(tl.tokens) > 0 { - t := tl.tokens[0] - tl.tokens = tl.tokens[1:] - return (*token)(&t) - } - return nil -} - -type patternType uint - -const ( - // leaf - patternArgument patternType = 1 << iota - patternCommand - patternOption - - // branch - patternRequired - patternOptionAL - patternOptionSSHORTCUT // Marker/placeholder for [options] shortcut. - patternOneOrMore - patternEither - - patternLeaf = patternArgument + - patternCommand + - patternOption - patternBranch = patternRequired + - patternOptionAL + - patternOptionSSHORTCUT + - patternOneOrMore + - patternEither - patternAll = patternLeaf + patternBranch - patternDefault = 0 -) - -func (pt patternType) String() string { - switch pt { - case patternArgument: - return "argument" - case patternCommand: - return "command" - case patternOption: - return "option" - case patternRequired: - return "required" - case patternOptionAL: - return "optional" - case patternOptionSSHORTCUT: - return "optionsshortcut" - case patternOneOrMore: - return "oneormore" - case patternEither: - return "either" - case patternLeaf: - return "leaf" - case patternBranch: - return "branch" - case patternAll: - return "all" - case patternDefault: - return "default" - } - return "" -} - -type pattern struct { - t patternType - - children patternList - - name string - value interface{} - - short string - long string - argcount int -} - -type patternList []*pattern - -func newBranchPattern(t patternType, pl ...*pattern) *pattern { - var p pattern - p.t = t - p.children = make(patternList, len(pl)) - copy(p.children, pl) - return &p -} - -func newRequired(pl ...*pattern) *pattern { - return newBranchPattern(patternRequired, pl...) -} - -func newEither(pl ...*pattern) *pattern { - return newBranchPattern(patternEither, pl...) -} - -func newOneOrMore(pl ...*pattern) *pattern { - return newBranchPattern(patternOneOrMore, pl...) -} - -func newOptional(pl ...*pattern) *pattern { - return newBranchPattern(patternOptionAL, pl...) -} - -func newOptionsShortcut() *pattern { - var p pattern - p.t = patternOptionSSHORTCUT - return &p -} - -func newLeafPattern(t patternType, name string, value interface{}) *pattern { - // default: value=nil - var p pattern - p.t = t - p.name = name - p.value = value - return &p -} - -func newArgument(name string, value interface{}) *pattern { - // default: value=nil - return newLeafPattern(patternArgument, name, value) -} - -func newCommand(name string, value interface{}) *pattern { - // default: value=false - var p pattern - p.t = patternCommand - p.name = name - p.value = value - return &p -} - -func newOption(short, long string, argcount int, value interface{}) *pattern { - // default: "", "", 0, false - var p pattern - p.t = patternOption - p.short = short - p.long = long - if long != "" { - p.name = long - } else { - p.name = short - } - p.argcount = argcount - if value == false && argcount > 0 { - p.value = nil - } else { - p.value = value - } - return &p -} - -func (p *pattern) flat(types patternType) (patternList, error) { - if p.t&patternLeaf != 0 { - if types == patternDefault { - types = patternAll - } - if p.t&types != 0 { - return patternList{p}, nil - } - return patternList{}, nil - } - - if p.t&patternBranch != 0 { - if p.t&types != 0 { - return patternList{p}, nil - } - result := patternList{} - for _, child := range p.children { - childFlat, err := child.flat(types) - if err != nil { - return nil, err - } - result = append(result, childFlat...) - } - return result, nil - } - return nil, newError("unknown pattern type: %d, %d", p.t, types) -} - -func (p *pattern) fix() error { - err := p.fixIdentities(nil) - if err != nil { - return err - } - p.fixRepeatingArguments() - return nil -} - -func (p *pattern) fixIdentities(uniq patternList) error { - // Make pattern-tree tips point to same object if they are equal. - if p.t&patternBranch == 0 { - return nil - } - if uniq == nil { - pFlat, err := p.flat(patternDefault) - if err != nil { - return err - } - uniq = pFlat.unique() - } - for i, child := range p.children { - if child.t&patternBranch == 0 { - ind, err := uniq.index(child) - if err != nil { - return err - } - p.children[i] = uniq[ind] - } else { - err := child.fixIdentities(uniq) - if err != nil { - return err - } - } - } - return nil -} - -func (p *pattern) fixRepeatingArguments() { - // Fix elements that should accumulate/increment values. - var either []patternList - - for _, child := range p.transform().children { - either = append(either, child.children) - } - for _, cas := range either { - casMultiple := patternList{} - for _, e := range cas { - if cas.count(e) > 1 { - casMultiple = append(casMultiple, e) - } - } - for _, e := range casMultiple { - if e.t == patternArgument || e.t == patternOption && e.argcount > 0 { - switch e.value.(type) { - case string: - e.value = strings.Fields(e.value.(string)) - case []string: - default: - e.value = []string{} - } - } - if e.t == patternCommand || e.t == patternOption && e.argcount == 0 { - e.value = 0 - } - } - } -} - -func (p *pattern) match(left *patternList, collected *patternList) (bool, *patternList, *patternList) { - if collected == nil { - collected = &patternList{} - } - if p.t&patternRequired != 0 { - l := left - c := collected - for _, p := range p.children { - var matched bool - matched, l, c = p.match(l, c) - if !matched { - return false, left, collected - } - } - return true, l, c - } else if p.t&patternOptionAL != 0 || p.t&patternOptionSSHORTCUT != 0 { - for _, p := range p.children { - _, left, collected = p.match(left, collected) - } - return true, left, collected - } else if p.t&patternOneOrMore != 0 { - if len(p.children) != 1 { - panic("OneOrMore.match(): assert len(p.children) == 1") - } - l := left - c := collected - var lAlt *patternList - matched := true - times := 0 - for matched { - // could it be that something didn't match but changed l or c? - matched, l, c = p.children[0].match(l, c) - if matched { - times++ - } - if lAlt == l { - break - } - lAlt = l - } - if times >= 1 { - return true, l, c - } - return false, left, collected - } else if p.t&patternEither != 0 { - type outcomeStruct struct { - matched bool - left *patternList - collected *patternList - length int - } - outcomes := []outcomeStruct{} - for _, p := range p.children { - matched, l, c := p.match(left, collected) - outcome := outcomeStruct{matched, l, c, len(*l)} - if matched { - outcomes = append(outcomes, outcome) - } - } - if len(outcomes) > 0 { - minLen := outcomes[0].length - minIndex := 0 - for i, v := range outcomes { - if v.length < minLen { - minIndex = i - } - } - return outcomes[minIndex].matched, outcomes[minIndex].left, outcomes[minIndex].collected - } - return false, left, collected - } else if p.t&patternLeaf != 0 { - pos, match := p.singleMatch(left) - var increment interface{} - if match == nil { - return false, left, collected - } - leftAlt := make(patternList, len((*left)[:pos]), len((*left)[:pos])+len((*left)[pos+1:])) - copy(leftAlt, (*left)[:pos]) - leftAlt = append(leftAlt, (*left)[pos+1:]...) - sameName := patternList{} - for _, a := range *collected { - if a.name == p.name { - sameName = append(sameName, a) - } - } - - switch p.value.(type) { - case int, []string: - switch p.value.(type) { - case int: - increment = 1 - case []string: - switch match.value.(type) { - case string: - increment = []string{match.value.(string)} - default: - increment = match.value - } - } - if len(sameName) == 0 { - match.value = increment - collectedMatch := make(patternList, len(*collected), len(*collected)+1) - copy(collectedMatch, *collected) - collectedMatch = append(collectedMatch, match) - return true, &leftAlt, &collectedMatch - } - switch sameName[0].value.(type) { - case int: - sameName[0].value = sameName[0].value.(int) + increment.(int) - case []string: - sameName[0].value = append(sameName[0].value.([]string), increment.([]string)...) - } - return true, &leftAlt, collected - } - collectedMatch := make(patternList, len(*collected), len(*collected)+1) - copy(collectedMatch, *collected) - collectedMatch = append(collectedMatch, match) - return true, &leftAlt, &collectedMatch - } - panic("unmatched type") -} - -func (p *pattern) singleMatch(left *patternList) (int, *pattern) { - if p.t&patternArgument != 0 { - for n, pat := range *left { - if pat.t&patternArgument != 0 { - return n, newArgument(p.name, pat.value) - } - } - return -1, nil - } else if p.t&patternCommand != 0 { - for n, pat := range *left { - if pat.t&patternArgument != 0 { - if pat.value == p.name { - return n, newCommand(p.name, true) - } - break - } - } - return -1, nil - } else if p.t&patternOption != 0 { - for n, pat := range *left { - if p.name == pat.name { - return n, pat - } - } - return -1, nil - } - panic("unmatched type") -} - -func (p *pattern) String() string { - if p.t&patternOption != 0 { - return fmt.Sprintf("%s(%s, %s, %d, %+v)", p.t, p.short, p.long, p.argcount, p.value) - } else if p.t&patternLeaf != 0 { - return fmt.Sprintf("%s(%s, %+v)", p.t, p.name, p.value) - } else if p.t&patternBranch != 0 { - result := "" - for i, child := range p.children { - if i > 0 { - result += ", " - } - result += child.String() - } - return fmt.Sprintf("%s(%s)", p.t, result) - } - panic("unmatched type") -} - -func (p *pattern) transform() *pattern { - /* - Expand pattern into an (almost) equivalent one, but with single Either. - - Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) - Quirks: [-a] => (-a), (-a...) => (-a -a) - */ - result := []patternList{} - groups := []patternList{patternList{p}} - parents := patternRequired + - patternOptionAL + - patternOptionSSHORTCUT + - patternEither + - patternOneOrMore - for len(groups) > 0 { - children := groups[0] - groups = groups[1:] - var child *pattern - for _, c := range children { - if c.t&parents != 0 { - child = c - break - } - } - if child != nil { - children.remove(child) - if child.t&patternEither != 0 { - for _, c := range child.children { - r := patternList{} - r = append(r, c) - r = append(r, children...) - groups = append(groups, r) - } - } else if child.t&patternOneOrMore != 0 { - r := patternList{} - r = append(r, child.children.double()...) - r = append(r, children...) - groups = append(groups, r) - } else { - r := patternList{} - r = append(r, child.children...) - r = append(r, children...) - groups = append(groups, r) - } - } else { - result = append(result, children) - } - } - either := patternList{} - for _, e := range result { - either = append(either, newRequired(e...)) - } - return newEither(either...) -} - -func (p *pattern) eq(other *pattern) bool { - return reflect.DeepEqual(p, other) -} - -func (pl patternList) unique() patternList { - table := make(map[string]bool) - result := patternList{} - for _, v := range pl { - if !table[v.String()] { - table[v.String()] = true - result = append(result, v) - } - } - return result -} - -func (pl patternList) index(p *pattern) (int, error) { - for i, c := range pl { - if c.eq(p) { - return i, nil - } - } - return -1, newError("%s not in list", p) -} - -func (pl patternList) count(p *pattern) int { - count := 0 - for _, c := range pl { - if c.eq(p) { - count++ - } - } - return count -} - -func (pl patternList) diff(l patternList) patternList { - lAlt := make(patternList, len(l)) - copy(lAlt, l) - result := make(patternList, 0, len(pl)) - for _, v := range pl { - if v != nil { - match := false - for i, w := range lAlt { - if w.eq(v) { - match = true - lAlt[i] = nil - break - } - } - if match == false { - result = append(result, v) - } - } - } - return result -} - -func (pl patternList) double() patternList { - l := len(pl) - result := make(patternList, l*2) - copy(result, pl) - copy(result[l:2*l], pl) - return result -} - -func (pl *patternList) remove(p *pattern) { - (*pl) = pl.diff(patternList{p}) -} - -func (pl patternList) dictionary() map[string]interface{} { - dict := make(map[string]interface{}) - for _, a := range pl { - dict[a.name] = a.value - } - return dict -} - -func stringPartition(s, sep string) (string, string, string) { - sepPos := strings.Index(s, sep) - if sepPos == -1 { // no seperator found - return s, "", "" - } - split := strings.SplitN(s, sep, 2) - return split[0], sep, split[1] -} - -// returns true if all cased characters in the string are uppercase -// and there are there is at least one cased charcter -func isStringUppercase(s string) bool { - if strings.ToUpper(s) != s { - return false - } - for _, c := range []rune(s) { - if unicode.IsUpper(c) { - return true - } - } - return false -} diff --git a/vendor/github.com/docopt/docopt-go/docopt_test.go b/vendor/github.com/docopt/docopt-go/docopt_test.go deleted file mode 100644 index 945eab52..00000000 --- a/vendor/github.com/docopt/docopt-go/docopt_test.go +++ /dev/null @@ -1,1536 +0,0 @@ -/* -Based of off docopt.py: https://github.com/docopt/docopt - -Licensed under terms of MIT license (see LICENSE-MIT) -Copyright (c) 2013 Keith Batten, kbatten@gmail.com -*/ - -package docopt - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "reflect" - "regexp" - "strings" - "testing" -) - -func TestPatternFlat(t *testing.T) { - q := patternList{ - newArgument("N", nil), - newOption("-a", "", 0, false), - newArgument("M", nil)} - p, err := newRequired( - newOneOrMore(newArgument("N", nil)), - newOption("-a", "", 0, false), - newArgument("M", nil)).flat(patternDefault) - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - q = patternList{newOptionsShortcut()} - p, err = newRequired( - newOptional(newOptionsShortcut()), - newOptional(newOption("-a", "", 0, false))).flat(patternOptionSSHORTCUT) - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - return -} - -func TestOption(t *testing.T) { - if !parseOption("-h").eq(newOption("-h", "", 0, false)) { - t.Fail() - } - if !parseOption("--help").eq(newOption("", "--help", 0, false)) { - t.Fail() - } - if !parseOption("-h --help").eq(newOption("-h", "--help", 0, false)) { - t.Fail() - } - if !parseOption("-h, --help").eq(newOption("-h", "--help", 0, false)) { - t.Fail() - } - - if !parseOption("-h TOPIC").eq(newOption("-h", "", 1, false)) { - t.Fail() - } - if !parseOption("--help TOPIC").eq(newOption("", "--help", 1, false)) { - t.Fail() - } - if !parseOption("-h TOPIC --help TOPIC").eq(newOption("-h", "--help", 1, false)) { - t.Fail() - } - if !parseOption("-h TOPIC, --help TOPIC").eq(newOption("-h", "--help", 1, false)) { - t.Fail() - } - if !parseOption("-h TOPIC, --help=TOPIC").eq(newOption("-h", "--help", 1, false)) { - t.Fail() - } - - if !parseOption("-h Description...").eq(newOption("-h", "", 0, false)) { - t.Fail() - } - if !parseOption("-h --help Description...").eq(newOption("-h", "--help", 0, false)) { - t.Fail() - } - if !parseOption("-h TOPIC Description...").eq(newOption("-h", "", 1, false)) { - t.Fail() - } - - if !parseOption(" -h").eq(newOption("-h", "", 0, false)) { - t.Fail() - } - - if !parseOption("-h TOPIC Description... [default: 2]").eq(newOption("-h", "", 1, "2")) { - t.Fail() - } - if !parseOption("-h TOPIC Descripton... [default: topic-1]").eq(newOption("-h", "", 1, "topic-1")) { - t.Fail() - } - if !parseOption("--help=TOPIC ... [default: 3.14]").eq(newOption("", "--help", 1, "3.14")) { - t.Fail() - } - if !parseOption("-h, --help=DIR ... [default: ./]").eq(newOption("-h", "--help", 1, "./")) { - t.Fail() - } - if !parseOption("-h TOPIC Descripton... [dEfAuLt: 2]").eq(newOption("-h", "", 1, "2")) { - t.Fail() - } - return -} - -func TestOptionName(t *testing.T) { - if newOption("-h", "", 0, false).name != "-h" { - t.Fail() - } - if newOption("-h", "--help", 0, false).name != "--help" { - t.Fail() - } - if newOption("", "--help", 0, false).name != "--help" { - t.Fail() - } - return -} - -func TestCommands(t *testing.T) { - if v, err := Parse("Usage: prog add", []string{"add"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": true}) != true { - t.Error(err) - } - if v, err := Parse("Usage: prog [add]", []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": false}) != true { - t.Error(err) - } - if v, err := Parse("Usage: prog [add]", []string{"add"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": true}) != true { - t.Error(err) - } - if v, err := Parse("Usage: prog (add|rm)", []string{"add"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": true, "rm": false}) != true { - t.Error(err) - } - if v, err := Parse("Usage: prog (add|rm)", []string{"rm"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": false, "rm": true}) != true { - t.Error(err) - } - if v, err := Parse("Usage: prog a b", []string{"a", "b"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"a": true, "b": true}) != true { - t.Error(err) - } - _, err := Parse("Usage: prog a b", []string{"b", "a"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } - return -} - -func TestFormalUsage(t *testing.T) { - doc := ` - Usage: prog [-hv] ARG - prog N M - - prog is a program` - usage := parseSection("usage:", doc)[0] - if usage != "Usage: prog [-hv] ARG\n prog N M" { - t.FailNow() - } - formal, err := formalUsage(usage) - if err != nil { - t.Fatal(err) - } - if formal != "( [-hv] ARG ) | ( N M )" { - t.Fail() - } - return -} - -func TestParseArgv(t *testing.T) { - o := patternList{ - newOption("-h", "", 0, false), - newOption("-v", "--verbose", 0, false), - newOption("-f", "--file", 1, false), - } - - p, err := parseArgv(tokenListFromString(""), &o, false) - q := patternList{} - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - p, err = parseArgv(tokenListFromString("-h"), &o, false) - q = patternList{newOption("-h", "", 0, true)} - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - p, err = parseArgv(tokenListFromString("-h --verbose"), &o, false) - q = patternList{ - newOption("-h", "", 0, true), - newOption("-v", "--verbose", 0, true), - } - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - p, err = parseArgv(tokenListFromString("-h --file f.txt"), &o, false) - q = patternList{ - newOption("-h", "", 0, true), - newOption("-f", "--file", 1, "f.txt"), - } - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - p, err = parseArgv(tokenListFromString("-h --file f.txt arg"), &o, false) - q = patternList{ - newOption("-h", "", 0, true), - newOption("-f", "--file", 1, "f.txt"), - newArgument("", "arg"), - } - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - p, err = parseArgv(tokenListFromString("-h --file f.txt arg arg2"), &o, false) - q = patternList{ - newOption("-h", "", 0, true), - newOption("-f", "--file", 1, "f.txt"), - newArgument("", "arg"), - newArgument("", "arg2"), - } - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } - - p, err = parseArgv(tokenListFromString("-h arg -- -v"), &o, false) - q = patternList{ - newOption("-h", "", 0, true), - newArgument("", "arg"), - newArgument("", "--"), - newArgument("", "-v"), - } - if reflect.DeepEqual(p, q) != true { - t.Error(err) - } -} - -func TestParsePattern(t *testing.T) { - o := patternList{ - newOption("-h", "", 0, false), - newOption("-v", "--verbose", 0, false), - newOption("-f", "--file", 1, false), - } - - p, err := parsePattern("[ -h ]", &o) - q := newRequired(newOptional(newOption("-h", "", 0, false))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("[ ARG ... ]", &o) - q = newRequired(newOptional( - newOneOrMore( - newArgument("ARG", nil)))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("[ -h | -v ]", &o) - q = newRequired( - newOptional( - newEither( - newOption("-h", "", 0, false), - newOption("-v", "--verbose", 0, false)))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("( -h | -v [ --file ] )", &o) - q = newRequired( - newRequired( - newEither( - newOption("-h", "", 0, false), - newRequired( - newOption("-v", "--verbose", 0, false), - newOptional( - newOption("-f", "--file", 1, nil)))))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("(-h|-v[--file=]N...)", &o) - q = newRequired( - newRequired( - newEither( - newOption("-h", "", 0, false), - newRequired( - newOption("-v", "--verbose", 0, false), - newOptional( - newOption("-f", "--file", 1, nil)), - newOneOrMore( - newArgument("N", nil)))))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("(N [M | (K | L)] | O P)", &o) - q = newRequired( - newRequired( - newEither( - newRequired( - newArgument("N", nil), - newOptional( - newEither( - newArgument("M", nil), - newRequired( - newEither( - newArgument("K", nil), - newArgument("L", nil)))))), - newRequired( - newArgument("O", nil), - newArgument("P", nil))))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("[ -h ] [N]", &o) - q = newRequired( - newOptional( - newOption("-h", "", 0, false)), - newOptional( - newArgument("N", nil))) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("[options]", &o) - q = newRequired( - newOptional( - newOptionsShortcut())) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("[options] A", &o) - q = newRequired( - newOptional( - newOptionsShortcut()), - newArgument("A", nil)) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("-v [options]", &o) - q = newRequired( - newOption("-v", "--verbose", 0, false), - newOptional( - newOptionsShortcut())) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("ADD", &o) - q = newRequired(newArgument("ADD", nil)) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("", &o) - q = newRequired(newArgument("", nil)) - if p.eq(q) != true { - t.Error(err) - } - - p, err = parsePattern("add", &o) - q = newRequired(newCommand("add", false)) - if p.eq(q) != true { - t.Error(err) - } -} - -func TestOptionMatch(t *testing.T) { - v, w, x := newOption("-a", "", 0, false).match( - &patternList{newOption("-a", "", 0, true)}, nil) - y := patternList{newOption("-a", "", 0, true)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newOption("-a", "", 0, false).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newOption("-a", "", 0, false).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - v, w, x = newOption("-a", "", 0, false).match( - &patternList{newArgument("N", nil)}, nil) - y = patternList{newArgument("N", nil)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newOption("-a", "", 0, false).match( - &patternList{ - newOption("-x", "", 0, false), - newOption("-a", "", 0, false), - newArgument("N", nil)}, nil) - y = patternList{ - newOption("-x", "", 0, false), - newArgument("N", nil)} - z := patternList{newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOption("-a", "", 0, false).match( - &patternList{ - newOption("-a", "", 0, true), - newOption("-a", "", 0, false)}, nil) - y = patternList{newOption("-a", "", 0, false)} - z = patternList{newOption("-a", "", 0, true)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } -} - -func TestArgumentMatch(t *testing.T) { - v, w, x := newArgument("N", nil).match( - &patternList{newArgument("N", 9)}, nil) - y := patternList{newArgument("N", 9)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newArgument("N", nil).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newArgument("N", nil).match( - &patternList{newOption("-x", "", 0, false), - newOption("-a", "", 0, false), - newArgument("", 5)}, nil) - y = patternList{newOption("-x", "", 0, false), - newOption("-a", "", 0, false)} - z := patternList{newArgument("N", 5)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newArgument("N", nil).match( - &patternList{newArgument("", 9), - newArgument("", 0)}, nil) - y = patternList{newArgument("", 0)} - z = patternList{newArgument("N", 9)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } -} - -func TestCommandMatch(t *testing.T) { - v, w, x := newCommand("c", false).match( - &patternList{newArgument("", "c")}, nil) - y := patternList{newCommand("c", true)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newCommand("c", false).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newCommand("c", false).match( - &patternList{ - newOption("-x", "", 0, false), - newOption("-a", "", 0, false), - newArgument("", "c")}, nil) - y = patternList{newOption("-x", "", 0, false), - newOption("-a", "", 0, false)} - z := patternList{newCommand("c", true)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newEither( - newCommand("add", false), - newCommand("rm", false)).match( - &patternList{newArgument("", "rm")}, nil) - y = patternList{newCommand("rm", true)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } -} - -func TestOptionalMatch(t *testing.T) { - v, w, x := newOptional(newOption("-a", "", 0, false)).match( - &patternList{newOption("-a", "", 0, false)}, nil) - y := patternList{newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newOptional(newOption("-a", "", 0, false)).match( - &patternList{}, nil) - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newOptional(newOption("-a", "", 0, false)).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newOptional(newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match( - &patternList{newOption("-a", "", 0, false)}, nil) - y = patternList{newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newOptional(newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match( - &patternList{newOption("-b", "", 0, false)}, nil) - y = patternList{newOption("-b", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newOptional(newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newOptional(newArgument("N", nil)).match( - &patternList{newArgument("", 9)}, nil) - y = patternList{newArgument("N", 9)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newOptional(newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match( - &patternList{newOption("-b", "", 0, false), - newOption("-x", "", 0, false), - newOption("-a", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - z := patternList{newOption("-a", "", 0, false), - newOption("-b", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } -} - -func TestRequiredMatch(t *testing.T) { - v, w, x := newRequired(newOption("-a", "", 0, false)).match( - &patternList{newOption("-a", "", 0, false)}, nil) - y := patternList{newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newRequired(newOption("-a", "", 0, false)).match(&patternList{}, nil) - if v != false || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - - v, w, x = newRequired(newOption("-a", "", 0, false)).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } - v, w, x = newRequired(newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match( - &patternList{newOption("-a", "", 0, false)}, nil) - y = patternList{newOption("-a", "", 0, false)} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, patternList{}) != true { - t.Fail() - } -} - -func TestEitherMatch(t *testing.T) { - v, w, x := newEither( - newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match( - &patternList{newOption("-a", "", 0, false)}, nil) - y := patternList{newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newEither( - newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match(&patternList{ - newOption("-a", "", 0, false), - newOption("-b", "", 0, false)}, nil) - y = patternList{newOption("-b", "", 0, false)} - z := patternList{newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newEither( - newOption("-a", "", 0, false), - newOption("-b", "", 0, false)).match(&patternList{ - newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - z = patternList{} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newEither( - newOption("-a", "", 0, false), - newOption("-b", "", 0, false), - newOption("-c", "", 0, false)).match(&patternList{ - newOption("-x", "", 0, false), - newOption("-b", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - z = patternList{newOption("-b", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - v, w, x = newEither( - newArgument("M", nil), - newRequired(newArgument("N", nil), - newArgument("M", nil))).match(&patternList{ - newArgument("", 1), - newArgument("", 2)}, nil) - y = patternList{} - z = patternList{newArgument("N", 1), newArgument("M", 2)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } -} - -func TestOneOrMoreMatch(t *testing.T) { - v, w, x := newOneOrMore(newArgument("N", nil)).match( - &patternList{newArgument("", 9)}, nil) - y := patternList{newArgument("N", 9)} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newArgument("N", nil)).match( - &patternList{}, nil) - y = patternList{} - z := patternList{} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newArgument("N", nil)).match( - &patternList{newOption("-x", "", 0, false)}, nil) - y = patternList{newOption("-x", "", 0, false)} - z = patternList{} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newArgument("N", nil)).match( - &patternList{newArgument("", 9), newArgument("", 8)}, nil) - y = patternList{} - z = patternList{newArgument("N", 9), newArgument("N", 8)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newArgument("N", nil)).match(&patternList{ - newArgument("", 9), - newOption("-x", "", 0, false), - newArgument("", 8)}, nil) - y = patternList{newOption("-x", "", 0, false)} - z = patternList{newArgument("N", 9), newArgument("N", 8)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newOption("-a", "", 0, false)).match(&patternList{ - newOption("-a", "", 0, false), - newArgument("", 8), - newOption("-a", "", 0, false)}, nil) - y = patternList{newArgument("", 8)} - z = patternList{newOption("-a", "", 0, false), newOption("-a", "", 0, false)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newOption("-a", "", 0, false)).match(&patternList{ - newArgument("", 8), - newOption("-x", "", 0, false)}, nil) - y = patternList{newArgument("", 8), newOption("-x", "", 0, false)} - z = patternList{} - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newRequired(newOption("-a", "", 0, false), - newArgument("N", nil))).match(&patternList{ - newOption("-a", "", 0, false), - newArgument("", 1), - newOption("-x", "", 0, false), - newOption("-a", "", 0, false), - newArgument("", 2)}, nil) - y = patternList{newOption("-x", "", 0, false)} - z = patternList{newOption("-a", "", 0, false), - newArgument("N", 1), - newOption("-a", "", 0, false), - newArgument("N", 2)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - v, w, x = newOneOrMore(newOptional(newArgument("N", nil))).match( - &patternList{newArgument("", 9)}, nil) - y = patternList{} - z = patternList{newArgument("N", 9)} - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } -} - -func TestListArgumentMatch(t *testing.T) { - p := newRequired( - newArgument("N", nil), - newArgument("N", nil)) - p.fix() - v, w, x := p.match(&patternList{newArgument("", "1"), - newArgument("", "2")}, nil) - y := patternList{newArgument("N", []string{"1", "2"})} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - p = newOneOrMore(newArgument("N", nil)) - p.fix() - v, w, x = p.match(&patternList{newArgument("", "1"), - newArgument("", "2"), newArgument("", "3")}, nil) - y = patternList{newArgument("N", []string{"1", "2", "3"})} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - p = newRequired(newArgument("N", nil), - newOneOrMore(newArgument("N", nil))) - p.fix() - v, w, x = p.match(&patternList{ - newArgument("", "1"), - newArgument("", "2"), - newArgument("", "3")}, nil) - y = patternList{newArgument("N", []string{"1", "2", "3"})} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - p = newRequired(newArgument("N", nil), - newRequired(newArgument("N", nil))) - p.fix() - v, w, x = p.match(&patternList{ - newArgument("", "1"), - newArgument("", "2")}, nil) - y = patternList{newArgument("N", []string{"1", "2"})} - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } -} - -func TestBasicPatternMatching(t *testing.T) { - // ( -a N [ -x Z ] ) - p := newRequired( - newOption("-a", "", 0, false), - newArgument("N", nil), - newOptional( - newOption("-x", "", 0, false), - newArgument("Z", nil))) - - // -a N - q := patternList{newOption("-a", "", 0, false), newArgument("", 9)} - y := patternList{newOption("-a", "", 0, false), newArgument("N", 9)} - v, w, x := p.match(&q, nil) - if v != true || - reflect.DeepEqual(*w, patternList{}) != true || - reflect.DeepEqual(*x, y) != true { - t.Fail() - } - - // -a -x N Z - q = patternList{newOption("-a", "", 0, false), - newOption("-x", "", 0, false), - newArgument("", 9), newArgument("", 5)} - y = patternList{} - z := patternList{newOption("-a", "", 0, false), newArgument("N", 9), - newOption("-x", "", 0, false), newArgument("Z", 5)} - v, w, x = p.match(&q, nil) - if v != true || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } - - // -x N Z # BZZ! - q = patternList{newOption("-x", "", 0, false), - newArgument("", 9), newArgument("", 5)} - y = patternList{newOption("-x", "", 0, false), - newArgument("", 9), newArgument("", 5)} - z = patternList{} - v, w, x = p.match(&q, nil) - if v != false || - reflect.DeepEqual(*w, y) != true || - reflect.DeepEqual(*x, z) != true { - t.Fail() - } -} - -func TestPatternEither(t *testing.T) { - p := newOption("-a", "", 0, false).transform() - q := newEither(newRequired( - newOption("-a", "", 0, false))) - if p.eq(q) != true { - t.Fail() - } - - p = newArgument("A", nil).transform() - q = newEither(newRequired( - newArgument("A", nil))) - if p.eq(q) != true { - t.Fail() - } - - p = newRequired( - newEither( - newOption("-a", "", 0, false), - newOption("-b", "", 0, false)), - newOption("-c", "", 0, false)).transform() - q = newEither( - newRequired( - newOption("-a", "", 0, false), - newOption("-c", "", 0, false)), - newRequired( - newOption("-b", "", 0, false), - newOption("-c", "", 0, false))) - if p.eq(q) != true { - t.Fail() - } - - p = newOptional(newOption("-a", "", 0, false), - newEither(newOption("-b", "", 0, false), - newOption("-c", "", 0, false))).transform() - q = newEither( - newRequired( - newOption("-b", "", 0, false), newOption("-a", "", 0, false)), - newRequired( - newOption("-c", "", 0, false), newOption("-a", "", 0, false))) - if p.eq(q) != true { - t.Fail() - } - - p = newEither(newOption("-x", "", 0, false), - newEither(newOption("-y", "", 0, false), - newOption("-z", "", 0, false))).transform() - q = newEither( - newRequired(newOption("-x", "", 0, false)), - newRequired(newOption("-y", "", 0, false)), - newRequired(newOption("-z", "", 0, false))) - if p.eq(q) != true { - t.Fail() - } - - p = newOneOrMore(newArgument("N", nil), - newArgument("M", nil)).transform() - q = newEither( - newRequired(newArgument("N", nil), newArgument("M", nil), - newArgument("N", nil), newArgument("M", nil))) - if p.eq(q) != true { - t.Fail() - } -} - -func TestPatternFixRepeatingArguments(t *testing.T) { - p := newOption("-a", "", 0, false) - p.fixRepeatingArguments() - if p.eq(newOption("-a", "", 0, false)) != true { - t.Fail() - } - - p = newArgument("N", nil) - p.fixRepeatingArguments() - if p.eq(newArgument("N", nil)) != true { - t.Fail() - } - - p = newRequired( - newArgument("N", nil), - newArgument("N", nil)) - q := newRequired( - newArgument("N", []string{}), - newArgument("N", []string{})) - p.fixRepeatingArguments() - if p.eq(q) != true { - t.Fail() - } - - p = newEither( - newArgument("N", nil), - newOneOrMore(newArgument("N", nil))) - q = newEither( - newArgument("N", []string{}), - newOneOrMore(newArgument("N", []string{}))) - p.fix() - if p.eq(q) != true { - t.Fail() - } -} - -func TestSet(t *testing.T) { - p := newArgument("N", nil) - q := newArgument("N", nil) - if reflect.DeepEqual(p, q) != true { - t.Fail() - } - pl := patternList{newArgument("N", nil), newArgument("N", nil)} - ql := patternList{newArgument("N", nil)} - if reflect.DeepEqual(pl.unique(), ql.unique()) != true { - t.Fail() - } -} - -func TestPatternFixIdentities1(t *testing.T) { - p := newRequired( - newArgument("N", nil), - newArgument("N", nil)) - if len(p.children) < 2 { - t.FailNow() - } - if p.children[0].eq(p.children[1]) != true { - t.Fail() - } - if p.children[0] == p.children[1] { - t.Fail() - } - p.fixIdentities(nil) - if p.children[0] != p.children[1] { - t.Fail() - } -} - -func TestPatternFixIdentities2(t *testing.T) { - p := newRequired( - newOptional( - newArgument("X", nil), - newArgument("N", nil)), - newArgument("N", nil)) - if len(p.children) < 2 { - t.FailNow() - } - if len(p.children[0].children) < 2 { - t.FailNow() - } - if p.children[0].children[1].eq(p.children[1]) != true { - t.Fail() - } - if p.children[0].children[1] == p.children[1] { - t.Fail() - } - p.fixIdentities(nil) - if p.children[0].children[1] != p.children[1] { - t.Fail() - } -} - -func TestLongOptionsErrorHandling(t *testing.T) { - _, err := Parse("Usage: prog", []string{"--non-existent"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(fmt.Sprintf("(%s) %s", reflect.TypeOf(err), err)) - } - _, err = Parse("Usage: prog [--version --verbose]\nOptions: --version\n --verbose", - []string{"--ver"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } - _, err = Parse("Usage: prog --long\nOptions: --long ARG", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } - _, err = Parse("Usage: prog --long ARG\nOptions: --long ARG", - []string{"--long"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(fmt.Sprintf("(%s) %s", reflect.TypeOf(err), err)) - } - _, err = Parse("Usage: prog --long=ARG\nOptions: --long", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } - _, err = Parse("Usage: prog --long\nOptions: --long", - []string{}, true, "--long=ARG", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } -} - -func TestShortOptionsErrorHandling(t *testing.T) { - _, err := Parse("Usage: prog -x\nOptions: -x this\n -x that", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(fmt.Sprintf("(%s) %s", reflect.TypeOf(err), err)) - } - _, err = Parse("Usage: prog", []string{"-x"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } - _, err = Parse("Usage: prog -o\nOptions: -o ARG", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } - _, err = Parse("Usage: prog -o ARG\nOptions: -o ARG", []string{"-o"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } -} - -func TestMatchingParen(t *testing.T) { - _, err := Parse("Usage: prog [a [b]", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } - _, err = Parse("Usage: prog [a [b] ] c )", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } -} - -func TestAllowDoubleDash(t *testing.T) { - if v, err := Parse("usage: prog [-o] [--] \noptions: -o", []string{"--", "-o"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-o": false, "": "-o", "--": true}) != true { - t.Error(err) - } - if v, err := Parse("usage: prog [-o] [--] \noptions: -o", []string{"-o", "1"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-o": true, "": "1", "--": false}) != true { - t.Error(err) - } - _, err := Parse("usage: prog [-o] \noptions:-o", []string{"-o"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { //"--" is not allowed; FIXME? - t.Error(err) - } -} - -func TestDocopt(t *testing.T) { - doc := `Usage: prog [-v] A - - Options: -v Be verbose.` - if v, err := Parse(doc, []string{"arg"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": false, "A": "arg"}) != true { - t.Error(err) - } - if v, err := Parse(doc, []string{"-v", "arg"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true, "A": "arg"}) != true { - t.Error(err) - } - - doc = `Usage: prog [-vqr] [FILE] - prog INPUT OUTPUT - prog --help - - Options: - -v print status messages - -q report only file names - -r show all occurrences of the same error - --help - - ` - if v, err := Parse(doc, []string{"-v", "file.py"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true, "-q": false, "-r": false, "--help": false, "FILE": "file.py", "INPUT": nil, "OUTPUT": nil}) != true { - t.Error(err) - } - if v, err := Parse(doc, []string{"-v"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true, "-q": false, "-r": false, "--help": false, "FILE": nil, "INPUT": nil, "OUTPUT": nil}) != true { - t.Error(err) - } - - _, err := Parse(doc, []string{"-v", "input.py", "output.py"}, true, "", false, false) // does not match - if _, ok := err.(*UserError); !ok { - t.Error(err) - } - _, err = Parse(doc, []string{"--fake"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } - _, output, err := parseOutput(doc, []string{"--hel"}, true, "", false) - if err != nil || len(output) == 0 { - t.Error(err) - } -} - -func TestLanguageErrors(t *testing.T) { - _, err := Parse("no usage with colon here", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } - _, err = Parse("usage: here \n\n and again usage: here", []string{}, true, "", false, false) - if _, ok := err.(*LanguageError); !ok { - t.Error(err) - } -} - -func TestIssue40(t *testing.T) { - _, output, err := parseOutput("usage: prog --help-commands | --help", []string{"--help"}, true, "", false) - if err != nil || len(output) == 0 { - t.Error(err) - } - if v, err := Parse("usage: prog --aabb | --aa", []string{"--aa"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--aabb": false, "--aa": true}) != true { - t.Error(err) - } -} - -func TestIssue34UnicodeStrings(t *testing.T) { - // TODO: see if applicable -} - -func TestCountMultipleFlags(t *testing.T) { - if v, err := Parse("usage: prog [-v]", []string{"-v"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true}) != true { - t.Error(err) - } - if v, err := Parse("usage: prog [-vv]", []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 0}) != true { - t.Error(err) - } - if v, err := Parse("usage: prog [-vv]", []string{"-v"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 1}) != true { - t.Error(err) - } - if v, err := Parse("usage: prog [-vv]", []string{"-vv"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 2}) != true { - t.Error(err) - } - _, err := Parse("usage: prog [-vv]", []string{"-vvv"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Error(err) - } - if v, err := Parse("usage: prog [-v | -vv | -vvv]", []string{"-vvv"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 3}) != true { - t.Error(err) - } - if v, err := Parse("usage: prog [-v...]", []string{"-vvvvvv"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 6}) != true { - t.Error(err) - } - if v, err := Parse("usage: prog [--ver --ver]", []string{"--ver", "--ver"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--ver": 2}) != true { - t.Error(err) - } -} - -func TestAnyOptionsParameter(t *testing.T) { - _, err := Parse("usage: prog [options]", - []string{"-foo", "--bar", "--spam=eggs"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Fail() - } - - _, err = Parse("usage: prog [options]", - []string{"--foo", "--bar", "--bar"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Fail() - } - _, err = Parse("usage: prog [options]", - []string{"--bar", "--bar", "--bar", "-ffff"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Fail() - } - _, err = Parse("usage: prog [options]", - []string{"--long=arg", "--long=another"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Fail() - } -} - -func TestDefaultValueForPositionalArguments(t *testing.T) { - doc := "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [default: x]" - if v, err := Parse(doc, []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--data": []string{"x"}}) != true { - t.Error(err) - } - - doc = "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [default: x y]" - if v, err := Parse(doc, []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--data": []string{"x", "y"}}) != true { - t.Error(err) - } - - doc = "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [default: x y]" - if v, err := Parse(doc, []string{"--data=this"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--data": []string{"this"}}) != true { - t.Error(err) - } -} - -func TestIssue59(t *testing.T) { - if v, err := Parse("usage: prog --long=", []string{"--long="}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--long": ""}) != true { - t.Error(err) - } - - if v, err := Parse("usage: prog -l \noptions: -l ", []string{"-l", ""}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-l": ""}) != true { - t.Error(err) - } -} - -func TestOptionsFirst(t *testing.T) { - if v, err := Parse("usage: prog [--opt] [...]", []string{"--opt", "this", "that"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--opt": true, "": []string{"this", "that"}}) != true { - t.Error(err) - } - - if v, err := Parse("usage: prog [--opt] [...]", []string{"this", "that", "--opt"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--opt": true, "": []string{"this", "that"}}) != true { - t.Error(err) - } - - if v, err := Parse("usage: prog [--opt] [...]", []string{"this", "that", "--opt"}, true, "", true, false); reflect.DeepEqual(v, map[string]interface{}{"--opt": false, "": []string{"this", "that", "--opt"}}) != true { - t.Error(err) - } -} - -func TestIssue68OptionsShortcutDoesNotIncludeOptionsInUsagePattern(t *testing.T) { - args, err := Parse("usage: prog [-ab] [options]\noptions: -x\n -y", []string{"-ax"}, true, "", false, false) - - if args["-a"] != true { - t.Error(err) - } - if args["-b"] != false { - t.Error(err) - } - if args["-x"] != true { - t.Error(err) - } - if args["-y"] != false { - t.Error(err) - } -} - -func TestIssue65EvaluateArgvWhenCalledNotWhenImported(t *testing.T) { - os.Args = strings.Fields("prog -a") - v, err := Parse("usage: prog [-ab]", nil, true, "", false, false) - w := map[string]interface{}{"-a": true, "-b": false} - if reflect.DeepEqual(v, w) != true { - t.Error(err) - } - - os.Args = strings.Fields("prog -b") - v, err = Parse("usage: prog [-ab]", nil, true, "", false, false) - w = map[string]interface{}{"-a": false, "-b": true} - if reflect.DeepEqual(v, w) != true { - t.Error(err) - } -} - -func TestIssue71DoubleDashIsNotAValidOptionArgument(t *testing.T) { - _, err := Parse("usage: prog [--log=LEVEL] [--] ...", - []string{"--log", "--", "1", "2"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Fail() - } - - _, err = Parse(`usage: prog [-l LEVEL] [--] ... - options: -l LEVEL`, []string{"-l", "--", "1", "2"}, true, "", false, false) - if _, ok := err.(*UserError); !ok { - t.Fail() - } -} - -func TestParseSection(t *testing.T) { - v := parseSection("usage:", "foo bar fizz buzz") - w := []string{} - if reflect.DeepEqual(v, w) != true { - t.Fail() - } - - v = parseSection("usage:", "usage: prog") - w = []string{"usage: prog"} - if reflect.DeepEqual(v, w) != true { - t.Fail() - } - - v = parseSection("usage:", "usage: -x\n -y") - w = []string{"usage: -x\n -y"} - if reflect.DeepEqual(v, w) != true { - t.Fail() - } - - usage := `usage: this - -usage:hai -usage: this that - -usage: foo - bar - -PROGRAM USAGE: - foo - bar -usage: -` + "\t" + `too -` + "\t" + `tar -Usage: eggs spam -BAZZ -usage: pit stop` - - v = parseSection("usage:", usage) - w = []string{"usage: this", - "usage:hai", - "usage: this that", - "usage: foo\n bar", - "PROGRAM USAGE:\n foo\n bar", - "usage:\n\ttoo\n\ttar", - "Usage: eggs spam", - "usage: pit stop", - } - if reflect.DeepEqual(v, w) != true { - t.Fail() - } -} - -func TestIssue126DefaultsNotParsedCorrectlyWhenTabs(t *testing.T) { - section := "Options:\n\t--foo= [default: bar]" - v := patternList{newOption("", "--foo", 1, "bar")} - if reflect.DeepEqual(parseDefaults(section), v) != true { - t.Fail() - } -} - -// conf file based test cases -func TestFileTestcases(t *testing.T) { - filenames := []string{"testcases.docopt", "test_golang.docopt"} - for _, filename := range filenames { - raw, err := ioutil.ReadFile(filename) - if err != nil { - t.Fatal(err) - } - - tests, err := parseTest(raw) - if err != nil { - t.Fatal(err) - } - for _, c := range tests { - result, err := Parse(c.doc, c.argv, true, "", false, false) - if _, ok := err.(*UserError); c.userError && !ok { - // expected a user-error - t.Error("testcase:", c.id, "result:", result) - } else if _, ok := err.(*UserError); !c.userError && ok { - // unexpected user-error - t.Error("testcase:", c.id, "error:", err, "result:", result) - } else if reflect.DeepEqual(c.expect, result) != true { - t.Error("testcase:", c.id, "result:", result, "expect:", c.expect) - } - } - } -} - -type testcase struct { - id int - doc string - prog string - argv []string - expect map[string]interface{} - userError bool -} - -func parseTest(raw []byte) ([]testcase, error) { - var res []testcase - commentPattern := regexp.MustCompile("#.*") - raw = commentPattern.ReplaceAll(raw, []byte("")) - raw = bytes.TrimSpace(raw) - if bytes.HasPrefix(raw, []byte(`"""`)) { - raw = raw[3:] - } - - id := 0 - for _, fixture := range bytes.Split(raw, []byte(`r"""`)) { - doc, _, body := stringPartition(string(fixture), `"""`) - for _, cas := range strings.Split(body, "$")[1:] { - argvString, _, expectString := stringPartition(strings.TrimSpace(cas), "\n") - prog, _, argvString := stringPartition(strings.TrimSpace(argvString), " ") - argv := []string{} - if len(argvString) > 0 { - argv = strings.Fields(argvString) - } - var expectUntyped interface{} - err := json.Unmarshal([]byte(expectString), &expectUntyped) - if err != nil { - return nil, err - } - switch expect := expectUntyped.(type) { - case string: // user-error - res = append(res, testcase{id, doc, prog, argv, nil, true}) - case map[string]interface{}: - // convert []interface{} values to []string - // convert float64 values to int - for k, vUntyped := range expect { - switch v := vUntyped.(type) { - case []interface{}: - itemList := make([]string, len(v)) - for i, itemUntyped := range v { - if item, ok := itemUntyped.(string); ok { - itemList[i] = item - } - } - expect[k] = itemList - case float64: - expect[k] = int(v) - } - } - res = append(res, testcase{id, doc, prog, argv, expect, false}) - default: - return nil, fmt.Errorf("unhandled json data type") - } - id++ - } - } - return res, nil -} - -// parseOutput wraps the Parse() function to also return stdout -func parseOutput(doc string, argv []string, help bool, version string, - optionsFirst bool) (map[string]interface{}, string, error) { - stdout := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - - args, err := Parse(doc, argv, help, version, optionsFirst, false) - - outChan := make(chan string) - go func() { - var buf bytes.Buffer - io.Copy(&buf, r) - outChan <- buf.String() - }() - - w.Close() - os.Stdout = stdout - output := <-outChan - - return args, output, err -} - -var debugEnabled = false - -func debugOn(l ...interface{}) { - debugEnabled = true - debug(l...) -} -func debugOff(l ...interface{}) { - debug(l...) - debugEnabled = false -} - -func debug(l ...interface{}) { - if debugEnabled { - fmt.Println(l...) - } -} diff --git a/vendor/github.com/docopt/docopt-go/example_test.go b/vendor/github.com/docopt/docopt-go/example_test.go deleted file mode 100644 index b87a149a..00000000 --- a/vendor/github.com/docopt/docopt-go/example_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package docopt - -import ( - "fmt" - "sort" -) - -func ExampleParse() { - usage := `Usage: - config_example tcp [] [--force] [--timeout=] - config_example serial [--baud=] [--timeout=] - config_example -h | --help | --version` - // parse the command line `comfig_example tcp 127.0.0.1 --force` - argv := []string{"tcp", "127.0.0.1", "--force"} - arguments, _ := Parse(usage, argv, true, "0.1.1rc", false) - // sort the keys of the arguments map - var keys []string - for k := range arguments { - keys = append(keys, k) - } - sort.Strings(keys) - // print the argument keys and values - for _, k := range keys { - fmt.Printf("%9s %v\n", k, arguments[k]) - } - // output: - // --baud - // --force true - // --help false - // --timeout - // --version false - // -h false - // 127.0.0.1 - // - // serial false - // tcp true -} diff --git a/vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go b/vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go deleted file mode 100644 index 7555b99f..00000000 --- a/vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "fmt" - "github.com/docopt/docopt-go" -) - -func main() { - usage := `Usage: arguments_example [-vqrh] [FILE] ... - arguments_example (--left | --right) CORRECTION FILE - -Process FILE and optionally apply correction to either left-hand side or -right-hand side. - -Arguments: - FILE optional input file - CORRECTION correction angle, needs FILE, --left or --right to be present - -Options: - -h --help - -v verbose mode - -q quiet mode - -r make report - --left use left-hand side - --right use right-hand side` - - arguments, _ := docopt.Parse(usage, nil, true, "", false) - fmt.Println(arguments) -} diff --git a/vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go b/vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go deleted file mode 100644 index 26c3b32e..00000000 --- a/vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "fmt" - "github.com/docopt/docopt-go" -) - -func main() { - usage := `Not a serious example. - -Usage: - calculator_example ( ( + | - | * | / ) )... - calculator_example [( , )]... - calculator_example (-h | --help) - -Examples: - calculator_example 1 + 2 + 3 + 4 + 5 - calculator_example 1 + 2 '*' 3 / 4 - 5 # note quotes around '*' - calculator_example sum 10 , 20 , 30 , 40 - -Options: - -h, --help -` - arguments, _ := docopt.Parse(usage, nil, true, "", false) - fmt.Println(arguments) -} diff --git a/vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go b/vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go deleted file mode 100644 index b0b1c180..00000000 --- a/vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "github.com/docopt/docopt-go" - "strings" -) - -func loadJSONConfig() map[string]interface{} { - var result map[string]interface{} - jsonData := []byte(`{"--force": true, "--timeout": "10", "--baud": "9600"}`) - json.Unmarshal(jsonData, &result) - return result -} - -func loadIniConfig() map[string]interface{} { - iniData := ` -[default-arguments] ---force ---baud=19200 -=localhost` - // trivial ini parser - // default value for an item is bool: true (for --force) - // otherwise the value is a string - iniParsed := make(map[string]map[string]interface{}) - var section string - for _, line := range strings.Split(iniData, "\n") { - if strings.HasPrefix(line, "[") { - section = line - iniParsed[section] = make(map[string]interface{}) - } else if section != "" { - kv := strings.SplitN(line, "=", 2) - if len(kv) == 1 { - iniParsed[section][kv[0]] = true - } else if len(kv) == 2 { - iniParsed[section][kv[0]] = kv[1] - } - } - } - return iniParsed["[default-arguments]"] -} - -// merge combines two maps. -// truthiness takes priority over falsiness -// mapA takes priority over mapB -func merge(mapA, mapB map[string]interface{}) map[string]interface{} { - result := make(map[string]interface{}) - for k, v := range mapA { - result[k] = v - } - for k, v := range mapB { - if _, ok := result[k]; !ok || result[k] == nil || result[k] == false { - result[k] = v - } - } - return result -} - -func main() { - usage := `Usage: - config_file_example tcp [] [--force] [--timeout=] - config_file_example serial [--baud=] [--timeout=] - config_file_example -h | --help | --version` - - jsonConfig := loadJSONConfig() - iniConfig := loadIniConfig() - arguments, _ := docopt.Parse(usage, nil, true, "0.1.1rc", false) - - // Arguments take priority over INI, INI takes priority over JSON - result := merge(arguments, merge(iniConfig, jsonConfig)) - - fmt.Println("JSON config: ", jsonConfig) - fmt.Println("INI config: ", iniConfig) - fmt.Println("Result: ", result) -} diff --git a/vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go b/vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go deleted file mode 100644 index c1da06f2..00000000 --- a/vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "fmt" - "github.com/docopt/docopt-go" -) - -func main() { - usage := `Usage: counted_example --help - counted_example -v... - counted_example go [go] - counted_example (--path=)... - counted_example - -Try: counted_example -vvvvvvvvvv - counted_example go go - counted_example --path ./here --path ./there - counted_example this.txt that.txt` - - arguments, _ := docopt.Parse(usage, nil, true, "", false) - fmt.Println(arguments) -} diff --git a/vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go b/vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go deleted file mode 100644 index 47a48841..00000000 --- a/vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go +++ /dev/null @@ -1,38 +0,0 @@ -package git - -import ( - "fmt" - "github.com/docopt/docopt-go" -) - -func main() { - usage := `usage: git branch [options] [-r | -a] [--merged= | --no-merged=] - git branch [options] [-l] [-f] [] - git branch [options] [-r] (-d | -D) - git branch [options] (-m | -M) [] - -Generic options: - -h, --help - -v, --verbose show hash and subject, give twice for upstream branch - -t, --track set up tracking mode (see git-pull(1)) - --set-upstream change upstream info - --color= use colored output - -r act on remote-tracking branches - --contains= print only branches that contain the commit - --abbrev= use digits to display SHA-1s - -Specific git-branch actions: - -a list both remote-tracking and local branches - -d delete fully merged branch - -D delete branch (even if not merged) - -m move/rename a branch and its reflog - -M move/rename a branch, even if target exists - -l create the branch's reflog - -f, --force force creation (when already exists) - --no-merged= print only not merged branches - --merged= print only merged branches -` - - args, _ := docopt.Parse(usage, nil, true, "", false) - fmt.Println(args) -} diff --git a/vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go b/vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go deleted file mode 100644 index 00fe71ce..00000000 --- a/vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go +++ /dev/null @@ -1,30 +0,0 @@ -package git - -import ( - "fmt" - "github.com/docopt/docopt-go" -) - -func main() { - usage := `usage: git checkout [options] - git checkout [options] -- ... - -options: - -q, --quiet suppress progress reporting - -b create and checkout a new branch - -B create/reset and checkout a branch - -l create reflog for new branch - -t, --track set upstream info for new branch - --orphan - new unparented branch - -2, --ours checkout our version for unmerged files - -3, --theirs checkout their version for unmerged files - -f, --force force checkout (throw away local modifications) - -m, --merge perform a 3-way merge with the new branch - --conflict