mirror of
https://github.com/fatedier/frp.git
synced 2026-04-05 08:39:17 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c008b14d0f | ||
|
|
853892f3cd | ||
|
|
e43f9f5850 | ||
|
|
d5f30ccd6b | ||
|
|
b87df569e7 | ||
|
|
976cf3e9f8 | ||
|
|
371c401f5b | ||
|
|
69919e8ef9 | ||
|
|
9abbe33790 | ||
|
|
4a5c00286e | ||
|
|
dfb892c8f6 | ||
|
|
461c4c18fd | ||
|
|
00b9ba95ae | ||
|
|
c47aad348d | ||
|
|
4cb4da3afc | ||
|
|
c1f57da00d | ||
|
|
fe187eb8ec | ||
|
|
0f6f674a64 | ||
|
|
814afbe1f6 | ||
|
|
3fde9176c9 | ||
|
|
af7cca1a93 | ||
|
|
7dd28a14aa |
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.8
|
FROM golang:1.10
|
||||||
|
|
||||||
COPY . /go/src/github.com/fatedier/frp
|
COPY . /go/src/github.com/fatedier/frp
|
||||||
|
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -39,7 +39,7 @@ ci:
|
|||||||
go test -v ./tests/...
|
go test -v ./tests/...
|
||||||
cd ./tests && ./clean_test.sh && cd -
|
cd ./tests && ./clean_test.sh && cd -
|
||||||
|
|
||||||
ciclean:
|
cic:
|
||||||
cd ./tests && ./clean_test.sh && cd -
|
cd ./tests && ./clean_test.sh && cd -
|
||||||
|
|
||||||
alltest: gotest ci
|
alltest: gotest ci
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -31,8 +31,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
|||||||
* [Encryption and Compression](#encryption-and-compression)
|
* [Encryption and Compression](#encryption-and-compression)
|
||||||
* [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
|
* [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
|
||||||
* [Get proxy status from client](#get-proxy-status-from-client)
|
* [Get proxy status from client](#get-proxy-status-from-client)
|
||||||
* [Privilege Mode](#privilege-mode)
|
* [Port White List](#port-white-list)
|
||||||
* [Port White List](#port-white-list)
|
|
||||||
* [TCP Stream Multiplexing](#tcp-stream-multiplexing)
|
* [TCP Stream Multiplexing](#tcp-stream-multiplexing)
|
||||||
* [Support KCP Protocol](#support-kcp-protocol)
|
* [Support KCP Protocol](#support-kcp-protocol)
|
||||||
* [Connection Pool](#connection-pool)
|
* [Connection Pool](#connection-pool)
|
||||||
@@ -42,6 +41,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
|||||||
* [Custom subdomain names](#custom-subdomain-names)
|
* [Custom subdomain names](#custom-subdomain-names)
|
||||||
* [URL routing](#url-routing)
|
* [URL routing](#url-routing)
|
||||||
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
|
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
|
||||||
|
* [Range ports mapping](#range-ports-mapping)
|
||||||
* [Plugin](#plugin)
|
* [Plugin](#plugin)
|
||||||
* [Development Plan](#development-plan)
|
* [Development Plan](#development-plan)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
@@ -383,7 +383,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
|
|||||||
|
|
||||||
### Authentication
|
### 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.
|
Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
|
||||||
|
|
||||||
@@ -422,21 +422,17 @@ Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to le
|
|||||||
|
|
||||||
Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set admin port in frpc's configure file.
|
Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set admin port in frpc's configure file.
|
||||||
|
|
||||||
### Privilege Mode
|
### Port White List
|
||||||
|
|
||||||
Privilege mode is the default and only mode support in frp since v0.10.0. All proxy configurations are set in client.
|
`allow_ports` in frps.ini is used for preventing abuse of ports:
|
||||||
|
|
||||||
#### Port White List
|
|
||||||
|
|
||||||
`privilege_allow_ports` in frps.ini is used for preventing abuse of ports:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frps.ini
|
# frps.ini
|
||||||
[common]
|
[common]
|
||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
```
|
```
|
||||||
|
|
||||||
`privilege_allow_ports` consists of a specific port or a range of ports divided by `,`.
|
`allow_ports` consists of a specific port or a range of ports divided by `,`.
|
||||||
|
|
||||||
### TCP Stream Multiplexing
|
### TCP Stream Multiplexing
|
||||||
|
|
||||||
@@ -539,7 +535,7 @@ type = http
|
|||||||
local_port = 80
|
local_port = 80
|
||||||
custom_domains = test.yourdomain.com
|
custom_domains = test.yourdomain.com
|
||||||
http_user = abc
|
http_user = abc
|
||||||
http_pwd = abc
|
http_passwd = abc
|
||||||
```
|
```
|
||||||
|
|
||||||
Visit `http://test.yourdomain.com` and now you need to input username and password.
|
Visit `http://test.yourdomain.com` and now you need to input username and password.
|
||||||
|
|||||||
17
README_zh.md
17
README_zh.md
@@ -29,8 +29,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
|||||||
* [加密与压缩](#加密与压缩)
|
* [加密与压缩](#加密与压缩)
|
||||||
* [客户端热加载配置文件](#客户端热加载配置文件)
|
* [客户端热加载配置文件](#客户端热加载配置文件)
|
||||||
* [客户端查看代理状态](#客户端查看代理状态)
|
* [客户端查看代理状态](#客户端查看代理状态)
|
||||||
* [特权模式](#特权模式)
|
* [端口白名单](#端口白名单)
|
||||||
* [端口白名单](#端口白名单)
|
|
||||||
* [TCP 多路复用](#tcp-多路复用)
|
* [TCP 多路复用](#tcp-多路复用)
|
||||||
* [底层通信可选 kcp 协议](#底层通信可选-kcp-协议)
|
* [底层通信可选 kcp 协议](#底层通信可选-kcp-协议)
|
||||||
* [连接池](#连接池)
|
* [连接池](#连接池)
|
||||||
@@ -401,7 +400,7 @@ dashboard_pwd = admin
|
|||||||
|
|
||||||
### 身份验证
|
### 身份验证
|
||||||
|
|
||||||
从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `privilege_token` 参数一致则身份验证通过。
|
从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。
|
||||||
|
|
||||||
需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
|
需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
|
||||||
|
|
||||||
@@ -450,21 +449,17 @@ admin_port = 7400
|
|||||||
|
|
||||||
frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。
|
frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。
|
||||||
|
|
||||||
### 特权模式
|
### 端口白名单
|
||||||
|
|
||||||
由于从 v0.10.0 版本开始,所有 proxy 都在客户端配置,原先的特权模式是目前唯一支持的模式。
|
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 `allow_ports` 来指定:
|
||||||
|
|
||||||
#### 端口白名单
|
|
||||||
|
|
||||||
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 privilege_allow_ports 来指定:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frps.ini
|
# frps.ini
|
||||||
[common]
|
[common]
|
||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
```
|
```
|
||||||
|
|
||||||
privilege_allow_ports 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
`allow_ports` 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
||||||
|
|
||||||
### TCP 多路复用
|
### TCP 多路复用
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
@@ -35,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
|
|||||||
// url router
|
// url router
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
|
|
||||||
user, passwd := config.ClientCommonCfg.AdminUser, config.ClientCommonCfg.AdminPwd
|
user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd
|
||||||
|
|
||||||
// api, see dashboard_api.go
|
// api, see dashboard_api.go
|
||||||
router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))
|
router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,6 +25,7 @@ import (
|
|||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"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]")
|
log.Info("Http request: [/api/reload]")
|
||||||
|
|
||||||
conf, err := ini.LoadFile(config.ClientCommonCfg.ConfigFile)
|
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 1
|
res.Code = 1
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Error("reload frpc config file error: %v", err)
|
log.Error("reload frpc config file error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
content := string(b)
|
||||||
|
|
||||||
newCommonCfg, err := config.LoadClientCommonConf(conf)
|
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 2
|
res.Code = 2
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
@@ -67,7 +70,15 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
res.Code = 3
|
res.Code = 3
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
@@ -125,18 +136,18 @@ func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp {
|
|||||||
}
|
}
|
||||||
psr.Plugin = cfg.Plugin
|
psr.Plugin = cfg.Plugin
|
||||||
if status.Err != "" {
|
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 {
|
} else {
|
||||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||||
}
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
if cfg.LocalPort != 0 {
|
if cfg.LocalPort != 0 {
|
||||||
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
|
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
|
||||||
}
|
}
|
||||||
if status.Err != "" {
|
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 {
|
} else {
|
||||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||||
}
|
}
|
||||||
case *config.HttpProxyConf:
|
case *config.HttpProxyConf:
|
||||||
if cfg.LocalPort != 0 {
|
if cfg.LocalPort != 0 {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/utils/crypto"
|
"github.com/fatedier/frp/utils/crypto"
|
||||||
@@ -29,7 +30,8 @@ import (
|
|||||||
"github.com/fatedier/frp/utils/shutdown"
|
"github.com/fatedier/frp/utils/shutdown"
|
||||||
"github.com/fatedier/frp/utils/util"
|
"github.com/fatedier/frp/utils/util"
|
||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
"github.com/xtaci/smux"
|
|
||||||
|
fmux "github.com/hashicorp/yamux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -49,7 +51,7 @@ type Control struct {
|
|||||||
conn frpNet.Conn
|
conn frpNet.Conn
|
||||||
|
|
||||||
// tcp stream multiplexing, if enabled
|
// tcp stream multiplexing, if enabled
|
||||||
session *smux.Session
|
session *fmux.Session
|
||||||
|
|
||||||
// put a message in this channel to send it over control connection to server
|
// put a message in this channel to send it over control connection to server
|
||||||
sendCh chan (msg.Message)
|
sendCh chan (msg.Message)
|
||||||
@@ -82,8 +84,8 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
|
|||||||
loginMsg := &msg.Login{
|
loginMsg := &msg.Login{
|
||||||
Arch: runtime.GOARCH,
|
Arch: runtime.GOARCH,
|
||||||
Os: runtime.GOOS,
|
Os: runtime.GOOS,
|
||||||
PoolCount: config.ClientCommonCfg.PoolCount,
|
PoolCount: g.GlbClientCfg.PoolCount,
|
||||||
User: config.ClientCommonCfg.User,
|
User: g.GlbClientCfg.User,
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
}
|
}
|
||||||
ctl := &Control{
|
ctl := &Control{
|
||||||
@@ -110,7 +112,7 @@ func (ctl *Control) Run() (err error) {
|
|||||||
|
|
||||||
// if login_fail_exit is true, just exit this program
|
// if login_fail_exit is true, just exit this program
|
||||||
// otherwise sleep a while and continues relogin to server
|
// otherwise sleep a while and continues relogin to server
|
||||||
if config.ClientCommonCfg.LoginFailExit {
|
if g.GlbClientCfg.LoginFailExit {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
@@ -183,8 +185,8 @@ func (ctl *Control) login() (err error) {
|
|||||||
ctl.session.Close()
|
ctl.session.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
conn, err := frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -195,8 +197,8 @@ func (ctl *Control) login() (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if config.ClientCommonCfg.TcpMux {
|
if g.GlbClientCfg.TcpMux {
|
||||||
session, errRet := smux.Client(conn, nil)
|
session, errRet := fmux.Client(conn, nil)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
return errRet
|
return errRet
|
||||||
}
|
}
|
||||||
@@ -210,7 +212,7 @@ func (ctl *Control) login() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().Unix()
|
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.Timestamp = now
|
||||||
ctl.loginMsg.RunId = ctl.runId
|
ctl.loginMsg.RunId = ctl.runId
|
||||||
|
|
||||||
@@ -234,7 +236,7 @@ func (ctl *Control) login() (err error) {
|
|||||||
ctl.conn = conn
|
ctl.conn = conn
|
||||||
// update runId got from server
|
// update runId got from server
|
||||||
ctl.runId = loginRespMsg.RunId
|
ctl.runId = loginRespMsg.RunId
|
||||||
config.ClientCommonCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
||||||
ctl.ClearLogPrefix()
|
ctl.ClearLogPrefix()
|
||||||
ctl.AddLogPrefix(loginRespMsg.RunId)
|
ctl.AddLogPrefix(loginRespMsg.RunId)
|
||||||
ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
|
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) {
|
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
||||||
if config.ClientCommonCfg.TcpMux {
|
if g.GlbClientCfg.TcpMux {
|
||||||
stream, errRet := ctl.session.OpenStream()
|
stream, errRet := ctl.session.OpenStream()
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
@@ -251,8 +253,8 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
|||||||
}
|
}
|
||||||
conn = frpNet.WrapConn(stream)
|
conn = frpNet.WrapConn(stream)
|
||||||
} else {
|
} else {
|
||||||
conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
conn, err = frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctl.Warn("start new connection to server error: %v", err)
|
ctl.Warn("start new connection to server error: %v", err)
|
||||||
return
|
return
|
||||||
@@ -271,7 +273,7 @@ func (ctl *Control) reader() {
|
|||||||
defer ctl.readerShutdown.Done()
|
defer ctl.readerShutdown.Done()
|
||||||
defer close(ctl.closedCh)
|
defer close(ctl.closedCh)
|
||||||
|
|
||||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||||
for {
|
for {
|
||||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -290,7 +292,7 @@ func (ctl *Control) reader() {
|
|||||||
// writer writes messages got from sendCh to frps
|
// writer writes messages got from sendCh to frps
|
||||||
func (ctl *Control) writer() {
|
func (ctl *Control) writer() {
|
||||||
defer ctl.writerShutdown.Done()
|
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 {
|
if err != nil {
|
||||||
ctl.conn.Error("crypto new writer error: %v", err)
|
ctl.conn.Error("crypto new writer error: %v", err)
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
@@ -318,7 +320,7 @@ func (ctl *Control) msgHandler() {
|
|||||||
}()
|
}()
|
||||||
defer ctl.msgHandlerShutdown.Done()
|
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()
|
defer hbSend.Stop()
|
||||||
hbCheck := time.NewTicker(time.Second)
|
hbCheck := time.NewTicker(time.Second)
|
||||||
defer hbCheck.Stop()
|
defer hbCheck.Stop()
|
||||||
@@ -332,7 +334,7 @@ func (ctl *Control) msgHandler() {
|
|||||||
ctl.Debug("send heartbeat to server")
|
ctl.Debug("send heartbeat to server")
|
||||||
ctl.sendCh <- &msg.Ping{}
|
ctl.sendCh <- &msg.Ping{}
|
||||||
case <-hbCheck.C:
|
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")
|
ctl.Warn("heartbeat timeout")
|
||||||
// let reader() stop
|
// let reader() stop
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/models/plugin"
|
"github.com/fatedier/frp/models/plugin"
|
||||||
@@ -46,7 +47,7 @@ type Proxy interface {
|
|||||||
|
|
||||||
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
||||||
baseProxy := BaseProxy{
|
baseProxy := BaseProxy{
|
||||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||||
}
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.TcpProxyConf:
|
case *config.TcpProxyConf:
|
||||||
@@ -115,7 +116,7 @@ func (pxy *TcpProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
@@ -144,7 +145,7 @@ func (pxy *HttpProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
@@ -173,7 +174,7 @@ func (pxy *HttpsProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// STCP
|
// STCP
|
||||||
@@ -202,7 +203,7 @@ func (pxy *StcpProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// XTCP
|
// XTCP
|
||||||
@@ -243,7 +244,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
|
|||||||
Sid: natHoleSidMsg.Sid,
|
Sid: natHoleSidMsg.Sid,
|
||||||
}
|
}
|
||||||
raddr, _ := net.ResolveUDPAddr("udp",
|
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)
|
clientConn, err := net.DialUDP("udp", nil, raddr)
|
||||||
defer clientConn.Close()
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ type ProxyStatus struct {
|
|||||||
|
|
||||||
func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
|
func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
|
||||||
return &ProxyWrapper{
|
return &ProxyWrapper{
|
||||||
Name: cfg.GetName(),
|
Name: cfg.GetBaseInfo().ProxyName,
|
||||||
Type: cfg.GetType(),
|
Type: cfg.GetBaseInfo().ProxyType,
|
||||||
Status: ProxyStatusNew,
|
Status: ProxyStatusNew,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
pxy: nil,
|
pxy: nil,
|
||||||
@@ -227,7 +227,7 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
|
|||||||
for _, s := range pxyStatus {
|
for _, s := range pxyStatus {
|
||||||
if status == s {
|
if status == s {
|
||||||
var newProxyMsg msg.NewProxy
|
var newProxyMsg msg.NewProxy
|
||||||
pxy.Cfg.UnMarshalToMsg(&newProxyMsg)
|
pxy.Cfg.MarshalToMsg(&newProxyMsg)
|
||||||
err := pm.sendMsg(&newProxyMsg)
|
err := pm.sendMsg(&newProxyMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pm.Warn("[%s] proxy send NewProxy message error")
|
pm.Warn("[%s] proxy send NewProxy message error")
|
||||||
@@ -240,15 +240,16 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, cfg := range pm.visitorCfgs {
|
for _, cfg := range pm.visitorCfgs {
|
||||||
if _, exist := pm.visitors[cfg.GetName()]; !exist {
|
name := cfg.GetBaseInfo().ProxyName
|
||||||
pm.Info("try to start visitor [%s]", cfg.GetName())
|
if _, exist := pm.visitors[name]; !exist {
|
||||||
|
pm.Info("try to start visitor [%s]", name)
|
||||||
visitor := NewVisitor(pm.ctl, cfg)
|
visitor := NewVisitor(pm.ctl, cfg)
|
||||||
err := visitor.Run()
|
err := visitor.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
visitor.Warn("start error: %v", err)
|
visitor.Warn("start error: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pm.visitors[cfg.GetName()] = visitor
|
pm.visitors[name] = visitor
|
||||||
visitor.Info("start visitor success")
|
visitor.Info("start visitor success")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
)
|
)
|
||||||
@@ -41,12 +42,12 @@ func (svr *Service) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ClientCommonCfg.AdminPort != 0 {
|
if g.GlbClientCfg.AdminPort != 0 {
|
||||||
err = svr.RunAdminServer(config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("run admin server error: %v", err)
|
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
|
<-svr.closedCh
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
frpIo "github.com/fatedier/frp/utils/io"
|
frpIo "github.com/fatedier/frp/utils/io"
|
||||||
@@ -45,7 +46,7 @@ type Visitor interface {
|
|||||||
func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
|
func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
|
||||||
baseVisitor := BaseVisitor{
|
baseVisitor := BaseVisitor{
|
||||||
ctl: ctl,
|
ctl: ctl,
|
||||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||||
}
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.StcpProxyConf:
|
case *config.StcpProxyConf:
|
||||||
@@ -193,13 +194,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
|
|||||||
defer userConn.Close()
|
defer userConn.Close()
|
||||||
|
|
||||||
sv.Debug("get a new xtcp user connection")
|
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")
|
sv.Error("xtcp is not supported by server")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
raddr, err := net.ResolveUDPAddr("udp",
|
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)
|
visitorConn, err := net.DialUDP("udp", nil, raddr)
|
||||||
defer visitorConn.Close()
|
defer visitorConn.Close()
|
||||||
|
|
||||||
|
|||||||
286
cmd/frpc/main.go
286
cmd/frpc/main.go
@@ -15,291 +15,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
configFile string = "./frpc.ini"
|
|
||||||
)
|
|
||||||
|
|
||||||
var usage string = `frpc is the client of frp
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
frpc [-c config_file] [-L log_file] [--log-level=<log_level>] [--server-addr=<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=<log_level> set log level: debug, info, warn, error
|
|
||||||
--server-addr=<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() {
|
func main() {
|
||||||
var err error
|
sub.Execute()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
96
cmd/frpc/sub/http.go
Normal file
96
cmd/frpc/sub/http.go
Normal file
@@ -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
|
||||||
|
},
|
||||||
|
}
|
||||||
88
cmd/frpc/sub/https.go
Normal file
88
cmd/frpc/sub/https.go
Normal file
@@ -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
|
||||||
|
},
|
||||||
|
}
|
||||||
92
cmd/frpc/sub/reload.go
Normal file
92
cmd/frpc/sub/reload.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
215
cmd/frpc/sub/root.go
Normal file
215
cmd/frpc/sub/root.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"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)
|
||||||
|
if g.GlbClientCfg.DnsServer != "" {
|
||||||
|
s := g.GlbClientCfg.DnsServer
|
||||||
|
if !strings.Contains(s, ":") {
|
||||||
|
s += ":53"
|
||||||
|
}
|
||||||
|
// Change default dns server for frpc
|
||||||
|
net.DefaultResolver = &net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return net.Dial("udp", s)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
146
cmd/frpc/sub/status.go
Normal file
146
cmd/frpc/sub/status.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
102
cmd/frpc/sub/stcp.go
Normal file
102
cmd/frpc/sub/stcp.go
Normal file
@@ -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: "stcp",
|
||||||
|
Short: "Run frpc with a single stcp 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
|
||||||
|
},
|
||||||
|
}
|
||||||
85
cmd/frpc/sub/tcp.go
Normal file
85
cmd/frpc/sub/tcp.go
Normal file
@@ -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
|
||||||
|
},
|
||||||
|
}
|
||||||
85
cmd/frpc/sub/udp.go
Normal file
85
cmd/frpc/sub/udp.go
Normal file
@@ -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
|
||||||
|
},
|
||||||
|
}
|
||||||
102
cmd/frpc/sub/xtcp.go
Normal file
102
cmd/frpc/sub/xtcp.go
Normal file
@@ -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: "xtcp",
|
||||||
|
Short: "Run frpc with a single xtcp 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
|
||||||
|
},
|
||||||
|
}
|
||||||
103
cmd/frps/main.go
103
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -14,105 +14,6 @@
|
|||||||
|
|
||||||
package main
|
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=<log_level>] [--addr=<bind_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=<log_level> set log level: debug, info, warn, error
|
|
||||||
--addr=<bind_addr> listen addr for client, example: 0.0.0.0:7000
|
|
||||||
-h --help show this screen
|
|
||||||
-v --version show version
|
|
||||||
`
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
Execute()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|||||||
194
cmd/frps/root.go
Normal file
194
cmd/frps/root.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
// 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 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", "", "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
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if cfgFile != "" {
|
||||||
|
err = parseServerCommonCfg(CfgFileTypeIni, cfgFile)
|
||||||
|
} else {
|
||||||
|
err = parseServerCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = filePath
|
||||||
|
|
||||||
|
err = g.GlbServerCfg.ServerCommonConf.Check()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config.InitServerCfg(&g.GlbServerCfg.ServerCommonConf)
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -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
|
# 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
|
# 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
|
# console or real logFile path like ./frpc.log
|
||||||
log_file = ./frpc.log
|
log_file = ./frpc.log
|
||||||
@@ -18,13 +18,13 @@ log_level = info
|
|||||||
log_max_days = 3
|
log_max_days = 3
|
||||||
|
|
||||||
# for authentication
|
# for authentication
|
||||||
privilege_token = 12345678
|
token = 12345678
|
||||||
|
|
||||||
# set admin address for control frpc's action by http api such as reload
|
# set admin address for control frpc's action by http api such as reload
|
||||||
admin_addr = 127.0.0.1
|
admin_addr = 127.0.0.1
|
||||||
admin_port = 7400
|
admin_port = 7400
|
||||||
admin_user = admin
|
admin_user = admin
|
||||||
admin_pwd = admin
|
admin_passwd = admin
|
||||||
|
|
||||||
# connections will be established in advance, default value is zero
|
# connections will be established in advance, default value is zero
|
||||||
pool_count = 5
|
pool_count = 5
|
||||||
@@ -43,6 +43,9 @@ login_fail_exit = true
|
|||||||
# now it supports tcp and kcp, default is tcp
|
# now it supports tcp and kcp, default is tcp
|
||||||
protocol = tcp
|
protocol = tcp
|
||||||
|
|
||||||
|
# specify a dns server, so frpc will use this instead of default one
|
||||||
|
dns_server = 8.8.8.8
|
||||||
|
|
||||||
# proxy names you want to start divided by ','
|
# proxy names you want to start divided by ','
|
||||||
# default is empty, means all proxies
|
# default is empty, means all proxies
|
||||||
# start = ssh,dns
|
# start = ssh,dns
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ vhost_https_port = 443
|
|||||||
dashboard_addr = 0.0.0.0
|
dashboard_addr = 0.0.0.0
|
||||||
dashboard_port = 7500
|
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_user = admin
|
||||||
dashboard_pwd = admin
|
dashboard_passwd = admin
|
||||||
|
|
||||||
# dashboard assets directory(only for debug mode)
|
# dashboard assets directory(only for debug mode)
|
||||||
# assets_dir = ./static
|
# assets_dir = ./static
|
||||||
@@ -39,15 +39,15 @@ log_level = info
|
|||||||
|
|
||||||
log_max_days = 3
|
log_max_days = 3
|
||||||
|
|
||||||
# privilege mode is the only supported mode since v0.10.0
|
# auth token
|
||||||
privilege_token = 12345678
|
token = 12345678
|
||||||
|
|
||||||
# heartbeat configure, it's not recommended to modify the default value
|
# heartbeat configure, it's not recommended to modify the default value
|
||||||
# the default value of heartbeat_timeout is 90
|
# the default value of heartbeat_timeout is 90
|
||||||
# heartbeat_timeout = 90
|
# heartbeat_timeout = 90
|
||||||
|
|
||||||
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
|
|
||||||
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
||||||
max_pool_count = 5
|
max_pool_count = 5
|
||||||
|
|||||||
32
g/g.go
Normal file
32
g/g.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
20
glide.lock
generated
20
glide.lock
generated
@@ -1,5 +1,5 @@
|
|||||||
hash: 4095d78a15bf0e7ffdd63331ce75d7199d663cc8710dcd08b9dcd09ba3183eac
|
hash: e2a62cbc49d9da8ff95682f5c0b7731a7047afdd139acddb691c51ea98f726e1
|
||||||
updated: 2018-01-23T14:48:38.764359+08:00
|
updated: 2018-04-25T02:41:38.15698+08:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/armon/go-socks5
|
- name: github.com/armon/go-socks5
|
||||||
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
||||||
@@ -7,8 +7,6 @@ imports:
|
|||||||
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
subpackages:
|
subpackages:
|
||||||
- spew
|
- spew
|
||||||
- name: github.com/docopt/docopt-go
|
|
||||||
version: 784ddc588536785e7299f7272f39101f7faccc3f
|
|
||||||
- name: github.com/fatedier/beego
|
- name: github.com/fatedier/beego
|
||||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -18,7 +16,11 @@ imports:
|
|||||||
- name: github.com/golang/snappy
|
- name: github.com/golang/snappy
|
||||||
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
||||||
- name: github.com/gorilla/websocket
|
- name: github.com/gorilla/websocket
|
||||||
version: 292fd08b2560ad524ee37396253d71570339a821
|
version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b
|
||||||
|
- name: github.com/hashicorp/yamux
|
||||||
|
version: 2658be15c5f05e76244154714161f17e3e77de2e
|
||||||
|
- name: github.com/inconshreveable/mousetrap
|
||||||
|
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
- name: github.com/julienschmidt/httprouter
|
- name: github.com/julienschmidt/httprouter
|
||||||
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
||||||
- name: github.com/klauspost/cpuid
|
- name: github.com/klauspost/cpuid
|
||||||
@@ -37,6 +39,10 @@ imports:
|
|||||||
- fs
|
- fs
|
||||||
- name: github.com/rodaine/table
|
- name: github.com/rodaine/table
|
||||||
version: 212a2ad1c462ed4d5b5511ea2b480a573281dbbd
|
version: 212a2ad1c462ed4d5b5511ea2b480a573281dbbd
|
||||||
|
- name: github.com/spf13/cobra
|
||||||
|
version: a1f051bc3eba734da4772d60e2d677f47cf93ef4
|
||||||
|
- name: github.com/spf13/pflag
|
||||||
|
version: 583c0c0531f06d5278b7d917446061adc344b5cd
|
||||||
- name: github.com/stretchr/testify
|
- name: github.com/stretchr/testify
|
||||||
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -53,10 +59,6 @@ imports:
|
|||||||
- sm4
|
- sm4
|
||||||
- name: github.com/vaughan0/go-ini
|
- name: github.com/vaughan0/go-ini
|
||||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||||
- name: github.com/xtaci/kcp-go
|
|
||||||
version: df437e2b8ec365a336200f9d9da53441cf72ed47
|
|
||||||
- name: github.com/xtaci/smux
|
|
||||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||||
subpackages:
|
subpackages:
|
||||||
|
|||||||
10
glide.yaml
10
glide.yaml
@@ -6,8 +6,6 @@ import:
|
|||||||
version: v1.1.0
|
version: v1.1.0
|
||||||
subpackages:
|
subpackages:
|
||||||
- spew
|
- spew
|
||||||
- package: github.com/docopt/docopt-go
|
|
||||||
version: 0.6.2
|
|
||||||
- package: github.com/fatedier/beego
|
- package: github.com/fatedier/beego
|
||||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -48,10 +46,6 @@ import:
|
|||||||
- sm4
|
- sm4
|
||||||
- package: github.com/vaughan0/go-ini
|
- package: github.com/vaughan0/go-ini
|
||||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||||
- package: github.com/xtaci/kcp-go
|
|
||||||
version: v3.17
|
|
||||||
- package: github.com/xtaci/smux
|
|
||||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
|
||||||
- package: golang.org/x/crypto
|
- package: golang.org/x/crypto
|
||||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -74,3 +68,7 @@ import:
|
|||||||
- package: github.com/rodaine/table
|
- package: github.com/rodaine/table
|
||||||
version: v1.0.0
|
version: v1.0.0
|
||||||
- package: github.com/gorilla/websocket
|
- package: github.com/gorilla/websocket
|
||||||
|
version: v1.2.0
|
||||||
|
- package: github.com/hashicorp/yamux
|
||||||
|
- package: github.com/spf13/cobra
|
||||||
|
version: v0.0.2
|
||||||
|
|||||||
@@ -23,46 +23,41 @@ import (
|
|||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ClientCommonCfg *ClientCommonConf
|
|
||||||
|
|
||||||
// client common config
|
// client common config
|
||||||
type ClientCommonConf struct {
|
type ClientCommonConf struct {
|
||||||
ConfigFile string
|
ServerAddr string `json:"server_addr"`
|
||||||
ServerAddr string
|
ServerPort int `json:"server_port"`
|
||||||
ServerPort int
|
HttpProxy string `json:"http_proxy"`
|
||||||
ServerUdpPort int // this is specified by login response message from frps
|
LogFile string `json:"log_file"`
|
||||||
HttpProxy string
|
LogWay string `json:"log_way"`
|
||||||
LogFile string
|
LogLevel string `json:"log_level"`
|
||||||
LogWay string
|
LogMaxDays int64 `json:"log_max_days"`
|
||||||
LogLevel string
|
Token string `json:"token"`
|
||||||
LogMaxDays int64
|
AdminAddr string `json:"admin_addr"`
|
||||||
PrivilegeToken string
|
AdminPort int `json:"admin_port"`
|
||||||
AdminAddr string
|
AdminUser string `json:"admin_user"`
|
||||||
AdminPort int
|
AdminPwd string `json:"admin_pwd"`
|
||||||
AdminUser string
|
PoolCount int `json:"pool_count"`
|
||||||
AdminPwd string
|
TcpMux bool `json:"tcp_mux"`
|
||||||
PoolCount int
|
User string `json:"user"`
|
||||||
TcpMux bool
|
DnsServer string `json:"dns_server"`
|
||||||
User string
|
LoginFailExit bool `json:"login_fail_exit"`
|
||||||
LoginFailExit bool
|
Start map[string]struct{} `json:"start"`
|
||||||
Start map[string]struct{}
|
Protocol string `json:"protocol"`
|
||||||
Protocol string
|
HeartBeatInterval int64 `json:"heartbeat_interval"`
|
||||||
HeartBeatInterval int64
|
HeartBeatTimeout int64 `json:"heartbeat_timeout"`
|
||||||
HeartBeatTimeout int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDeaultClientCommonConf() *ClientCommonConf {
|
func GetDefaultClientConf() *ClientCommonConf {
|
||||||
return &ClientCommonConf{
|
return &ClientCommonConf{
|
||||||
ConfigFile: "./frpc.ini",
|
|
||||||
ServerAddr: "0.0.0.0",
|
ServerAddr: "0.0.0.0",
|
||||||
ServerPort: 7000,
|
ServerPort: 7000,
|
||||||
ServerUdpPort: 0,
|
HttpProxy: os.Getenv("http_proxy"),
|
||||||
HttpProxy: "",
|
|
||||||
LogFile: "console",
|
LogFile: "console",
|
||||||
LogWay: "console",
|
LogWay: "console",
|
||||||
LogLevel: "info",
|
LogLevel: "info",
|
||||||
LogMaxDays: 3,
|
LogMaxDays: 3,
|
||||||
PrivilegeToken: "",
|
Token: "",
|
||||||
AdminAddr: "127.0.0.1",
|
AdminAddr: "127.0.0.1",
|
||||||
AdminPort: 0,
|
AdminPort: 0,
|
||||||
AdminUser: "",
|
AdminUser: "",
|
||||||
@@ -70,6 +65,7 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
|||||||
PoolCount: 1,
|
PoolCount: 1,
|
||||||
TcpMux: true,
|
TcpMux: true,
|
||||||
User: "",
|
User: "",
|
||||||
|
DnsServer: "",
|
||||||
LoginFailExit: true,
|
LoginFailExit: true,
|
||||||
Start: make(map[string]struct{}),
|
Start: make(map[string]struct{}),
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
@@ -78,21 +74,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 (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
v int64
|
v int64
|
||||||
)
|
)
|
||||||
cfg = GetDeaultClientCommonConf()
|
if tmpStr, ok = conf.Get("common", "server_addr"); ok {
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "server_addr")
|
|
||||||
if ok {
|
|
||||||
cfg.ServerAddr = tmpStr
|
cfg.ServerAddr = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "server_port")
|
if tmpStr, ok = conf.Get("common", "server_port"); ok {
|
||||||
if ok {
|
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid server_port")
|
err = fmt.Errorf("Parse conf error: invalid server_port")
|
||||||
@@ -101,16 +104,11 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
cfg.ServerPort = int(v)
|
cfg.ServerPort = int(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "http_proxy")
|
if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
|
||||||
if ok {
|
|
||||||
cfg.HttpProxy = tmpStr
|
cfg.HttpProxy = tmpStr
|
||||||
} else {
|
|
||||||
// get http_proxy from env
|
|
||||||
cfg.HttpProxy = os.Getenv("http_proxy")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_file")
|
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogFile = tmpStr
|
cfg.LogFile = tmpStr
|
||||||
if cfg.LogFile == "console" {
|
if cfg.LogFile == "console" {
|
||||||
cfg.LogWay = "console"
|
cfg.LogWay = "console"
|
||||||
@@ -119,30 +117,25 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_level")
|
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogLevel = tmpStr
|
cfg.LogLevel = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||||
cfg.LogMaxDays = v
|
cfg.LogMaxDays = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "privilege_token")
|
if tmpStr, ok = conf.Get("common", "token"); ok {
|
||||||
if ok {
|
cfg.Token = tmpStr
|
||||||
cfg.PrivilegeToken = tmpStr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_addr")
|
if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AdminAddr = tmpStr
|
cfg.AdminAddr = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_port")
|
if tmpStr, ok = conf.Get("common", "admin_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||||
cfg.AdminPort = int(v)
|
cfg.AdminPort = int(v)
|
||||||
} else {
|
} else {
|
||||||
@@ -151,55 +144,48 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_user")
|
if tmpStr, ok = conf.Get("common", "admin_user"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AdminUser = tmpStr
|
cfg.AdminUser = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_pwd")
|
if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AdminPwd = tmpStr
|
cfg.AdminPwd = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "pool_count")
|
if tmpStr, ok = conf.Get("common", "pool_count"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
cfg.PoolCount = 1
|
|
||||||
} else {
|
|
||||||
cfg.PoolCount = int(v)
|
cfg.PoolCount = int(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||||
if ok && tmpStr == "false" {
|
|
||||||
cfg.TcpMux = false
|
cfg.TcpMux = false
|
||||||
} else {
|
} else {
|
||||||
cfg.TcpMux = true
|
cfg.TcpMux = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "user")
|
if tmpStr, ok = conf.Get("common", "user"); ok {
|
||||||
if ok {
|
|
||||||
cfg.User = tmpStr
|
cfg.User = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "start")
|
if tmpStr, ok = conf.Get("common", "dns_server"); ok {
|
||||||
if ok {
|
cfg.DnsServer = tmpStr
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpStr, ok = conf.Get("common", "start"); ok {
|
||||||
proxyNames := strings.Split(tmpStr, ",")
|
proxyNames := strings.Split(tmpStr, ",")
|
||||||
for _, name := range proxyNames {
|
for _, name := range proxyNames {
|
||||||
cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "login_fail_exit")
|
if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
|
||||||
if ok && tmpStr == "false" {
|
|
||||||
cfg.LoginFailExit = false
|
cfg.LoginFailExit = false
|
||||||
} else {
|
} else {
|
||||||
cfg.LoginFailExit = true
|
cfg.LoginFailExit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "protocol")
|
if tmpStr, ok = conf.Get("common", "protocol"); ok {
|
||||||
if ok {
|
|
||||||
// Now it only support tcp and kcp.
|
// Now it only support tcp and kcp.
|
||||||
if tmpStr != "kcp" {
|
if tmpStr != "kcp" {
|
||||||
tmpStr = "tcp"
|
tmpStr = "tcp"
|
||||||
@@ -207,10 +193,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
cfg.Protocol = tmpStr
|
cfg.Protocol = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@@ -218,17 +202,18 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_interval")
|
if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
cfg.HeartBeatInterval = v
|
cfg.HeartBeatInterval = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ClientCommonConf) Check() (err error) {
|
||||||
if cfg.HeartBeatInterval <= 0 {
|
if cfg.HeartBeatInterval <= 0 {
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ import (
|
|||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
)
|
)
|
||||||
|
|
||||||
var proxyConfTypeMap map[string]reflect.Type
|
var (
|
||||||
|
proxyConfTypeMap map[string]reflect.Type
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proxyConfTypeMap = make(map[string]reflect.Type)
|
proxyConfTypeMap = make(map[string]reflect.Type)
|
||||||
@@ -51,17 +53,16 @@ func NewConfByType(proxyType string) ProxyConf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProxyConf interface {
|
type ProxyConf interface {
|
||||||
GetName() string
|
|
||||||
GetType() string
|
|
||||||
GetBaseInfo() *BaseProxyConf
|
GetBaseInfo() *BaseProxyConf
|
||||||
LoadFromMsg(pMsg *msg.NewProxy)
|
UnmarshalFromMsg(pMsg *msg.NewProxy)
|
||||||
LoadFromFile(name string, conf ini.Section) error
|
UnmarshalFromIni(prefix string, name string, conf ini.Section) error
|
||||||
UnMarshalToMsg(pMsg *msg.NewProxy)
|
MarshalToMsg(pMsg *msg.NewProxy)
|
||||||
Check() error
|
CheckForCli() error
|
||||||
|
CheckForSvr() error
|
||||||
Compare(conf ProxyConf) bool
|
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 == "" {
|
if pMsg.ProxyType == "" {
|
||||||
pMsg.ProxyType = consts.TcpProxy
|
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)
|
err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.LoadFromMsg(pMsg)
|
cfg.UnmarshalFromMsg(pMsg)
|
||||||
err = cfg.Check()
|
err = cfg.CheckForSvr()
|
||||||
return
|
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"]
|
proxyType := section["type"]
|
||||||
if proxyType == "" {
|
if proxyType == "" {
|
||||||
proxyType = consts.TcpProxy
|
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)
|
err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = cfg.LoadFromFile(name, section)
|
if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cfg.CheckForCli()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,14 +104,6 @@ type BaseProxyConf struct {
|
|||||||
UseCompression bool `json:"use_compression"`
|
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 {
|
func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
@@ -122,23 +118,19 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.ProxyName = pMsg.ProxyName
|
cfg.ProxyName = pMsg.ProxyName
|
||||||
cfg.ProxyType = pMsg.ProxyType
|
cfg.ProxyType = pMsg.ProxyType
|
||||||
cfg.UseEncryption = pMsg.UseEncryption
|
cfg.UseEncryption = pMsg.UseEncryption
|
||||||
cfg.UseCompression = pMsg.UseCompression
|
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 (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
if ClientCommonCfg.User != "" {
|
cfg.ProxyName = prefix + name
|
||||||
cfg.ProxyName = ClientCommonCfg.User + "." + name
|
|
||||||
} else {
|
|
||||||
cfg.ProxyName = name
|
|
||||||
}
|
|
||||||
cfg.ProxyType = section["type"]
|
cfg.ProxyType = section["type"]
|
||||||
|
|
||||||
tmpStr, ok = section["use_encryption"]
|
tmpStr, ok = section["use_encryption"]
|
||||||
@@ -153,7 +145,7 @@ func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
pMsg.ProxyName = cfg.ProxyName
|
pMsg.ProxyName = cfg.ProxyName
|
||||||
pMsg.ProxyType = cfg.ProxyType
|
pMsg.ProxyType = cfg.ProxyType
|
||||||
pMsg.UseEncryption = cfg.UseEncryption
|
pMsg.UseEncryption = cfg.UseEncryption
|
||||||
@@ -162,24 +154,21 @@ func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
|||||||
|
|
||||||
// Bind info
|
// Bind info
|
||||||
type BindInfoConf struct {
|
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 {
|
func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool {
|
||||||
if cfg.BindAddr != cmp.BindAddr ||
|
if cfg.RemotePort != cmp.RemotePort {
|
||||||
cfg.RemotePort != cmp.RemotePort {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *BindInfoConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BindAddr = ServerCommonCfg.ProxyBindAddr
|
|
||||||
cfg.RemotePort = pMsg.RemotePort
|
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 (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
@@ -197,14 +186,10 @@ func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *BindInfoConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
pMsg.RemotePort = cfg.RemotePort
|
pMsg.RemotePort = cfg.RemotePort
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) check() (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Domain info
|
// Domain info
|
||||||
type DomainConf struct {
|
type DomainConf struct {
|
||||||
CustomDomains []string `json:"custom_domains"`
|
CustomDomains []string `json:"custom_domains"`
|
||||||
@@ -219,12 +204,12 @@ func (cfg *DomainConf) compare(cmp *DomainConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *DomainConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.CustomDomains = pMsg.CustomDomains
|
cfg.CustomDomains = pMsg.CustomDomains
|
||||||
cfg.SubDomain = pMsg.SubDomain
|
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 (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
@@ -239,42 +224,60 @@ func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error
|
|||||||
if tmpStr, ok = section["subdomain"]; ok {
|
if tmpStr, ok = section["subdomain"]; ok {
|
||||||
cfg.SubDomain = tmpStr
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *DomainConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
pMsg.CustomDomains = cfg.CustomDomains
|
pMsg.CustomDomains = cfg.CustomDomains
|
||||||
pMsg.SubDomain = cfg.SubDomain
|
pMsg.SubDomain = cfg.SubDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) check() (err error) {
|
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 {
|
for _, domain := range cfg.CustomDomains {
|
||||||
if ServerCommonCfg.SubDomainHost != "" && len(strings.Split(ServerCommonCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||||
if strings.Contains(domain, ServerCommonCfg.SubDomainHost) {
|
if strings.Contains(domain, subDomainHost) {
|
||||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, ServerCommonCfg.SubDomainHost)
|
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.SubDomain != "" {
|
if cfg.SubDomain != "" {
|
||||||
if ServerCommonCfg.SubDomainHost == "" {
|
if subDomainHost == "" {
|
||||||
return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
|
return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
|
||||||
}
|
}
|
||||||
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
||||||
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local service info
|
// Local service info
|
||||||
type LocalSvrConf struct {
|
type LocalSvrConf struct {
|
||||||
LocalIp string `json:"-"`
|
LocalIp string `json:"local_ip"`
|
||||||
LocalPort int `json:"-"`
|
LocalPort int `json:"local_port"`
|
||||||
|
|
||||||
|
Plugin string `json:"plugin"`
|
||||||
|
PluginParams map[string]string `json:"plugin_params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
||||||
@@ -282,30 +285,6 @@ func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
|||||||
cfg.LocalPort != cmp.LocalPort {
|
cfg.LocalPort != cmp.LocalPort {
|
||||||
return false
|
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 ||
|
if cfg.Plugin != cmp.Plugin ||
|
||||||
len(cfg.PluginParams) != len(cmp.PluginParams) {
|
len(cfg.PluginParams) != len(cmp.PluginParams) {
|
||||||
return false
|
return false
|
||||||
@@ -319,7 +298,7 @@ func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
|||||||
return true
|
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.Plugin = section["plugin"]
|
||||||
cfg.PluginParams = make(map[string]string)
|
cfg.PluginParams = make(map[string]string)
|
||||||
if cfg.Plugin != "" {
|
if cfg.Plugin != "" {
|
||||||
@@ -330,7 +309,17 @@ func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
return
|
||||||
}
|
}
|
||||||
@@ -341,7 +330,6 @@ type TcpProxyConf struct {
|
|||||||
BindInfoConf
|
BindInfoConf
|
||||||
|
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
@@ -352,43 +340,38 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
|
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) Check() (err error) {
|
func (cfg *TcpProxyConf) CheckForCli() error { return nil }
|
||||||
err = cfg.BindInfoConf.check()
|
|
||||||
return
|
func (cfg *TcpProxyConf) CheckForSvr() error { return nil }
|
||||||
}
|
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
type UdpProxyConf struct {
|
type UdpProxyConf struct {
|
||||||
@@ -412,33 +395,32 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) Check() (err error) {
|
func (cfg *UdpProxyConf) CheckForCli() error { return nil }
|
||||||
err = cfg.BindInfoConf.check()
|
|
||||||
return
|
func (cfg *UdpProxyConf) CheckForSvr() error { return nil }
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
type HttpProxyConf struct {
|
type HttpProxyConf struct {
|
||||||
@@ -446,12 +428,11 @@ type HttpProxyConf struct {
|
|||||||
DomainConf
|
DomainConf
|
||||||
|
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
|
|
||||||
Locations []string `json:"locations"`
|
Locations []string `json:"locations"`
|
||||||
HostHeaderRewrite string `json:"host_header_rewrite"`
|
HostHeaderRewrite string `json:"host_header_rewrite"`
|
||||||
HttpUser string `json:"-"`
|
HttpUser string `json:"http_user"`
|
||||||
HttpPwd string `json:"-"`
|
HttpPwd string `json:"http_pwd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
@@ -463,7 +444,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
|
||||||
strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
|
strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
|
||||||
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
|
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
|
||||||
cfg.HttpUser != cmpConf.HttpUser ||
|
cfg.HttpUser != cmpConf.HttpUser ||
|
||||||
@@ -473,9 +453,9 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||||
|
|
||||||
cfg.Locations = pMsg.Locations
|
cfg.Locations = pMsg.Locations
|
||||||
cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
|
cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
|
||||||
@@ -483,17 +463,15 @@ func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
|||||||
cfg.HttpPwd = pMsg.HttpPwd
|
cfg.HttpPwd = pMsg.HttpPwd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -512,9 +490,9 @@ func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||||
|
|
||||||
pMsg.Locations = cfg.Locations
|
pMsg.Locations = cfg.Locations
|
||||||
pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
|
pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
|
||||||
@@ -522,11 +500,20 @@ func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
|||||||
pMsg.HttpPwd = cfg.HttpPwd
|
pMsg.HttpPwd = cfg.HttpPwd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) Check() (err error) {
|
func (cfg *HttpProxyConf) CheckForCli() (err error) {
|
||||||
if ServerCommonCfg.VhostHttpPort == 0 {
|
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||||
return fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +523,6 @@ type HttpsProxyConf struct {
|
|||||||
DomainConf
|
DomainConf
|
||||||
|
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
@@ -547,43 +533,49 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) Check() (err error) {
|
func (cfg *HttpsProxyConf) CheckForCli() (err error) {
|
||||||
if ServerCommonCfg.VhostHttpsPort == 0 {
|
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")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,7 +588,6 @@ type StcpProxyConf struct {
|
|||||||
|
|
||||||
// used in role server
|
// used in role server
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
|
|
||||||
// used in role visitor
|
// used in role visitor
|
||||||
ServerName string `json:"server_name"`
|
ServerName string `json:"server_name"`
|
||||||
@@ -612,7 +603,6 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
|
||||||
cfg.Role != cmpConf.Role ||
|
cfg.Role != cmpConf.Role ||
|
||||||
cfg.Sk != cmpConf.Sk ||
|
cfg.Sk != cmpConf.Sk ||
|
||||||
cfg.ServerName != cmpConf.ServerName ||
|
cfg.ServerName != cmpConf.ServerName ||
|
||||||
@@ -624,13 +614,13 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only for role server.
|
// Only for role server.
|
||||||
func (cfg *StcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.Sk = pMsg.Sk
|
cfg.Sk = pMsg.Sk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
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)
|
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *StcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
pMsg.Sk = cfg.Sk
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +690,6 @@ type XtcpProxyConf struct {
|
|||||||
|
|
||||||
// used in role server
|
// used in role server
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
|
|
||||||
// used in role visitor
|
// used in role visitor
|
||||||
ServerName string `json:"server_name"`
|
ServerName string `json:"server_name"`
|
||||||
@@ -704,7 +705,6 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
|
||||||
cfg.Role != cmpConf.Role ||
|
cfg.Role != cmpConf.Role ||
|
||||||
cfg.Sk != cmpConf.Sk ||
|
cfg.Sk != cmpConf.Sk ||
|
||||||
cfg.ServerName != cmpConf.ServerName ||
|
cfg.ServerName != cmpConf.ServerName ||
|
||||||
@@ -716,13 +716,13 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only for role server.
|
// Only for role server.
|
||||||
func (cfg *XtcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.Sk = pMsg.Sk
|
cfg.Sk = pMsg.Sk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
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)
|
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *XtcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
pMsg.Sk = cfg.Sk
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,7 +817,7 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in
|
|||||||
|
|
||||||
// if len(startProxy) is 0, start all
|
// if len(startProxy) is 0, start all
|
||||||
// otherwise just start proxies in startProxy map
|
// otherwise just start proxies in startProxy map
|
||||||
func 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) {
|
proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) {
|
||||||
|
|
||||||
if prefix != "" {
|
if prefix != "" {
|
||||||
@@ -842,9 +854,7 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s
|
|||||||
}
|
}
|
||||||
|
|
||||||
for subName, subSection := range subSections {
|
for subName, subSection := range subSections {
|
||||||
// some proxy or visotr configure may be used this prefix
|
cfg, err := NewProxyConfFromIni(prefix, subName, subSection)
|
||||||
subSection["prefix"] = prefix
|
|
||||||
cfg, err := NewProxyConfFromFile(subName, subSection)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return proxyConfs, visitorConfs, err
|
return proxyConfs, visitorConfs, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,94 +24,109 @@ import (
|
|||||||
"github.com/fatedier/frp/utils/util"
|
"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
|
// common config
|
||||||
type ServerCommonConf struct {
|
type ServerCommonConf struct {
|
||||||
ConfigFile string
|
BindAddr string `json:"bind_addr"`
|
||||||
BindAddr string
|
BindPort int `json:"bind_port"`
|
||||||
BindPort int
|
BindUdpPort int `json:"bind_udp_port"`
|
||||||
BindUdpPort int
|
KcpBindPort int `json:"kcp_bind_port"`
|
||||||
KcpBindPort int
|
ProxyBindAddr string `json:"proxy_bind_addr"`
|
||||||
ProxyBindAddr string
|
|
||||||
|
|
||||||
// If VhostHttpPort equals 0, don't listen a public port for http protocol.
|
// 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
|
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
||||||
VhostHttpsPort int
|
VhostHttpsPort int `json:"vhost_http_port"`
|
||||||
DashboardAddr string
|
DashboardAddr string `json:"dashboard_addr"`
|
||||||
|
|
||||||
// if DashboardPort equals 0, dashboard is not available
|
// if DashboardPort equals 0, dashboard is not available
|
||||||
DashboardPort int
|
DashboardPort int `json:"dashboard_port"`
|
||||||
DashboardUser string
|
DashboardUser string `json:"dashboard_user"`
|
||||||
DashboardPwd string
|
DashboardPwd string `json:"dashboard_pwd"`
|
||||||
AssetsDir string
|
AssetsDir string `json:"asserts_dir"`
|
||||||
LogFile string
|
LogFile string `json:"log_file"`
|
||||||
LogWay string // console or file
|
LogWay string `json:"log_way"` // console or file
|
||||||
LogLevel string
|
LogLevel string `json:"log_level"`
|
||||||
LogMaxDays int64
|
LogMaxDays int64 `json:"log_max_days"`
|
||||||
PrivilegeMode bool
|
Token string `json:"token"`
|
||||||
PrivilegeToken string
|
AuthTimeout int64 `json:"auth_timeout"`
|
||||||
AuthTimeout int64
|
SubDomainHost string `json:"subdomain_host"`
|
||||||
SubDomainHost string
|
TcpMux bool `json:"tcp_mux"`
|
||||||
TcpMux bool
|
|
||||||
|
|
||||||
PrivilegeAllowPorts map[int]struct{}
|
AllowPorts map[int]struct{}
|
||||||
MaxPoolCount int64
|
MaxPoolCount int64 `json:"max_pool_count"`
|
||||||
MaxPortsPerClient int64
|
MaxPortsPerClient int64 `json:"max_ports_per_client"`
|
||||||
HeartBeatTimeout int64
|
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
|
||||||
UserConnTimeout int64
|
UserConnTimeout int64 `json:"user_conn_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultServerCommonConf() *ServerCommonConf {
|
func GetDefaultServerConf() *ServerCommonConf {
|
||||||
return &ServerCommonConf{
|
return &ServerCommonConf{
|
||||||
ConfigFile: "./frps.ini",
|
BindAddr: "0.0.0.0",
|
||||||
BindAddr: "0.0.0.0",
|
BindPort: 7000,
|
||||||
BindPort: 7000,
|
BindUdpPort: 0,
|
||||||
BindUdpPort: 0,
|
KcpBindPort: 0,
|
||||||
KcpBindPort: 0,
|
ProxyBindAddr: "0.0.0.0",
|
||||||
ProxyBindAddr: "0.0.0.0",
|
VhostHttpPort: 0,
|
||||||
VhostHttpPort: 0,
|
VhostHttpsPort: 0,
|
||||||
VhostHttpsPort: 0,
|
DashboardAddr: "0.0.0.0",
|
||||||
DashboardAddr: "0.0.0.0",
|
DashboardPort: 0,
|
||||||
DashboardPort: 0,
|
DashboardUser: "admin",
|
||||||
DashboardUser: "admin",
|
DashboardPwd: "admin",
|
||||||
DashboardPwd: "admin",
|
AssetsDir: "",
|
||||||
AssetsDir: "",
|
LogFile: "console",
|
||||||
LogFile: "console",
|
LogWay: "console",
|
||||||
LogWay: "console",
|
LogLevel: "info",
|
||||||
LogLevel: "info",
|
LogMaxDays: 3,
|
||||||
LogMaxDays: 3,
|
Token: "",
|
||||||
PrivilegeMode: true,
|
AuthTimeout: 900,
|
||||||
PrivilegeToken: "",
|
SubDomainHost: "",
|
||||||
AuthTimeout: 900,
|
TcpMux: true,
|
||||||
SubDomainHost: "",
|
AllowPorts: make(map[int]struct{}),
|
||||||
TcpMux: true,
|
MaxPoolCount: 5,
|
||||||
PrivilegeAllowPorts: make(map[int]struct{}),
|
MaxPortsPerClient: 0,
|
||||||
MaxPoolCount: 5,
|
HeartBeatTimeout: 90,
|
||||||
MaxPortsPerClient: 0,
|
UserConnTimeout: 10,
|
||||||
HeartBeatTimeout: 90,
|
|
||||||
UserConnTimeout: 10,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load server common configure.
|
func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) {
|
||||||
func LoadServerCommonConf(conf ini.File) (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 (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
v int64
|
v int64
|
||||||
)
|
)
|
||||||
cfg = GetDefaultServerCommonConf()
|
if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "bind_addr")
|
|
||||||
if ok {
|
|
||||||
cfg.BindAddr = tmpStr
|
cfg.BindAddr = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "bind_port")
|
if tmpStr, ok = conf.Get("common", "bind_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
||||||
return
|
return
|
||||||
@@ -120,8 +135,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "bind_udp_port")
|
if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
||||||
return
|
return
|
||||||
@@ -130,8 +144,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "kcp_bind_port")
|
if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
||||||
return
|
return
|
||||||
@@ -140,15 +153,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "proxy_bind_addr")
|
if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
|
||||||
if ok {
|
|
||||||
cfg.ProxyBindAddr = tmpStr
|
cfg.ProxyBindAddr = tmpStr
|
||||||
} else {
|
} else {
|
||||||
cfg.ProxyBindAddr = cfg.BindAddr
|
cfg.ProxyBindAddr = cfg.BindAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "vhost_http_port")
|
if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
||||||
return
|
return
|
||||||
@@ -159,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.VhostHttpPort = 0
|
cfg.VhostHttpPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "vhost_https_port")
|
if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
||||||
return
|
return
|
||||||
@@ -171,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.VhostHttpsPort = 0
|
cfg.VhostHttpsPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_addr")
|
if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
|
||||||
if ok {
|
|
||||||
cfg.DashboardAddr = tmpStr
|
cfg.DashboardAddr = tmpStr
|
||||||
} else {
|
} else {
|
||||||
cfg.DashboardAddr = cfg.BindAddr
|
cfg.DashboardAddr = cfg.BindAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_port")
|
if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
||||||
return
|
return
|
||||||
@@ -190,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.DashboardPort = 0
|
cfg.DashboardPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_user")
|
if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
|
||||||
if ok {
|
|
||||||
cfg.DashboardUser = tmpStr
|
cfg.DashboardUser = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_pwd")
|
if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
|
||||||
if ok {
|
|
||||||
cfg.DashboardPwd = tmpStr
|
cfg.DashboardPwd = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "assets_dir")
|
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AssetsDir = tmpStr
|
cfg.AssetsDir = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_file")
|
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogFile = tmpStr
|
cfg.LogFile = tmpStr
|
||||||
if cfg.LogFile == "console" {
|
if cfg.LogFile == "console" {
|
||||||
cfg.LogWay = "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 tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogLevel = tmpStr
|
cfg.LogLevel = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||||
if ok {
|
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cfg.LogMaxDays = v
|
cfg.LogMaxDays = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "privilege_mode")
|
cfg.Token, _ = conf.Get("common", "token")
|
||||||
if ok {
|
|
||||||
if tmpStr == "true" {
|
if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
|
||||||
cfg.PrivilegeMode = true
|
// e.g. 1000-2000,2001,2002,3000-4000
|
||||||
|
ports, errRet := util.ParseRangeNumbers(allowPortsStr)
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, port := range ports {
|
||||||
|
cfg.AllowPorts[int(port)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivilegeMode configure
|
if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
|
||||||
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 v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||||
return
|
return
|
||||||
@@ -268,8 +258,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "max_ports_per_client")
|
if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||||
return
|
return
|
||||||
@@ -282,8 +271,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "authentication_timeout")
|
if tmpStr, ok = conf.Get("common", "authentication_timeout"); ok {
|
||||||
if ok {
|
|
||||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
|
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 tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
|
||||||
if ok {
|
|
||||||
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||||
if ok && tmpStr == "false" {
|
|
||||||
cfg.TcpMux = false
|
cfg.TcpMux = false
|
||||||
} else {
|
} else {
|
||||||
cfg.TcpMux = true
|
cfg.TcpMux = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||||
if ok {
|
|
||||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
||||||
@@ -317,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *ServerCommonConf) Check() (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/consts"
|
"github.com/fatedier/frp/models/consts"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
@@ -103,7 +104,7 @@ func (ctl *Control) Start() {
|
|||||||
loginRespMsg := &msg.LoginResp{
|
loginRespMsg := &msg.LoginResp{
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
RunId: ctl.runId,
|
RunId: ctl.runId,
|
||||||
ServerUdpPort: config.ServerCommonCfg.BindUdpPort,
|
ServerUdpPort: g.GlbServerCfg.BindUdpPort,
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
msg.WriteMsg(ctl.conn, loginRespMsg)
|
msg.WriteMsg(ctl.conn, loginRespMsg)
|
||||||
@@ -172,7 +173,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
|||||||
return
|
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")
|
err = fmt.Errorf("timeout trying to get work connection")
|
||||||
ctl.conn.Warn("%v", err)
|
ctl.conn.Warn("%v", err)
|
||||||
return
|
return
|
||||||
@@ -202,7 +203,7 @@ func (ctl *Control) writer() {
|
|||||||
defer ctl.allShutdown.Start()
|
defer ctl.allShutdown.Start()
|
||||||
defer ctl.writerShutdown.Done()
|
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 {
|
if err != nil {
|
||||||
ctl.conn.Error("crypto new writer error: %v", err)
|
ctl.conn.Error("crypto new writer error: %v", err)
|
||||||
ctl.allShutdown.Start()
|
ctl.allShutdown.Start()
|
||||||
@@ -231,7 +232,7 @@ func (ctl *Control) reader() {
|
|||||||
defer ctl.allShutdown.Start()
|
defer ctl.allShutdown.Start()
|
||||||
defer ctl.readerShutdown.Done()
|
defer ctl.readerShutdown.Done()
|
||||||
|
|
||||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||||
for {
|
for {
|
||||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -301,7 +302,7 @@ func (ctl *Control) manager() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-heartbeat.C:
|
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.conn.Warn("heartbeat timeout")
|
||||||
ctl.allShutdown.Start()
|
ctl.allShutdown.Start()
|
||||||
return
|
return
|
||||||
@@ -342,7 +343,7 @@ func (ctl *Control) manager() {
|
|||||||
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
||||||
var pxyConf config.ProxyConf
|
var pxyConf config.ProxyConf
|
||||||
// Load configures from NewProxy message and check.
|
// Load configures from NewProxy message and check.
|
||||||
pxyConf, err = config.NewProxyConf(pxyMsg)
|
pxyConf, err = config.NewProxyConfFromMsg(pxyMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -355,9 +356,9 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check ports used number in each client
|
// Check ports used number in each client
|
||||||
if config.ServerCommonCfg.MaxPortsPerClient > 0 {
|
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||||
ctl.mu.Lock()
|
ctl.mu.Lock()
|
||||||
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(config.ServerCommonCfg.MaxPortsPerClient) {
|
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) {
|
||||||
ctl.mu.Unlock()
|
ctl.mu.Unlock()
|
||||||
err = fmt.Errorf("exceed the max_ports_per_client")
|
err = fmt.Errorf("exceed the max_ports_per_client")
|
||||||
return
|
return
|
||||||
@@ -404,7 +405,7 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ServerCommonCfg.MaxPortsPerClient > 0 {
|
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||||
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
||||||
}
|
}
|
||||||
pxy.Close()
|
pxy.Close()
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"github.com/fatedier/frp/assets"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
@@ -36,10 +36,14 @@ func RunDashboardServer(addr string, port int) (err error) {
|
|||||||
// url router
|
// url router
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
|
|
||||||
user, passwd := config.ServerCommonCfg.DashboardUser, config.ServerCommonCfg.DashboardPwd
|
user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd
|
||||||
|
|
||||||
// api, see dashboard_api.go
|
// api, see dashboard_api.go
|
||||||
router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd))
|
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/tcp", frpNet.HttprouterBasicAuth(apiProxyTcp, user, passwd))
|
||||||
router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd))
|
router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd))
|
||||||
router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd))
|
router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/consts"
|
"github.com/fatedier/frp/models/consts"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"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]")
|
log.Info("Http request: [/api/serverinfo]")
|
||||||
cfg := config.ServerCommonCfg
|
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||||
serverStats := StatsGetServer()
|
serverStats := StatsGetServer()
|
||||||
res = ServerInfoResp{
|
res = ServerInfoResp{
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
@@ -189,6 +190,119 @@ func getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
|
|||||||
return
|
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
|
// api/proxy/traffic/:name
|
||||||
type GetProxyTrafficResp struct {
|
type GetProxyTrafficResp struct {
|
||||||
GeneralResponse
|
GeneralResponse
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
"github.com/fatedier/frp/utils/metric"
|
"github.com/fatedier/frp/utils/metric"
|
||||||
)
|
)
|
||||||
@@ -92,19 +92,19 @@ func StatsClearUselessInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsNewClient() {
|
func StatsNewClient() {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.ClientCounts.Inc(1)
|
globalStats.ClientCounts.Inc(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StatsCloseClient() {
|
func StatsCloseClient() {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.ClientCounts.Dec(1)
|
globalStats.ClientCounts.Dec(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StatsNewProxy(name string, proxyType string) {
|
func StatsNewProxy(name string, proxyType string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
defer globalStats.mu.Unlock()
|
defer globalStats.mu.Unlock()
|
||||||
counter, ok := globalStats.ProxyTypeCounts[proxyType]
|
counter, ok := globalStats.ProxyTypeCounts[proxyType]
|
||||||
@@ -130,7 +130,7 @@ func StatsNewProxy(name string, proxyType string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsCloseProxy(proxyName string, proxyType string) {
|
func StatsCloseProxy(proxyName string, proxyType string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
defer globalStats.mu.Unlock()
|
defer globalStats.mu.Unlock()
|
||||||
if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
|
if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
|
||||||
@@ -143,7 +143,7 @@ func StatsCloseProxy(proxyName string, proxyType string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsOpenConnection(name string) {
|
func StatsOpenConnection(name string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.CurConns.Inc(1)
|
globalStats.CurConns.Inc(1)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -157,7 +157,7 @@ func StatsOpenConnection(name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsCloseConnection(name string) {
|
func StatsCloseConnection(name string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.CurConns.Dec(1)
|
globalStats.CurConns.Dec(1)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -171,7 +171,7 @@ func StatsCloseConnection(name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsAddTrafficIn(name string, trafficIn int64) {
|
func StatsAddTrafficIn(name string, trafficIn int64) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.TotalTrafficIn.Inc(trafficIn)
|
globalStats.TotalTrafficIn.Inc(trafficIn)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -186,7 +186,7 @@ func StatsAddTrafficIn(name string, trafficIn int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsAddTrafficOut(name string, trafficOut int64) {
|
func StatsAddTrafficOut(name string, trafficOut int64) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.TotalTrafficOut.Inc(trafficOut)
|
globalStats.TotalTrafficOut.Inc(trafficOut)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -263,6 +263,37 @@ func StatsGetProxiesByType(proxyType string) []*ProxyStats {
|
|||||||
return res
|
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 {
|
type ProxyTrafficInfo struct {
|
||||||
Name string
|
Name string
|
||||||
TrafficIn []int64
|
TrafficIn []int64
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/models/proto/udp"
|
"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) {
|
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
||||||
basePxy := BaseProxy{
|
basePxy := BaseProxy{
|
||||||
name: pxyConf.GetName(),
|
name: pxyConf.GetBaseInfo().ProxyName,
|
||||||
ctl: ctl,
|
ctl: ctl,
|
||||||
listeners: make([]frpNet.Listener, 0),
|
listeners: make([]frpNet.Listener, 0),
|
||||||
Logger: log.NewPrefixLogger(ctl.runId),
|
Logger: log.NewPrefixLogger(ctl.runId),
|
||||||
@@ -191,7 +192,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
|
|||||||
|
|
||||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||||
pxy.cfg.RemotePort = 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 {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
@@ -244,7 +245,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
|||||||
}
|
}
|
||||||
tmpDomain := routeConfig.Domain
|
tmpDomain := routeConfig.Domain
|
||||||
tmpLocation := routeConfig.Location
|
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.closeFuncs = append(pxy.closeFuncs, func() {
|
||||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||||
})
|
})
|
||||||
@@ -253,7 +254,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pxy.cfg.SubDomain != "" {
|
if pxy.cfg.SubDomain != "" {
|
||||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
routeConfig.Location = location
|
routeConfig.Location = location
|
||||||
err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
|
err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
|
||||||
@@ -262,7 +263,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
|||||||
}
|
}
|
||||||
tmpDomain := routeConfig.Domain
|
tmpDomain := routeConfig.Domain
|
||||||
tmpLocation := routeConfig.Location
|
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.closeFuncs = append(pxy.closeFuncs, func() {
|
||||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
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
|
var rwc io.ReadWriteCloser = tmpConn
|
||||||
if pxy.cfg.UseEncryption {
|
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 {
|
if err != nil {
|
||||||
pxy.Error("create encryption stream error: %v", err)
|
pxy.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
@@ -334,11 +335,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
|||||||
l.AddLogPrefix(pxy.name)
|
l.AddLogPrefix(pxy.name)
|
||||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||||
pxy.listeners = append(pxy.listeners, l)
|
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 != "" {
|
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)
|
l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
@@ -347,7 +348,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
|||||||
l.AddLogPrefix(pxy.name)
|
l.AddLogPrefix(pxy.name)
|
||||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||||
pxy.listeners = append(pxy.listeners, l)
|
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)
|
pxy.startListenHandler(pxy, HandleUserTcpConnection)
|
||||||
@@ -478,7 +479,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
|
|||||||
|
|
||||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||||
pxy.cfg.RemotePort = 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 {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
@@ -644,7 +645,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) {
|
|||||||
var local io.ReadWriteCloser = workConn
|
var local io.ReadWriteCloser = workConn
|
||||||
cfg := pxy.GetConf().GetBaseInfo()
|
cfg := pxy.GetConf().GetBaseInfo()
|
||||||
if cfg.UseEncryption {
|
if cfg.UseEncryption {
|
||||||
local, err = frpIo.WithEncryption(local, []byte(config.ServerCommonCfg.PrivilegeToken))
|
local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pxy.Error("create encryption stream error: %v", err)
|
pxy.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"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/models/msg"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
"github.com/fatedier/frp/utils/vhost"
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
|
|
||||||
"github.com/xtaci/smux"
|
fmux "github.com/hashicorp/yamux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -71,13 +71,13 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewService() (svr *Service, err error) {
|
func NewService() (svr *Service, err error) {
|
||||||
cfg := config.ServerCommonCfg
|
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||||
svr = &Service{
|
svr = &Service{
|
||||||
ctlManager: NewControlManager(),
|
ctlManager: NewControlManager(),
|
||||||
pxyManager: NewProxyManager(),
|
pxyManager: NewProxyManager(),
|
||||||
visitorManager: NewVisitorManager(),
|
visitorManager: NewVisitorManager(),
|
||||||
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||||
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init assets.
|
// Init assets.
|
||||||
@@ -170,7 +170,7 @@ func (svr *Service) Run() {
|
|||||||
if svr.natHoleController != nil {
|
if svr.natHoleController != nil {
|
||||||
go svr.natHoleController.Run()
|
go svr.natHoleController.Run()
|
||||||
}
|
}
|
||||||
if config.ServerCommonCfg.KcpBindPort > 0 {
|
if g.GlbServerCfg.KcpBindPort > 0 {
|
||||||
go svr.HandleListener(svr.kcpListener)
|
go svr.HandleListener(svr.kcpListener)
|
||||||
}
|
}
|
||||||
svr.HandleListener(svr.listener)
|
svr.HandleListener(svr.listener)
|
||||||
@@ -233,8 +233,8 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ServerCommonCfg.TcpMux {
|
if g.GlbServerCfg.TcpMux {
|
||||||
session, err := smux.Server(frpConn, nil)
|
session, err := fmux.Server(frpConn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to create mux connection: %v", err)
|
log.Warn("Failed to create mux connection: %v", err)
|
||||||
frpConn.Close()
|
frpConn.Close()
|
||||||
@@ -270,11 +270,11 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
|
|||||||
|
|
||||||
// Check auth.
|
// Check auth.
|
||||||
nowTime := time.Now().Unix()
|
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")
|
err = fmt.Errorf("authorization timeout")
|
||||||
return
|
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")
|
err = fmt.Errorf("authorization failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ server_port = 10700
|
|||||||
log_file = ./frpc.log
|
log_file = ./frpc.log
|
||||||
# debug, info, warn, error
|
# debug, info, warn, error
|
||||||
log_level = debug
|
log_level = debug
|
||||||
privilege_token = 123456
|
token = 123456
|
||||||
admin_port = 10600
|
admin_port = 10600
|
||||||
admin_user = abc
|
admin_user = abc
|
||||||
admin_pwd = abc
|
admin_pwd = abc
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ server_port = 10700
|
|||||||
log_file = ./frpc_visitor.log
|
log_file = ./frpc_visitor.log
|
||||||
# debug, info, warn, error
|
# debug, info, warn, error
|
||||||
log_level = debug
|
log_level = debug
|
||||||
privilege_token = 123456
|
token = 123456
|
||||||
|
|
||||||
[stcp_visitor]
|
[stcp_visitor]
|
||||||
type = stcp
|
type = stcp
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ bind_port = 10700
|
|||||||
vhost_http_port = 10804
|
vhost_http_port = 10804
|
||||||
log_file = ./frps.log
|
log_file = ./frps.log
|
||||||
log_level = debug
|
log_level = debug
|
||||||
privilege_token = 123456
|
token = 123456
|
||||||
privilege_allow_ports = 10000-20000,20002,30000-50000
|
allow_ports = 10000-20000,20002,30000-50000
|
||||||
subdomain_host = sub.com
|
subdomain_host = sub.com
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ func TestWebSocket(t *testing.T) {
|
|||||||
assert.Equal(TEST_HTTP_NORMAL_STR, string(msg))
|
assert.Equal(TEST_HTTP_NORMAL_STR, string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrivilegeAllowPorts(t *testing.T) {
|
func TestAllowPorts(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
// Port not allowed
|
// Port not allowed
|
||||||
status, err := getProxyStatus(ProxyTcpPortNotAllowed)
|
status, err := getProxyStatus(ProxyTcpPortNotAllowed)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
|
|
||||||
kcp "github.com/xtaci/kcp-go"
|
kcp "github.com/fatedier/kcp-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Conn is the interface of connections used in frp.
|
// Conn is the interface of connections used in frp.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.16.1"
|
var version string = "0.18.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|||||||
31
vendor/github.com/docopt/docopt-go/.travis.yml
generated
vendored
31
vendor/github.com/docopt/docopt-go/.travis.yml
generated
vendored
@@ -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
|
|
||||||
20
vendor/github.com/docopt/docopt-go/LICENSE
generated
vendored
20
vendor/github.com/docopt/docopt-go/LICENSE
generated
vendored
@@ -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.
|
|
||||||
88
vendor/github.com/docopt/docopt-go/README.md
generated
vendored
88
vendor/github.com/docopt/docopt-go/README.md
generated
vendored
@@ -1,88 +0,0 @@
|
|||||||
docopt-go
|
|
||||||
=========
|
|
||||||
|
|
||||||
[](https://travis-ci.org/docopt/docopt.go)
|
|
||||||
[](https://coveralls.io/r/docopt/docopt.go)
|
|
||||||
[](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 <name>...
|
|
||||||
naval_fate ship <name> move <x> <y> [--speed=<kn>]
|
|
||||||
naval_fate ship shoot <x> <y>
|
|
||||||
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
|
|
||||||
naval_fate -h | --help
|
|
||||||
naval_fate --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help Show this screen.
|
|
||||||
--version Show version.
|
|
||||||
--speed=<kn> 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`.
|
|
||||||
1239
vendor/github.com/docopt/docopt-go/docopt.go
generated
vendored
1239
vendor/github.com/docopt/docopt-go/docopt.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1536
vendor/github.com/docopt/docopt-go/docopt_test.go
generated
vendored
1536
vendor/github.com/docopt/docopt-go/docopt_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
37
vendor/github.com/docopt/docopt-go/example_test.go
generated
vendored
37
vendor/github.com/docopt/docopt-go/example_test.go
generated
vendored
@@ -1,37 +0,0 @@
|
|||||||
package docopt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleParse() {
|
|
||||||
usage := `Usage:
|
|
||||||
config_example tcp [<host>] [--force] [--timeout=<seconds>]
|
|
||||||
config_example serial <port> [--baud=<rate>] [--timeout=<seconds>]
|
|
||||||
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 <nil>
|
|
||||||
// --force true
|
|
||||||
// --help false
|
|
||||||
// --timeout <nil>
|
|
||||||
// --version false
|
|
||||||
// -h false
|
|
||||||
// <host> 127.0.0.1
|
|
||||||
// <port> <nil>
|
|
||||||
// serial false
|
|
||||||
// tcp true
|
|
||||||
}
|
|
||||||
29
vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go
generated
vendored
29
vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go
generated
vendored
@@ -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)
|
|
||||||
}
|
|
||||||
26
vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go
generated
vendored
26
vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go
generated
vendored
@@ -1,26 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Not a serious example.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
calculator_example <value> ( ( + | - | * | / ) <value> )...
|
|
||||||
calculator_example <function> <value> [( , <value> )]...
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
76
vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go
generated
vendored
76
vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go
generated
vendored
@@ -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
|
|
||||||
<host>=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 [<host>] [--force] [--timeout=<seconds>]
|
|
||||||
config_file_example serial <port> [--baud=<rate>] [--timeout=<seconds>]
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
22
vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go
generated
vendored
22
vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go
generated
vendored
@@ -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=<path>)...
|
|
||||||
counted_example <file> <file>
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
38
vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go
generated
vendored
38
vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go
generated
vendored
@@ -1,38 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git branch [options] [-r | -a] [--merged=<commit> | --no-merged=<commit>]
|
|
||||||
git branch [options] [-l] [-f] <branchname> [<start-point>]
|
|
||||||
git branch [options] [-r] (-d | -D) <branchname>
|
|
||||||
git branch [options] (-m | -M) [<oldbranch>] <newbranch>
|
|
||||||
|
|
||||||
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=<when> use colored output
|
|
||||||
-r act on remote-tracking branches
|
|
||||||
--contains=<commit> print only branches that contain the commit
|
|
||||||
--abbrev=<n> use <n> 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=<commit> print only not merged branches
|
|
||||||
--merged=<commit> print only merged branches
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
30
vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go
generated
vendored
30
vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git checkout [options] <branch>
|
|
||||||
git checkout [options] <branch> -- <file>...
|
|
||||||
|
|
||||||
options:
|
|
||||||
-q, --quiet suppress progress reporting
|
|
||||||
-b <branch> create and checkout a new branch
|
|
||||||
-B <branch> create/reset and checkout a branch
|
|
||||||
-l create reflog for new branch
|
|
||||||
-t, --track set upstream info for new branch
|
|
||||||
--orphan <new branch>
|
|
||||||
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 <style> conflict style (merge or diff3)
|
|
||||||
-p, --patch select hunks interactively
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
37
vendor/github.com/docopt/docopt-go/examples/git/clone/git_clone.go
generated
vendored
37
vendor/github.com/docopt/docopt-go/examples/git/clone/git_clone.go
generated
vendored
@@ -1,37 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git clone [options] [--] <repo> [<dir>]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-v, --verbose be more verbose
|
|
||||||
-q, --quiet be more quiet
|
|
||||||
--progress force progress reporting
|
|
||||||
-n, --no-checkout don't create a checkout
|
|
||||||
--bare create a bare repository
|
|
||||||
--mirror create a mirror repository (implies bare)
|
|
||||||
-l, --local to clone from a local repository
|
|
||||||
--no-hardlinks don't use local hardlinks, always copy
|
|
||||||
-s, --shared setup as shared repository
|
|
||||||
--recursive initialize submodules in the clone
|
|
||||||
--recurse-submodules initialize submodules in the clone
|
|
||||||
--template <template-directory>
|
|
||||||
directory from which templates will be used
|
|
||||||
--reference <repo> reference repository
|
|
||||||
-o, --origin <branch>
|
|
||||||
use <branch> instead of 'origin' to track upstream
|
|
||||||
-b, --branch <branch>
|
|
||||||
checkout <branch> instead of the remote's HEAD
|
|
||||||
-u, --upload-pack <path>
|
|
||||||
path to git-upload-pack on the remote
|
|
||||||
--depth <depth> create a shallow clone of that depth
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
108
vendor/github.com/docopt/docopt-go/examples/git/git.go
generated
vendored
108
vendor/github.com/docopt/docopt-go/examples/git/git.go
generated
vendored
@@ -1,108 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git [--version] [--exec-path=<path>] [--html-path]
|
|
||||||
[-p|--paginate|--no-pager] [--no-replace-objects]
|
|
||||||
[--bare] [--git-dir=<path>] [--work-tree=<path>]
|
|
||||||
[-c <name>=<value>] [--help]
|
|
||||||
<command> [<args>...]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-c <name=value>
|
|
||||||
-h, --help
|
|
||||||
-p, --paginate
|
|
||||||
|
|
||||||
The most commonly used git commands are:
|
|
||||||
add Add file contents to the index
|
|
||||||
branch List, create, or delete branches
|
|
||||||
checkout Checkout a branch or paths to the working tree
|
|
||||||
clone Clone a repository into a new directory
|
|
||||||
commit Record changes to the repository
|
|
||||||
push Update remote refs along with associated objects
|
|
||||||
remote Manage set of tracked repositories
|
|
||||||
|
|
||||||
See 'git help <command>' for more information on a specific command.
|
|
||||||
`
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "git version 1.7.4.4", true)
|
|
||||||
|
|
||||||
fmt.Println("global arguments:")
|
|
||||||
fmt.Println(args)
|
|
||||||
|
|
||||||
fmt.Println("command arguments:")
|
|
||||||
cmd := args["<command>"].(string)
|
|
||||||
cmdArgs := args["<args>"].([]string)
|
|
||||||
|
|
||||||
err := runCommand(cmd, cmdArgs)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func goRun(scriptName string, args []string) (err error) {
|
|
||||||
cmdArgs := make([]string, 2)
|
|
||||||
cmdArgs[0] = "run"
|
|
||||||
cmdArgs[1] = scriptName
|
|
||||||
cmdArgs = append(cmdArgs, args...)
|
|
||||||
osCmd := exec.Command("go", cmdArgs...)
|
|
||||||
var out []byte
|
|
||||||
out, err = osCmd.Output()
|
|
||||||
fmt.Println(string(out))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCommand(cmd string, args []string) (err error) {
|
|
||||||
argv := make([]string, 1)
|
|
||||||
argv[0] = cmd
|
|
||||||
argv = append(argv, args...)
|
|
||||||
switch cmd {
|
|
||||||
case "add":
|
|
||||||
// subcommand is a function call
|
|
||||||
return cmdAdd(argv)
|
|
||||||
case "branch":
|
|
||||||
// subcommand is a script
|
|
||||||
return goRun("branch/git_branch.go", argv)
|
|
||||||
case "checkout", "clone", "commit", "push", "remote":
|
|
||||||
// subcommand is a script
|
|
||||||
scriptName := fmt.Sprintf("%s/git_%s.go", cmd, cmd)
|
|
||||||
return goRun(scriptName, argv)
|
|
||||||
case "help", "":
|
|
||||||
return goRun("git.go", []string{"git_add.go", "--help"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("%s is not a git command. See 'git help'", cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdAdd(argv []string) (err error) {
|
|
||||||
usage := `usage: git add [options] [--] [<filepattern>...]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h, --help
|
|
||||||
-n, --dry-run dry run
|
|
||||||
-v, --verbose be verbose
|
|
||||||
-i, --interactive interactive picking
|
|
||||||
-p, --patch select hunks interactively
|
|
||||||
-e, --edit edit current diff and apply
|
|
||||||
-f, --force allow adding otherwise ignored files
|
|
||||||
-u, --update update tracked files
|
|
||||||
-N, --intent-to-add record only the fact that the path will be added later
|
|
||||||
-A, --all add all, noticing removal of tracked files
|
|
||||||
--refresh don't add, only refresh the index
|
|
||||||
--ignore-errors just skip files which cannot be added because of errors
|
|
||||||
--ignore-missing check if - even missing - files are ignored in dry run
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
34
vendor/github.com/docopt/docopt-go/examples/git/push/git_push.go
generated
vendored
34
vendor/github.com/docopt/docopt-go/examples/git/push/git_push.go
generated
vendored
@@ -1,34 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git push [options] [<repository> [<refspec>...]]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h, --help
|
|
||||||
-v, --verbose be more verbose
|
|
||||||
-q, --quiet be more quiet
|
|
||||||
--repo <repository> repository
|
|
||||||
--all push all refs
|
|
||||||
--mirror mirror all refs
|
|
||||||
--delete delete refs
|
|
||||||
--tags push tags (can't be used with --all or --mirror)
|
|
||||||
-n, --dry-run dry run
|
|
||||||
--porcelain machine-readable output
|
|
||||||
-f, --force force updates
|
|
||||||
--thin use thin pack
|
|
||||||
--receive-pack <receive-pack>
|
|
||||||
receive pack program
|
|
||||||
--exec <receive-pack>
|
|
||||||
receive pack program
|
|
||||||
-u, --set-upstream set upstream for git pull/status
|
|
||||||
--progress force progress reporting
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
28
vendor/github.com/docopt/docopt-go/examples/git/remote/git_remote.go
generated
vendored
28
vendor/github.com/docopt/docopt-go/examples/git/remote/git_remote.go
generated
vendored
@@ -1,28 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git remote [-v | --verbose]
|
|
||||||
git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
|
|
||||||
git remote rename <old> <new>
|
|
||||||
git remote rm <name>
|
|
||||||
git remote set-head <name> (-a | -d | <branch>)
|
|
||||||
git remote [-v | --verbose] show [-n] <name>
|
|
||||||
git remote prune [-n | --dry-run] <name>
|
|
||||||
git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]
|
|
||||||
git remote set-branches <name> [--add] <branch>...
|
|
||||||
git remote set-url <name> <newurl> [<oldurl>]
|
|
||||||
git remote set-url --add <name> <newurl>
|
|
||||||
git remote set-url --delete <name> <url>
|
|
||||||
|
|
||||||
options:
|
|
||||||
-v, --verbose be verbose; must be placed before a subcommand
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
28
vendor/github.com/docopt/docopt-go/examples/naval_fate/naval_fate.go
generated
vendored
28
vendor/github.com/docopt/docopt-go/examples/naval_fate/naval_fate.go
generated
vendored
@@ -1,28 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Naval Fate.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
naval_fate ship new <name>...
|
|
||||||
naval_fate ship <name> move <x> <y> [--speed=<kn>]
|
|
||||||
naval_fate ship shoot <x> <y>
|
|
||||||
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
|
|
||||||
naval_fate -h | --help
|
|
||||||
naval_fate --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help Show this screen.
|
|
||||||
--version Show version.
|
|
||||||
--speed=<kn> 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)
|
|
||||||
}
|
|
||||||
19
vendor/github.com/docopt/docopt-go/examples/odd_even/odd_even_example.go
generated
vendored
19
vendor/github.com/docopt/docopt-go/examples/odd_even/odd_even_example.go
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage: odd_even_example [-h | --help] (ODD EVEN)...
|
|
||||||
|
|
||||||
Example, try:
|
|
||||||
odd_even_example 1 2 3 4
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
43
vendor/github.com/docopt/docopt-go/examples/options/options_example.go
generated
vendored
43
vendor/github.com/docopt/docopt-go/examples/options/options_example.go
generated
vendored
@@ -1,43 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Example of program with many options using docopt.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
options_example [-hvqrf NAME] [--exclude=PATTERNS]
|
|
||||||
[--select=ERRORS | --ignore=ERRORS] [--show-source]
|
|
||||||
[--statistics] [--count] [--benchmark] PATH...
|
|
||||||
options_example (--doctest | --testsuite=DIR)
|
|
||||||
options_example --version
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
PATH destination path
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help show this help message and exit
|
|
||||||
--version show version and exit
|
|
||||||
-v --verbose print status messages
|
|
||||||
-q --quiet report only file names
|
|
||||||
-r --repeat show all occurrences of the same error
|
|
||||||
--exclude=PATTERNS exclude files or directories which match these comma
|
|
||||||
separated patterns [default: .svn,CVS,.bzr,.hg,.git]
|
|
||||||
-f NAME --file=NAME when parsing directories, only check filenames matching
|
|
||||||
these comma separated patterns [default: *.go]
|
|
||||||
--select=ERRORS select errors and warnings (e.g. E,W6)
|
|
||||||
--ignore=ERRORS skip errors and warnings (e.g. E4,W)
|
|
||||||
--show-source show source code for each error
|
|
||||||
--statistics count errors and warnings
|
|
||||||
--count print total number of errors and warnings to standard
|
|
||||||
error and set exit code to 1 if total is not null
|
|
||||||
--benchmark measure processing speed
|
|
||||||
--testsuite=DIR run regression tests from dir
|
|
||||||
--doctest run doctest on myself`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "1.0.0rc2", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
24
vendor/github.com/docopt/docopt-go/examples/options_shortcut/options_shortcut_example.go
generated
vendored
24
vendor/github.com/docopt/docopt-go/examples/options_shortcut/options_shortcut_example.go
generated
vendored
@@ -1,24 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Example of program which uses [options] shortcut in pattern.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
options_shortcut_example [options] <port>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help show this help message and exit
|
|
||||||
--version show version and exit
|
|
||||||
-n, --number N use N as a number
|
|
||||||
-t, --timeout TIMEOUT set timeout TIMEOUT seconds
|
|
||||||
--apply apply changes to database
|
|
||||||
-q operate in quiet mode`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "1.0.0rc2", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
16
vendor/github.com/docopt/docopt-go/examples/quick/quick_example.go
generated
vendored
16
vendor/github.com/docopt/docopt-go/examples/quick/quick_example.go
generated
vendored
@@ -1,16 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage:
|
|
||||||
quick_example tcp <host> <port> [--timeout=<seconds>]
|
|
||||||
quick_example serial <port> [--baud=9600] [--timeout=<seconds>]
|
|
||||||
quick_example -h | --help | --version`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "0.1.1rc", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
31
vendor/github.com/docopt/docopt-go/examples/type_assert/type_assert_example.go
generated
vendored
31
vendor/github.com/docopt/docopt-go/examples/type_assert/type_assert_example.go
generated
vendored
@@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: foo [-x] [-y]`
|
|
||||||
|
|
||||||
arguments, err := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(arguments)
|
|
||||||
|
|
||||||
var x = arguments["-x"].(bool) // type assertion required
|
|
||||||
if x == true {
|
|
||||||
fmt.Println("x is true")
|
|
||||||
}
|
|
||||||
|
|
||||||
y := arguments["-y"] // no type assertion needed
|
|
||||||
if y == true {
|
|
||||||
fmt.Println("y is true")
|
|
||||||
}
|
|
||||||
y2 := arguments["-y"]
|
|
||||||
if y2 == 10 { // this will never be true, a type assertion would have produced a build error
|
|
||||||
fmt.Println("y is 10")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
vendor/github.com/docopt/docopt-go/test_golang.docopt
generated
vendored
9
vendor/github.com/docopt/docopt-go/test_golang.docopt
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
r"""usage: prog [NAME_-2]..."""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME_-2": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME_-2": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME_-2": []}
|
|
||||||
957
vendor/github.com/docopt/docopt-go/testcases.docopt
generated
vendored
957
vendor/github.com/docopt/docopt-go/testcases.docopt
generated
vendored
@@ -1,957 +0,0 @@
|
|||||||
r"""Usage: prog
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{}
|
|
||||||
|
|
||||||
$ prog --xxx
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -a All.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"-a": false}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true}
|
|
||||||
|
|
||||||
$ prog -x
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: --all All.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--all": false}
|
|
||||||
|
|
||||||
$ prog --all
|
|
||||||
{"--all": true}
|
|
||||||
|
|
||||||
$ prog --xxx
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -v, --verbose Verbose.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --verbose
|
|
||||||
{"--verbose": true}
|
|
||||||
|
|
||||||
$ prog --ver
|
|
||||||
{"--verbose": true}
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"--verbose": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -p PATH
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -p home/
|
|
||||||
{"-p": "home/"}
|
|
||||||
|
|
||||||
$ prog -phome/
|
|
||||||
{"-p": "home/"}
|
|
||||||
|
|
||||||
$ prog -p
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: --path <path>
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --path home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --path=home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --pa home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --pa=home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --path
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -p PATH, --path=<path> Path to files.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -proot
|
|
||||||
{"--path": "root"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -p --path PATH Path to files.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -p root
|
|
||||||
{"--path": "root"}
|
|
||||||
|
|
||||||
$ prog --path root
|
|
||||||
{"--path": "root"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-p PATH Path to files [default: ./]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"-p": "./"}
|
|
||||||
|
|
||||||
$ prog -phome
|
|
||||||
{"-p": "home"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""UsAgE: prog [options]
|
|
||||||
|
|
||||||
OpTiOnS: --path=<files> Path to files
|
|
||||||
[dEfAuLt: /root]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--path": "/root"}
|
|
||||||
|
|
||||||
$ prog --path=home
|
|
||||||
{"--path": "home"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [options]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-a Add
|
|
||||||
-r Remote
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -r -m Hello
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "Hello"}
|
|
||||||
|
|
||||||
$ prog -armyourass
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "yourass"}
|
|
||||||
|
|
||||||
$ prog -a -r
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": null}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: --version
|
|
||||||
--verbose
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --version
|
|
||||||
{"--version": true,
|
|
||||||
"--verbose": false}
|
|
||||||
|
|
||||||
$ prog --verbose
|
|
||||||
{"--version": false,
|
|
||||||
"--verbose": true}
|
|
||||||
|
|
||||||
$ prog --ver
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog --verb
|
|
||||||
{"--version": false,
|
|
||||||
"--verbose": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-a -r -m <msg>]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-a Add
|
|
||||||
-r Remote
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -armyourass
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "yourass"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-armmsg]
|
|
||||||
|
|
||||||
options: -a Add
|
|
||||||
-r Remote
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -r -m Hello
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "Hello"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog -a -b
|
|
||||||
|
|
||||||
options:
|
|
||||||
-a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (-a -b)
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-a] -b
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
{"-a": false, "-b": true}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [(-a -b)]
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-a": false, "-b": false}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (-a|-b)
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true, "-b": false}
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
{"-a": false, "-b": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [ -a | -b ]
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-a": false, "-b": false}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true, "-b": false}
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
{"-a": false, "-b": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog <arg>"""
|
|
||||||
$ prog 10
|
|
||||||
{"<arg>": "10"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [<arg>]"""
|
|
||||||
$ prog 10
|
|
||||||
{"<arg>": "10"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<arg>": null}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog <kind> <name> <type>"""
|
|
||||||
$ prog 10 20 40
|
|
||||||
{"<kind>": "10", "<name>": "20", "<type>": "40"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog <kind> [<name> <type>]"""
|
|
||||||
$ prog 10 20 40
|
|
||||||
{"<kind>": "10", "<name>": "20", "<type>": "40"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
{"<kind>": "10", "<name>": "20", "<type>": null}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [<kind> | <name> <type>]"""
|
|
||||||
$ prog 10 20 40
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog 20 40
|
|
||||||
{"<kind>": null, "<name>": "20", "<type>": "40"}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<kind>": null, "<name>": null, "<type>": null}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (<kind> --all | <name>)
|
|
||||||
|
|
||||||
options:
|
|
||||||
--all
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog 10 --all
|
|
||||||
{"<kind>": "10", "--all": true, "<name>": null}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"<kind>": null, "--all": false, "<name>": "10"}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [<name> <name>]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"<name>": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"<name>": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<name>": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [(<name> <name>)]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"<name>": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<name>": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog NAME..."""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [NAME]..."""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [NAME...]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [NAME [NAME ...]]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (NAME | --foo NAME)
|
|
||||||
|
|
||||||
options: --foo
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": "10", "--foo": false}
|
|
||||||
|
|
||||||
$ prog --foo 10
|
|
||||||
{"NAME": "10", "--foo": true}
|
|
||||||
|
|
||||||
$ prog --foo=10
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (NAME | --foo) [--bar | NAME]
|
|
||||||
|
|
||||||
options: --foo
|
|
||||||
options: --bar
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"], "--foo": false, "--bar": false}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"], "--foo": false, "--bar": false}
|
|
||||||
|
|
||||||
$ prog --foo --bar
|
|
||||||
{"NAME": [], "--foo": true, "--bar": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Naval Fate.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
prog ship new <name>...
|
|
||||||
prog ship [<name>] move <x> <y> [--speed=<kn>]
|
|
||||||
prog ship shoot <x> <y>
|
|
||||||
prog mine (set|remove) <x> <y> [--moored|--drifting]
|
|
||||||
prog -h | --help
|
|
||||||
prog --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help Show this screen.
|
|
||||||
--version Show version.
|
|
||||||
--speed=<kn> Speed in knots [default: 10].
|
|
||||||
--moored Mored (anchored) mine.
|
|
||||||
--drifting Drifting mine.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog ship Guardian move 150 300 --speed=20
|
|
||||||
{"--drifting": false,
|
|
||||||
"--help": false,
|
|
||||||
"--moored": false,
|
|
||||||
"--speed": "20",
|
|
||||||
"--version": false,
|
|
||||||
"<name>": ["Guardian"],
|
|
||||||
"<x>": "150",
|
|
||||||
"<y>": "300",
|
|
||||||
"mine": false,
|
|
||||||
"move": true,
|
|
||||||
"new": false,
|
|
||||||
"remove": false,
|
|
||||||
"set": false,
|
|
||||||
"ship": true,
|
|
||||||
"shoot": false}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog --hello"""
|
|
||||||
$ prog --hello
|
|
||||||
{"--hello": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [--hello=<world>]"""
|
|
||||||
$ prog
|
|
||||||
{"--hello": null}
|
|
||||||
|
|
||||||
$ prog --hello wrld
|
|
||||||
{"--hello": "wrld"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-o]"""
|
|
||||||
$ prog
|
|
||||||
{"-o": false}
|
|
||||||
|
|
||||||
$ prog -o
|
|
||||||
{"-o": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-opr]"""
|
|
||||||
$ prog -op
|
|
||||||
{"-o": true, "-p": true, "-r": false}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog --aabb | --aa"""
|
|
||||||
$ prog --aa
|
|
||||||
{"--aabb": false, "--aa": true}
|
|
||||||
|
|
||||||
$ prog --a
|
|
||||||
"user-error" # not a unique prefix
|
|
||||||
|
|
||||||
#
|
|
||||||
# Counting number of flags
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: prog -v"""
|
|
||||||
$ prog -v
|
|
||||||
{"-v": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [-v -v]"""
|
|
||||||
$ prog
|
|
||||||
{"-v": 0}
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"-v": 1}
|
|
||||||
|
|
||||||
$ prog -vv
|
|
||||||
{"-v": 2}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog -v ..."""
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"-v": 1}
|
|
||||||
|
|
||||||
$ prog -vv
|
|
||||||
{"-v": 2}
|
|
||||||
|
|
||||||
$ prog -vvvvvv
|
|
||||||
{"-v": 6}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [-v | -vv | -vvv]
|
|
||||||
|
|
||||||
This one is probably most readable user-friednly variant.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"-v": 0}
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"-v": 1}
|
|
||||||
|
|
||||||
$ prog -vv
|
|
||||||
{"-v": 2}
|
|
||||||
|
|
||||||
$ prog -vvvv
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [--ver --ver]"""
|
|
||||||
$ prog --ver --ver
|
|
||||||
{"--ver": 2}
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Counting commands
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [go]"""
|
|
||||||
$ prog go
|
|
||||||
{"go": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [go go]"""
|
|
||||||
$ prog
|
|
||||||
{"go": 0}
|
|
||||||
|
|
||||||
$ prog go
|
|
||||||
{"go": 1}
|
|
||||||
|
|
||||||
$ prog go go
|
|
||||||
{"go": 2}
|
|
||||||
|
|
||||||
$ prog go go go
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
r"""usage: prog go..."""
|
|
||||||
$ prog go go go go go
|
|
||||||
{"go": 5}
|
|
||||||
|
|
||||||
#
|
|
||||||
# [options] does not include options from usage-pattern
|
|
||||||
#
|
|
||||||
r"""usage: prog [options] [-a]
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
"""
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true, "-b": false}
|
|
||||||
|
|
||||||
$ prog -aa
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test [options] shourtcut
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: prog [options] A
|
|
||||||
Options:
|
|
||||||
-q Be quiet
|
|
||||||
-v Be verbose.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog arg
|
|
||||||
{"A": "arg", "-v": false, "-q": false}
|
|
||||||
|
|
||||||
$ prog -v arg
|
|
||||||
{"A": "arg", "-v": true, "-q": false}
|
|
||||||
|
|
||||||
$ prog -q arg
|
|
||||||
{"A": "arg", "-v": false, "-q": true}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test single dash
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [-]"""
|
|
||||||
|
|
||||||
$ prog -
|
|
||||||
{"-": true}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If argument is repeated, its value should always be a list
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [NAME [NAME ...]]"""
|
|
||||||
|
|
||||||
$ prog a b
|
|
||||||
{"NAME": ["a", "b"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Option's argument defaults to null/None
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [options]
|
|
||||||
options:
|
|
||||||
-a Add
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a
|
|
||||||
{"-m": null, "-a": true}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test options without description
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog --hello"""
|
|
||||||
$ prog --hello
|
|
||||||
{"--hello": true}
|
|
||||||
|
|
||||||
r"""usage: prog [--hello=<world>]"""
|
|
||||||
$ prog
|
|
||||||
{"--hello": null}
|
|
||||||
|
|
||||||
$ prog --hello wrld
|
|
||||||
{"--hello": "wrld"}
|
|
||||||
|
|
||||||
r"""usage: prog [-o]"""
|
|
||||||
$ prog
|
|
||||||
{"-o": false}
|
|
||||||
|
|
||||||
$ prog -o
|
|
||||||
{"-o": true}
|
|
||||||
|
|
||||||
r"""usage: prog [-opr]"""
|
|
||||||
$ prog -op
|
|
||||||
{"-o": true, "-p": true, "-r": false}
|
|
||||||
|
|
||||||
r"""usage: git [-v | --verbose]"""
|
|
||||||
$ prog -v
|
|
||||||
{"-v": true, "--verbose": false}
|
|
||||||
|
|
||||||
r"""usage: git remote [-v | --verbose]"""
|
|
||||||
$ prog remote -v
|
|
||||||
{"remote": true, "-v": true, "--verbose": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test empty usage pattern
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog"""
|
|
||||||
$ prog
|
|
||||||
{}
|
|
||||||
|
|
||||||
r"""usage: prog
|
|
||||||
prog <a> <b>
|
|
||||||
"""
|
|
||||||
$ prog 1 2
|
|
||||||
{"<a>": "1", "<b>": "2"}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<a>": null, "<b>": null}
|
|
||||||
|
|
||||||
r"""usage: prog <a> <b>
|
|
||||||
prog
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"<a>": null, "<b>": null}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Option's argument should not capture default value from usage pattern
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [--file=<f>]"""
|
|
||||||
$ prog
|
|
||||||
{"--file": null}
|
|
||||||
|
|
||||||
r"""usage: prog [--file=<f>]
|
|
||||||
|
|
||||||
options: --file <a>
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--file": null}
|
|
||||||
|
|
||||||
r"""Usage: prog [-a <host:port>]
|
|
||||||
|
|
||||||
Options: -a, --address <host:port> TCP address [default: localhost:6283].
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--address": "localhost:6283"}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If option with argument could be repeated,
|
|
||||||
# its arguments should be accumulated into a list
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog --long=<arg> ..."""
|
|
||||||
|
|
||||||
$ prog --long one
|
|
||||||
{"--long": ["one"]}
|
|
||||||
|
|
||||||
$ prog --long one --long two
|
|
||||||
{"--long": ["one", "two"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test multiple elements repeated at once
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog (go <direction> --speed=<km/h>)..."""
|
|
||||||
$ prog go left --speed=5 go right --speed=9
|
|
||||||
{"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Required options should work with option shortcut
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [options] -a
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If option could be repeated its defaults should be split into a list
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [-o <o>]...
|
|
||||||
|
|
||||||
options: -o <o> [default: x]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -o this -o that
|
|
||||||
{"-o": ["this", "that"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-o": ["x"]}
|
|
||||||
|
|
||||||
r"""usage: prog [-o <o>]...
|
|
||||||
|
|
||||||
options: -o <o> [default: x y]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -o this
|
|
||||||
{"-o": ["this"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-o": ["x", "y"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test stacked option's argument
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog -pPATH
|
|
||||||
|
|
||||||
options: -p PATH
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -pHOME
|
|
||||||
{"-p": "HOME"}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Issue 56: Repeated mutually exclusive args give nested lists sometimes
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: foo (--xx=x|--yy=y)..."""
|
|
||||||
$ prog --xx=1 --yy=2
|
|
||||||
{"--xx": ["1"], "--yy": ["2"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# POSIXly correct tokenization
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [<input file>]"""
|
|
||||||
$ prog f.txt
|
|
||||||
{"<input file>": "f.txt"}
|
|
||||||
|
|
||||||
r"""usage: prog [--input=<file name>]..."""
|
|
||||||
$ prog --input a.txt --input=b.txt
|
|
||||||
{"--input": ["a.txt", "b.txt"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Issue 85: `[options]` shourtcut with multiple subcommands
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog good [options]
|
|
||||||
prog fail [options]
|
|
||||||
|
|
||||||
options: --loglevel=N
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog fail --loglevel 5
|
|
||||||
{"--loglevel": "5", "fail": true, "good": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Usage-section syntax
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage:prog --foo"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true}
|
|
||||||
|
|
||||||
r"""PROGRAM USAGE: prog --foo"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true}
|
|
||||||
|
|
||||||
r"""Usage: prog --foo
|
|
||||||
prog --bar
|
|
||||||
NOT PART OF SECTION"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true, "--bar": false}
|
|
||||||
|
|
||||||
r"""Usage:
|
|
||||||
prog --foo
|
|
||||||
prog --bar
|
|
||||||
|
|
||||||
NOT PART OF SECTION"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true, "--bar": false}
|
|
||||||
|
|
||||||
r"""Usage:
|
|
||||||
prog --foo
|
|
||||||
prog --bar
|
|
||||||
NOT PART OF SECTION"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true, "--bar": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Options-section syntax
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
global options: --foo
|
|
||||||
local options: --baz
|
|
||||||
--bar
|
|
||||||
other options:
|
|
||||||
--egg
|
|
||||||
--spam
|
|
||||||
-not-an-option-
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --baz --egg
|
|
||||||
{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false}
|
|
||||||
1
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
1
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
@@ -8,7 +8,6 @@ matrix:
|
|||||||
- go: 1.6
|
- go: 1.6
|
||||||
- go: 1.7
|
- go: 1.7
|
||||||
- go: 1.8
|
- go: 1.8
|
||||||
- go: 1.9
|
|
||||||
- go: tip
|
- go: tip
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: tip
|
||||||
|
|||||||
140
vendor/github.com/gorilla/websocket/client.go
generated
vendored
140
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@@ -5,8 +5,10 @@
|
|||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -86,6 +88,50 @@ type Dialer struct {
|
|||||||
|
|
||||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||||
|
|
||||||
|
// parseURL parses the URL.
|
||||||
|
//
|
||||||
|
// This function is a replacement for the standard library url.Parse function.
|
||||||
|
// In Go 1.4 and earlier, url.Parse loses information from the path.
|
||||||
|
func parseURL(s string) (*url.URL, error) {
|
||||||
|
// From the RFC:
|
||||||
|
//
|
||||||
|
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
|
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
|
var u url.URL
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(s, "ws://"):
|
||||||
|
u.Scheme = "ws"
|
||||||
|
s = s[len("ws://"):]
|
||||||
|
case strings.HasPrefix(s, "wss://"):
|
||||||
|
u.Scheme = "wss"
|
||||||
|
s = s[len("wss://"):]
|
||||||
|
default:
|
||||||
|
return nil, errMalformedURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := strings.Index(s, "?"); i >= 0 {
|
||||||
|
u.RawQuery = s[i+1:]
|
||||||
|
s = s[:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := strings.Index(s, "/"); i >= 0 {
|
||||||
|
u.Opaque = s[i:]
|
||||||
|
s = s[:i]
|
||||||
|
} else {
|
||||||
|
u.Opaque = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Host = s
|
||||||
|
|
||||||
|
if strings.Contains(u.Host, "@") {
|
||||||
|
// Don't bother parsing user information because user information is
|
||||||
|
// not allowed in websocket URIs.
|
||||||
|
return nil, errMalformedURL
|
||||||
|
}
|
||||||
|
|
||||||
|
return &u, nil
|
||||||
|
}
|
||||||
|
|
||||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||||
hostPort = u.Host
|
hostPort = u.Host
|
||||||
hostNoPort = u.Host
|
hostNoPort = u.Host
|
||||||
@@ -104,7 +150,7 @@ func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
|||||||
return hostPort, hostNoPort
|
return hostPort, hostNoPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultDialer is a dialer with all fields set to the default values.
|
// DefaultDialer is a dialer with all fields set to the default zero values.
|
||||||
var DefaultDialer = &Dialer{
|
var DefaultDialer = &Dialer{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
}
|
}
|
||||||
@@ -131,7 +177,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse(urlStr)
|
u, err := parseURL(urlStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -200,52 +246,36 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostPort, hostNoPort := hostPortNoPort(u)
|
||||||
|
|
||||||
|
var proxyURL *url.URL
|
||||||
|
// Check wether the proxy method has been configured
|
||||||
|
if d.Proxy != nil {
|
||||||
|
proxyURL, err = d.Proxy(req)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetHostPort string
|
||||||
|
if proxyURL != nil {
|
||||||
|
targetHostPort, _ = hostPortNoPort(proxyURL)
|
||||||
|
} else {
|
||||||
|
targetHostPort = hostPort
|
||||||
|
}
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d.HandshakeTimeout != 0 {
|
if d.HandshakeTimeout != 0 {
|
||||||
deadline = time.Now().Add(d.HandshakeTimeout)
|
deadline = time.Now().Add(d.HandshakeTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get network dial function.
|
|
||||||
netDial := d.NetDial
|
netDial := d.NetDial
|
||||||
if netDial == nil {
|
if netDial == nil {
|
||||||
netDialer := &net.Dialer{Deadline: deadline}
|
netDialer := &net.Dialer{Deadline: deadline}
|
||||||
netDial = netDialer.Dial
|
netDial = netDialer.Dial
|
||||||
}
|
}
|
||||||
|
|
||||||
// If needed, wrap the dial function to set the connection deadline.
|
netConn, err := netDial("tcp", targetHostPort)
|
||||||
if !deadline.Equal(time.Time{}) {
|
|
||||||
forwardDial := netDial
|
|
||||||
netDial = func(network, addr string) (net.Conn, error) {
|
|
||||||
c, err := forwardDial(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = c.SetDeadline(deadline)
|
|
||||||
if err != nil {
|
|
||||||
c.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If needed, wrap the dial function to connect through a proxy.
|
|
||||||
if d.Proxy != nil {
|
|
||||||
proxyURL, err := d.Proxy(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if proxyURL != nil {
|
|
||||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
netDial = dialer.Dial
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hostPort, hostNoPort := hostPortNoPort(u)
|
|
||||||
netConn, err := netDial("tcp", hostPort)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -256,6 +286,42 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := netConn.SetDeadline(deadline); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxyURL != nil {
|
||||||
|
connectHeader := make(http.Header)
|
||||||
|
if user := proxyURL.User; user != nil {
|
||||||
|
proxyUser := user.Username()
|
||||||
|
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
||||||
|
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
||||||
|
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectReq := &http.Request{
|
||||||
|
Method: "CONNECT",
|
||||||
|
URL: &url.URL{Opaque: hostPort},
|
||||||
|
Host: hostPort,
|
||||||
|
Header: connectHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
connectReq.Write(netConn)
|
||||||
|
|
||||||
|
// Read response.
|
||||||
|
// Okay to use and discard buffered reader here, because
|
||||||
|
// TLS server will not speak until spoken to.
|
||||||
|
br := bufio.NewReader(netConn)
|
||||||
|
resp, err := http.ReadResponse(br, connectReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
f := strings.SplitN(resp.Status, " ", 2)
|
||||||
|
return nil, nil, errors.New(f[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if u.Scheme == "https" {
|
if u.Scheme == "https" {
|
||||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||||
if cfg.ServerName == "" {
|
if cfg.ServerName == "" {
|
||||||
|
|||||||
124
vendor/github.com/gorilla/websocket/client_server_test.go
generated
vendored
124
vendor/github.com/gorilla/websocket/client_server_test.go
generated
vendored
@@ -5,14 +5,11 @@
|
|||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -34,10 +31,9 @@ var cstUpgrader = Upgrader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cstDialer = Dialer{
|
var cstDialer = Dialer{
|
||||||
Subprotocols: []string{"p1", "p2"},
|
Subprotocols: []string{"p1", "p2"},
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
HandshakeTimeout: 30 * time.Second,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cstHandler struct{ *testing.T }
|
type cstHandler struct{ *testing.T }
|
||||||
@@ -147,9 +143,8 @@ func TestProxyDial(t *testing.T) {
|
|||||||
s := newServer(t)
|
s := newServer(t)
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
surl, _ := url.Parse(s.Server.URL)
|
surl, _ := url.Parse(s.URL)
|
||||||
|
|
||||||
cstDialer := cstDialer // make local copy for modification on next line.
|
|
||||||
cstDialer.Proxy = http.ProxyURL(surl)
|
cstDialer.Proxy = http.ProxyURL(surl)
|
||||||
|
|
||||||
connect := false
|
connect := false
|
||||||
@@ -165,8 +160,8 @@ func TestProxyDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !connect {
|
if !connect {
|
||||||
t.Log("connect not received")
|
t.Log("connect not recieved")
|
||||||
http.Error(w, "connect not received", 405)
|
http.Error(w, "connect not recieved", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
origHandler.ServeHTTP(w, r)
|
origHandler.ServeHTTP(w, r)
|
||||||
@@ -178,16 +173,16 @@ func TestProxyDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
sendRecv(t, ws)
|
sendRecv(t, ws)
|
||||||
|
|
||||||
|
cstDialer.Proxy = http.ProxyFromEnvironment
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyAuthorizationDial(t *testing.T) {
|
func TestProxyAuthorizationDial(t *testing.T) {
|
||||||
s := newServer(t)
|
s := newServer(t)
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
surl, _ := url.Parse(s.Server.URL)
|
surl, _ := url.Parse(s.URL)
|
||||||
surl.User = url.UserPassword("username", "password")
|
surl.User = url.UserPassword("username", "password")
|
||||||
|
|
||||||
cstDialer := cstDialer // make local copy for modification on next line.
|
|
||||||
cstDialer.Proxy = http.ProxyURL(surl)
|
cstDialer.Proxy = http.ProxyURL(surl)
|
||||||
|
|
||||||
connect := false
|
connect := false
|
||||||
@@ -205,8 +200,8 @@ func TestProxyAuthorizationDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !connect {
|
if !connect {
|
||||||
t.Log("connect with proxy authorization not received")
|
t.Log("connect with proxy authorization not recieved")
|
||||||
http.Error(w, "connect with proxy authorization not received", 405)
|
http.Error(w, "connect with proxy authorization not recieved", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
origHandler.ServeHTTP(w, r)
|
origHandler.ServeHTTP(w, r)
|
||||||
@@ -218,6 +213,8 @@ func TestProxyAuthorizationDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
sendRecv(t, ws)
|
sendRecv(t, ws)
|
||||||
|
|
||||||
|
cstDialer.Proxy = http.ProxyFromEnvironment
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDial(t *testing.T) {
|
func TestDial(t *testing.T) {
|
||||||
@@ -240,7 +237,7 @@ func TestDialCookieJar(t *testing.T) {
|
|||||||
d := cstDialer
|
d := cstDialer
|
||||||
d.Jar = jar
|
d.Jar = jar
|
||||||
|
|
||||||
u, _ := url.Parse(s.URL)
|
u, _ := parseURL(s.URL)
|
||||||
|
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "ws":
|
case "ws":
|
||||||
@@ -249,7 +246,7 @@ func TestDialCookieJar(t *testing.T) {
|
|||||||
u.Scheme = "https"
|
u.Scheme = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
cookies := []*http.Cookie{{Name: "gorilla", Value: "ws", Path: "/"}}
|
cookies := []*http.Cookie{&http.Cookie{Name: "gorilla", Value: "ws", Path: "/"}}
|
||||||
d.Jar.SetCookies(u, cookies)
|
d.Jar.SetCookies(u, cookies)
|
||||||
|
|
||||||
ws, _, err := d.Dial(s.URL, nil)
|
ws, _, err := d.Dial(s.URL, nil)
|
||||||
@@ -401,17 +398,9 @@ func TestBadMethod(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", s.URL, strings.NewReader(""))
|
resp, err := http.PostForm(s.URL, url.Values{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewRequest returned error %v", err)
|
t.Fatalf("PostForm returned error %v", err)
|
||||||
}
|
|
||||||
req.Header.Set("Connection", "upgrade")
|
|
||||||
req.Header.Set("Upgrade", "websocket")
|
|
||||||
req.Header.Set("Sec-Websocket-Version", "13")
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Do returned error %v", err)
|
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusMethodNotAllowed {
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
@@ -521,82 +510,3 @@ func TestDialCompression(t *testing.T) {
|
|||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
sendRecv(t, ws)
|
sendRecv(t, ws)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSocksProxyDial(t *testing.T) {
|
|
||||||
s := newServer(t)
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
proxyListener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("listen failed: %v", err)
|
|
||||||
}
|
|
||||||
defer proxyListener.Close()
|
|
||||||
go func() {
|
|
||||||
c1, err := proxyListener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("proxy accept failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c1.Close()
|
|
||||||
|
|
||||||
c1.SetDeadline(time.Now().Add(30 * time.Second))
|
|
||||||
|
|
||||||
buf := make([]byte, 32)
|
|
||||||
if _, err := io.ReadFull(c1, buf[:3]); err != nil {
|
|
||||||
t.Errorf("read failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if want := []byte{5, 1, 0}; !bytes.Equal(want, buf[:len(want)]) {
|
|
||||||
t.Errorf("read %x, want %x", buf[:len(want)], want)
|
|
||||||
}
|
|
||||||
if _, err := c1.Write([]byte{5, 0}); err != nil {
|
|
||||||
t.Errorf("write failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(c1, buf[:10]); err != nil {
|
|
||||||
t.Errorf("read failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if want := []byte{5, 1, 0, 1}; !bytes.Equal(want, buf[:len(want)]) {
|
|
||||||
t.Errorf("read %x, want %x", buf[:len(want)], want)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf[1] = 0
|
|
||||||
if _, err := c1.Write(buf[:10]); err != nil {
|
|
||||||
t.Errorf("write failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.IP(buf[4:8])
|
|
||||||
port := binary.BigEndian.Uint16(buf[8:10])
|
|
||||||
|
|
||||||
c2, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: int(port)})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("dial failed; %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c2.Close()
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
io.Copy(c1, c2)
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
io.Copy(c2, c1)
|
|
||||||
<-done
|
|
||||||
}()
|
|
||||||
|
|
||||||
purl, err := url.Parse("socks5://" + proxyListener.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("parse failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cstDialer := cstDialer // make local copy for modification on next line.
|
|
||||||
cstDialer.Proxy = http.ProxyURL(purl)
|
|
||||||
|
|
||||||
ws, _, err := cstDialer.Dial(s.URL, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Dial: %v", err)
|
|
||||||
}
|
|
||||||
defer ws.Close()
|
|
||||||
sendRecv(t, ws)
|
|
||||||
}
|
|
||||||
|
|||||||
40
vendor/github.com/gorilla/websocket/client_test.go
generated
vendored
40
vendor/github.com/gorilla/websocket/client_test.go
generated
vendored
@@ -6,9 +6,49 @@ package websocket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var parseURLTests = []struct {
|
||||||
|
s string
|
||||||
|
u *url.URL
|
||||||
|
rui string
|
||||||
|
}{
|
||||||
|
{"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"},
|
||||||
|
{"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"},
|
||||||
|
{"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}, "/"},
|
||||||
|
{"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}, "/"},
|
||||||
|
{"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}, "/a/b"},
|
||||||
|
{"ss://example.com/a/b", nil, ""},
|
||||||
|
{"ws://webmaster@example.com/", nil, ""},
|
||||||
|
{"wss://example.com/a/b?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b", RawQuery: "x=y"}, "/a/b?x=y"},
|
||||||
|
{"wss://example.com?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/", RawQuery: "x=y"}, "/?x=y"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseURL(t *testing.T) {
|
||||||
|
for _, tt := range parseURLTests {
|
||||||
|
u, err := parseURL(tt.s)
|
||||||
|
if tt.u != nil && err != nil {
|
||||||
|
t.Errorf("parseURL(%q) returned error %v", tt.s, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tt.u == nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("parseURL(%q) did not return error", tt.s)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(u, tt.u) {
|
||||||
|
t.Errorf("parseURL(%q) = %v, want %v", tt.s, u, tt.u)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u.RequestURI() != tt.rui {
|
||||||
|
t.Errorf("parseURL(%q).RequestURI() = %v, want %v", tt.s, u.RequestURI(), tt.rui)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var hostPortNoPortTests = []struct {
|
var hostPortNoPortTests = []struct {
|
||||||
u *url.URL
|
u *url.URL
|
||||||
hostPort, hostNoPort string
|
hostPort, hostNoPort string
|
||||||
|
|||||||
44
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
44
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@@ -76,7 +76,7 @@ const (
|
|||||||
// is UTF-8 encoded text.
|
// is UTF-8 encoded text.
|
||||||
PingMessage = 9
|
PingMessage = 9
|
||||||
|
|
||||||
// PongMessage denotes a pong control message. The optional message payload
|
// PongMessage denotes a ping control message. The optional message payload
|
||||||
// is UTF-8 encoded text.
|
// is UTF-8 encoded text.
|
||||||
PongMessage = 10
|
PongMessage = 10
|
||||||
)
|
)
|
||||||
@@ -100,8 +100,9 @@ func (e *netError) Error() string { return e.msg }
|
|||||||
func (e *netError) Temporary() bool { return e.temporary }
|
func (e *netError) Temporary() bool { return e.temporary }
|
||||||
func (e *netError) Timeout() bool { return e.timeout }
|
func (e *netError) Timeout() bool { return e.timeout }
|
||||||
|
|
||||||
// CloseError represents a close message.
|
// CloseError represents close frame.
|
||||||
type CloseError struct {
|
type CloseError struct {
|
||||||
|
|
||||||
// Code is defined in RFC 6455, section 11.7.
|
// Code is defined in RFC 6455, section 11.7.
|
||||||
Code int
|
Code int
|
||||||
|
|
||||||
@@ -342,8 +343,7 @@ func (c *Conn) Subprotocol() string {
|
|||||||
return c.subprotocol
|
return c.subprotocol
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the underlying network connection without sending or waiting
|
// Close closes the underlying network connection without sending or waiting for a close frame.
|
||||||
// for a close message.
|
|
||||||
func (c *Conn) Close() error {
|
func (c *Conn) Close() error {
|
||||||
return c.conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
@@ -484,9 +484,6 @@ func (c *Conn) prepWrite(messageType int) error {
|
|||||||
//
|
//
|
||||||
// There can be at most one open writer on a connection. NextWriter closes the
|
// There can be at most one open writer on a connection. NextWriter closes the
|
||||||
// previous writer if the application has not already done so.
|
// previous writer if the application has not already done so.
|
||||||
//
|
|
||||||
// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and
|
|
||||||
// PongMessage) are supported.
|
|
||||||
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
||||||
if err := c.prepWrite(messageType); err != nil {
|
if err := c.prepWrite(messageType); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -767,6 +764,7 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
|
|||||||
// Read methods
|
// Read methods
|
||||||
|
|
||||||
func (c *Conn) advanceFrame() (int, error) {
|
func (c *Conn) advanceFrame() (int, error) {
|
||||||
|
|
||||||
// 1. Skip remainder of previous frame.
|
// 1. Skip remainder of previous frame.
|
||||||
|
|
||||||
if c.readRemaining > 0 {
|
if c.readRemaining > 0 {
|
||||||
@@ -1035,7 +1033,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetReadLimit sets the maximum size for a message read from the peer. If a
|
// SetReadLimit sets the maximum size for a message read from the peer. If a
|
||||||
// message exceeds the limit, the connection sends a close message to the peer
|
// message exceeds the limit, the connection sends a close frame to the peer
|
||||||
// and returns ErrReadLimit to the application.
|
// and returns ErrReadLimit to the application.
|
||||||
func (c *Conn) SetReadLimit(limit int64) {
|
func (c *Conn) SetReadLimit(limit int64) {
|
||||||
c.readLimit = limit
|
c.readLimit = limit
|
||||||
@@ -1048,21 +1046,24 @@ func (c *Conn) CloseHandler() func(code int, text string) error {
|
|||||||
|
|
||||||
// SetCloseHandler sets the handler for close messages received from the peer.
|
// SetCloseHandler sets the handler for close messages received from the peer.
|
||||||
// The code argument to h is the received close code or CloseNoStatusReceived
|
// The code argument to h is the received close code or CloseNoStatusReceived
|
||||||
// if the close message is empty. The default close handler sends a close
|
// if the close message is empty. The default close handler sends a close frame
|
||||||
// message back to the peer.
|
// back to the peer.
|
||||||
//
|
//
|
||||||
// The application must read the connection to process close messages as
|
// The application must read the connection to process close messages as
|
||||||
// described in the section on Control Messages above.
|
// described in the section on Control Frames above.
|
||||||
//
|
//
|
||||||
// The connection read methods return a CloseError when a close message is
|
// The connection read methods return a CloseError when a close frame is
|
||||||
// received. Most applications should handle close messages as part of their
|
// received. Most applications should handle close messages as part of their
|
||||||
// normal error handling. Applications should only set a close handler when the
|
// normal error handling. Applications should only set a close handler when the
|
||||||
// application must perform some action before sending a close message back to
|
// application must perform some action before sending a close frame back to
|
||||||
// the peer.
|
// the peer.
|
||||||
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(code int, text string) error {
|
h = func(code int, text string) error {
|
||||||
message := FormatCloseMessage(code, "")
|
message := []byte{}
|
||||||
|
if code != CloseNoStatusReceived {
|
||||||
|
message = FormatCloseMessage(code, "")
|
||||||
|
}
|
||||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1076,11 +1077,11 @@ func (c *Conn) PingHandler() func(appData string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetPingHandler sets the handler for ping messages received from the peer.
|
// SetPingHandler sets the handler for ping messages received from the peer.
|
||||||
// The appData argument to h is the PING message application data. The default
|
// The appData argument to h is the PING frame application data. The default
|
||||||
// ping handler sends a pong to the peer.
|
// ping handler sends a pong to the peer.
|
||||||
//
|
//
|
||||||
// The application must read the connection to process ping messages as
|
// The application must read the connection to process ping messages as
|
||||||
// described in the section on Control Messages above.
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(message string) error {
|
h = func(message string) error {
|
||||||
@@ -1102,11 +1103,11 @@ func (c *Conn) PongHandler() func(appData string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetPongHandler sets the handler for pong messages received from the peer.
|
// SetPongHandler sets the handler for pong messages received from the peer.
|
||||||
// The appData argument to h is the PONG message application data. The default
|
// The appData argument to h is the PONG frame application data. The default
|
||||||
// pong handler does nothing.
|
// pong handler does nothing.
|
||||||
//
|
//
|
||||||
// The application must read the connection to process ping messages as
|
// The application must read the connection to process ping messages as
|
||||||
// described in the section on Control Messages above.
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(string) error { return nil }
|
h = func(string) error { return nil }
|
||||||
@@ -1140,14 +1141,7 @@ func (c *Conn) SetCompressionLevel(level int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
||||||
// An empty message is returned for code CloseNoStatusReceived.
|
|
||||||
func FormatCloseMessage(closeCode int, text string) []byte {
|
func FormatCloseMessage(closeCode int, text string) []byte {
|
||||||
if closeCode == CloseNoStatusReceived {
|
|
||||||
// Return empty message because it's illegal to send
|
|
||||||
// CloseNoStatusReceived. Return non-nil value in case application
|
|
||||||
// checks for nil.
|
|
||||||
return []byte{}
|
|
||||||
}
|
|
||||||
buf := make([]byte, 2+len(text))
|
buf := make([]byte, 2+len(text))
|
||||||
binary.BigEndian.PutUint16(buf, uint16(closeCode))
|
binary.BigEndian.PutUint16(buf, uint16(closeCode))
|
||||||
copy(buf[2:], text)
|
copy(buf[2:], text)
|
||||||
|
|||||||
3
vendor/github.com/gorilla/websocket/conn_test.go
generated
vendored
3
vendor/github.com/gorilla/websocket/conn_test.go
generated
vendored
@@ -341,6 +341,7 @@ func TestUnderlyingConn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBufioReadBytes(t *testing.T) {
|
func TestBufioReadBytes(t *testing.T) {
|
||||||
|
|
||||||
// Test calling bufio.ReadBytes for value longer than read buffer size.
|
// Test calling bufio.ReadBytes for value longer than read buffer size.
|
||||||
|
|
||||||
m := make([]byte, 512)
|
m := make([]byte, 512)
|
||||||
@@ -365,7 +366,7 @@ func TestBufioReadBytes(t *testing.T) {
|
|||||||
t.Fatalf("ReadBytes() returned %v", err)
|
t.Fatalf("ReadBytes() returned %v", err)
|
||||||
}
|
}
|
||||||
if len(p) != len(m) {
|
if len(p) != len(m) {
|
||||||
t.Fatalf("read returned %d bytes, want %d bytes", len(p), len(m))
|
t.Fatalf("read returnd %d bytes, want %d bytes", len(p), len(m))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
45
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
45
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
@@ -6,8 +6,9 @@
|
|||||||
//
|
//
|
||||||
// Overview
|
// Overview
|
||||||
//
|
//
|
||||||
// The Conn type represents a WebSocket connection. A server application calls
|
// The Conn type represents a WebSocket connection. A server application uses
|
||||||
// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
|
// the Upgrade function from an Upgrader object with a HTTP request handler
|
||||||
|
// to get a pointer to a Conn:
|
||||||
//
|
//
|
||||||
// var upgrader = websocket.Upgrader{
|
// var upgrader = websocket.Upgrader{
|
||||||
// ReadBufferSize: 1024,
|
// ReadBufferSize: 1024,
|
||||||
@@ -30,12 +31,10 @@
|
|||||||
// for {
|
// for {
|
||||||
// messageType, p, err := conn.ReadMessage()
|
// messageType, p, err := conn.ReadMessage()
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// log.Println(err)
|
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
// if err := conn.WriteMessage(messageType, p); err != nil {
|
// if err = conn.WriteMessage(messageType, p); err != nil {
|
||||||
// log.Println(err)
|
// return err
|
||||||
// return
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -86,26 +85,20 @@
|
|||||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
||||||
// methods to send a control message to the peer.
|
// methods to send a control message to the peer.
|
||||||
//
|
//
|
||||||
// Connections handle received close messages by calling the handler function
|
// Connections handle received close messages by sending a close message to the
|
||||||
// set with the SetCloseHandler method and by returning a *CloseError from the
|
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
|
||||||
// NextReader, ReadMessage or the message Read method. The default close
|
// message Read method.
|
||||||
// handler sends a close message to the peer.
|
|
||||||
//
|
//
|
||||||
// Connections handle received ping messages by calling the handler function
|
// Connections handle received ping and pong messages by invoking callback
|
||||||
// set with the SetPingHandler method. The default ping handler sends a pong
|
// functions set with SetPingHandler and SetPongHandler methods. The callback
|
||||||
// message to the peer.
|
// functions are called from the NextReader, ReadMessage and the message Read
|
||||||
|
// methods.
|
||||||
//
|
//
|
||||||
// Connections handle received pong messages by calling the handler function
|
// The default ping handler sends a pong to the peer. The application's reading
|
||||||
// set with the SetPongHandler method. The default pong handler does nothing.
|
// goroutine can block for a short time while the handler writes the pong data
|
||||||
// If an application sends ping messages, then the application should set a
|
// to the connection.
|
||||||
// pong handler to receive the corresponding pong.
|
|
||||||
//
|
//
|
||||||
// The control message handler functions are called from the NextReader,
|
// The application must read the connection to process ping, pong and close
|
||||||
// ReadMessage and message reader Read methods. The default close and ping
|
|
||||||
// handlers can block these methods for a short time when the handler writes to
|
|
||||||
// the connection.
|
|
||||||
//
|
|
||||||
// The application must read the connection to process close, ping and pong
|
|
||||||
// messages sent from the peer. If the application is not otherwise interested
|
// messages sent from the peer. If the application is not otherwise interested
|
||||||
// in messages from the peer, then the application should start a goroutine to
|
// in messages from the peer, then the application should start a goroutine to
|
||||||
// read and discard messages from the peer. A simple example is:
|
// read and discard messages from the peer. A simple example is:
|
||||||
@@ -154,9 +147,9 @@
|
|||||||
// CheckOrigin: func(r *http.Request) bool { return true },
|
// CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The deprecated package-level Upgrade function does not perform origin
|
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||||
// checking. The application is responsible for checking the Origin header
|
// application's responsibility to check the Origin header before calling
|
||||||
// before calling the Upgrade function.
|
// Upgrade.
|
||||||
//
|
//
|
||||||
// Compression EXPERIMENTAL
|
// Compression EXPERIMENTAL
|
||||||
//
|
//
|
||||||
|
|||||||
2
vendor/github.com/gorilla/websocket/examples/chat/README.md
generated
vendored
2
vendor/github.com/gorilla/websocket/examples/chat/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Chat Example
|
# Chat Example
|
||||||
|
|
||||||
This application shows how to use the
|
This application shows how to use use the
|
||||||
[websocket](https://github.com/gorilla/websocket) package to implement a simple
|
[websocket](https://github.com/gorilla/websocket) package to implement a simple
|
||||||
web chat application.
|
web chat application.
|
||||||
|
|
||||||
|
|||||||
4
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
4
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
@@ -64,7 +64,7 @@ func (c *Client) readPump() {
|
|||||||
for {
|
for {
|
||||||
_, message, err := c.conn.ReadMessage()
|
_, message, err := c.conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||||
log.Printf("error: %v", err)
|
log.Printf("error: %v", err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -113,7 +113,7 @@ func (c *Client) writePump() {
|
|||||||
}
|
}
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/gorilla/websocket/examples/echo/server.go
generated
vendored
1
vendor/github.com/gorilla/websocket/examples/echo/server.go
generated
vendored
@@ -55,7 +55,6 @@ func main() {
|
|||||||
|
|
||||||
var homeTemplate = template.Must(template.New("").Parse(`
|
var homeTemplate = template.Must(template.New("").Parse(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
11
vendor/github.com/gorilla/websocket/json.go
generated
vendored
11
vendor/github.com/gorilla/websocket/json.go
generated
vendored
@@ -9,14 +9,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteJSON writes the JSON encoding of v as a message.
|
// WriteJSON is deprecated, use c.WriteJSON instead.
|
||||||
//
|
|
||||||
// Deprecated: Use c.WriteJSON instead.
|
|
||||||
func WriteJSON(c *Conn, v interface{}) error {
|
func WriteJSON(c *Conn, v interface{}) error {
|
||||||
return c.WriteJSON(v)
|
return c.WriteJSON(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteJSON writes the JSON encoding of v as a message.
|
// WriteJSON writes the JSON encoding of v to the connection.
|
||||||
//
|
//
|
||||||
// See the documentation for encoding/json Marshal for details about the
|
// See the documentation for encoding/json Marshal for details about the
|
||||||
// conversion of Go values to JSON.
|
// conversion of Go values to JSON.
|
||||||
@@ -33,10 +31,7 @@ func (c *Conn) WriteJSON(v interface{}) error {
|
|||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
// ReadJSON is deprecated, use c.ReadJSON instead.
|
||||||
// it in the value pointed to by v.
|
|
||||||
//
|
|
||||||
// Deprecated: Use c.ReadJSON instead.
|
|
||||||
func ReadJSON(c *Conn, v interface{}) error {
|
func ReadJSON(c *Conn, v interface{}) error {
|
||||||
return c.ReadJSON(v)
|
return c.ReadJSON(v)
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
1
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
@@ -11,6 +11,7 @@ import "unsafe"
|
|||||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
|
||||||
// Mask one byte at a time for small buffers.
|
// Mask one byte at a time for small buffers.
|
||||||
if len(b) < 2*wordSize {
|
if len(b) < 2*wordSize {
|
||||||
for i := range b {
|
for i := range b {
|
||||||
|
|||||||
77
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
77
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
@@ -1,77 +0,0 @@
|
|||||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type netDialerFunc func(netowrk, addr string) (net.Conn, error)
|
|
||||||
|
|
||||||
func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
return fn(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpProxyDialer struct {
|
|
||||||
proxyURL *url.URL
|
|
||||||
fowardDial func(network, addr string) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
|
|
||||||
hostPort, _ := hostPortNoPort(hpd.proxyURL)
|
|
||||||
conn, err := hpd.fowardDial(network, hostPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
connectHeader := make(http.Header)
|
|
||||||
if user := hpd.proxyURL.User; user != nil {
|
|
||||||
proxyUser := user.Username()
|
|
||||||
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
|
||||||
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
|
||||||
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectReq := &http.Request{
|
|
||||||
Method: "CONNECT",
|
|
||||||
URL: &url.URL{Opaque: addr},
|
|
||||||
Host: addr,
|
|
||||||
Header: connectHeader,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := connectReq.Write(conn); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read response. It's OK to use and discard buffered reader here becaue
|
|
||||||
// the remote server does not speak until spoken to.
|
|
||||||
br := bufio.NewReader(conn)
|
|
||||||
resp, err := http.ReadResponse(br, connectReq)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
conn.Close()
|
|
||||||
f := strings.SplitN(resp.Status, " ", 2)
|
|
||||||
return nil, errors.New(f[1])
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
37
vendor/github.com/gorilla/websocket/server.go
generated
vendored
37
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@@ -76,7 +76,7 @@ func checkSameOrigin(r *http.Request) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return equalASCIIFold(u.Host, r.Host)
|
return u.Host == r.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
||||||
@@ -104,28 +104,26 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
|
|||||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
||||||
// response.
|
// response.
|
||||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||||
const badHandshake = "websocket: the client is not using the websocket protocol: "
|
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method != "GET" {
|
if r.Method != "GET" {
|
||||||
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
|
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||||
}
|
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||||
|
}
|
||||||
|
|
||||||
checkOrigin := u.CheckOrigin
|
checkOrigin := u.CheckOrigin
|
||||||
if checkOrigin == nil {
|
if checkOrigin == nil {
|
||||||
checkOrigin = checkSameOrigin
|
checkOrigin = checkSameOrigin
|
||||||
@@ -232,11 +230,10 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
|
|
||||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||||
//
|
//
|
||||||
// Deprecated: Use websocket.Upgrader instead.
|
// This function is deprecated, use websocket.Upgrader instead.
|
||||||
//
|
//
|
||||||
// Upgrade does not perform origin checking. The application is responsible for
|
// The application is responsible for checking the request origin before
|
||||||
// checking the Origin header before calling Upgrade. An example implementation
|
// calling Upgrade. An example implementation of the same origin policy is:
|
||||||
// of the same origin policy check is:
|
|
||||||
//
|
//
|
||||||
// if req.Header.Get("Origin") != "http://"+req.Host {
|
// if req.Header.Get("Origin") != "http://"+req.Host {
|
||||||
// http.Error(w, "Origin not allowed", 403)
|
// http.Error(w, "Origin not allowed", 403)
|
||||||
|
|||||||
18
vendor/github.com/gorilla/websocket/server_test.go
generated
vendored
18
vendor/github.com/gorilla/websocket/server_test.go
generated
vendored
@@ -49,21 +49,3 @@ func TestIsWebSocketUpgrade(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkSameOriginTests = []struct {
|
|
||||||
ok bool
|
|
||||||
r *http.Request
|
|
||||||
}{
|
|
||||||
{false, &http.Request{Host: "example.org", Header: map[string][]string{"Origin": []string{"https://other.org"}}}},
|
|
||||||
{true, &http.Request{Host: "example.org", Header: map[string][]string{"Origin": []string{"https://example.org"}}}},
|
|
||||||
{true, &http.Request{Host: "Example.org", Header: map[string][]string{"Origin": []string{"https://example.org"}}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckSameOrigin(t *testing.T) {
|
|
||||||
for _, tt := range checkSameOriginTests {
|
|
||||||
ok := checkSameOrigin(tt.r)
|
|
||||||
if tt.ok != ok {
|
|
||||||
t.Errorf("checkSameOrigin(%+v) returned %v, want %v", tt.r, ok, tt.ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
33
vendor/github.com/gorilla/websocket/util.go
generated
vendored
33
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@@ -11,7 +11,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
@@ -112,14 +111,14 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
|
|||||||
case escape:
|
case escape:
|
||||||
escape = false
|
escape = false
|
||||||
p[j] = b
|
p[j] = b
|
||||||
j++
|
j += 1
|
||||||
case b == '\\':
|
case b == '\\':
|
||||||
escape = true
|
escape = true
|
||||||
case b == '"':
|
case b == '"':
|
||||||
return string(p[:j]), s[i+1:]
|
return string(p[:j]), s[i+1:]
|
||||||
default:
|
default:
|
||||||
p[j] = b
|
p[j] = b
|
||||||
j++
|
j += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", ""
|
return "", ""
|
||||||
@@ -128,31 +127,8 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
|
|||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// equalASCIIFold returns true if s is equal to t with ASCII case folding.
|
|
||||||
func equalASCIIFold(s, t string) bool {
|
|
||||||
for s != "" && t != "" {
|
|
||||||
sr, size := utf8.DecodeRuneInString(s)
|
|
||||||
s = s[size:]
|
|
||||||
tr, size := utf8.DecodeRuneInString(t)
|
|
||||||
t = t[size:]
|
|
||||||
if sr == tr {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if 'A' <= sr && sr <= 'Z' {
|
|
||||||
sr = sr + 'a' - 'A'
|
|
||||||
}
|
|
||||||
if 'A' <= tr && tr <= 'Z' {
|
|
||||||
tr = tr + 'a' - 'A'
|
|
||||||
}
|
|
||||||
if sr != tr {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s == t
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokenListContainsValue returns true if the 1#token header with the given
|
// tokenListContainsValue returns true if the 1#token header with the given
|
||||||
// name contains a token equal to value with ASCII case folding.
|
// name contains token.
|
||||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||||
headers:
|
headers:
|
||||||
for _, s := range header[name] {
|
for _, s := range header[name] {
|
||||||
@@ -166,7 +142,7 @@ headers:
|
|||||||
if s != "" && s[0] != ',' {
|
if s != "" && s[0] != ',' {
|
||||||
continue headers
|
continue headers
|
||||||
}
|
}
|
||||||
if equalASCIIFold(t, value) {
|
if strings.EqualFold(t, value) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if s == "" {
|
if s == "" {
|
||||||
@@ -180,6 +156,7 @@ headers:
|
|||||||
|
|
||||||
// parseExtensiosn parses WebSocket extensions from a header.
|
// parseExtensiosn parses WebSocket extensions from a header.
|
||||||
func parseExtensions(header http.Header) []map[string]string {
|
func parseExtensions(header http.Header) []map[string]string {
|
||||||
|
|
||||||
// From RFC 6455:
|
// From RFC 6455:
|
||||||
//
|
//
|
||||||
// Sec-WebSocket-Extensions = extension-list
|
// Sec-WebSocket-Extensions = extension-list
|
||||||
|
|||||||
49
vendor/github.com/gorilla/websocket/util_test.go
generated
vendored
49
vendor/github.com/gorilla/websocket/util_test.go
generated
vendored
@@ -10,24 +10,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var equalASCIIFoldTests = []struct {
|
|
||||||
t, s string
|
|
||||||
eq bool
|
|
||||||
}{
|
|
||||||
{"WebSocket", "websocket", true},
|
|
||||||
{"websocket", "WebSocket", true},
|
|
||||||
{"Öyster", "öyster", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualASCIIFold(t *testing.T) {
|
|
||||||
for _, tt := range equalASCIIFoldTests {
|
|
||||||
eq := equalASCIIFold(tt.s, tt.t)
|
|
||||||
if eq != tt.eq {
|
|
||||||
t.Errorf("equalASCIIFold(%q, %q) = %v, want %v", tt.s, tt.t, eq, tt.eq)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenListContainsValueTests = []struct {
|
var tokenListContainsValueTests = []struct {
|
||||||
value string
|
value string
|
||||||
ok bool
|
ok bool
|
||||||
@@ -56,32 +38,29 @@ var parseExtensionTests = []struct {
|
|||||||
value string
|
value string
|
||||||
extensions []map[string]string
|
extensions []map[string]string
|
||||||
}{
|
}{
|
||||||
{`foo`, []map[string]string{{"": "foo"}}},
|
{`foo`, []map[string]string{map[string]string{"": "foo"}}},
|
||||||
{`foo, bar; baz=2`, []map[string]string{
|
{`foo, bar; baz=2`, []map[string]string{
|
||||||
{"": "foo"},
|
map[string]string{"": "foo"},
|
||||||
{"": "bar", "baz": "2"}}},
|
map[string]string{"": "bar", "baz": "2"}}},
|
||||||
{`foo; bar="b,a;z"`, []map[string]string{
|
{`foo; bar="b,a;z"`, []map[string]string{
|
||||||
{"": "foo", "bar": "b,a;z"}}},
|
map[string]string{"": "foo", "bar": "b,a;z"}}},
|
||||||
{`foo , bar; baz = 2`, []map[string]string{
|
{`foo , bar; baz = 2`, []map[string]string{
|
||||||
{"": "foo"},
|
map[string]string{"": "foo"},
|
||||||
{"": "bar", "baz": "2"}}},
|
map[string]string{"": "bar", "baz": "2"}}},
|
||||||
{`foo, bar; baz=2 junk`, []map[string]string{
|
{`foo, bar; baz=2 junk`, []map[string]string{
|
||||||
{"": "foo"}}},
|
map[string]string{"": "foo"}}},
|
||||||
{`foo junk, bar; baz=2 junk`, nil},
|
{`foo junk, bar; baz=2 junk`, nil},
|
||||||
{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{
|
{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{
|
||||||
{"": "mux", "max-channels": "4", "flow-control": ""},
|
map[string]string{"": "mux", "max-channels": "4", "flow-control": ""},
|
||||||
{"": "deflate-stream"}}},
|
map[string]string{"": "deflate-stream"}}},
|
||||||
{`permessage-foo; x="10"`, []map[string]string{
|
{`permessage-foo; x="10"`, []map[string]string{
|
||||||
{"": "permessage-foo", "x": "10"}}},
|
map[string]string{"": "permessage-foo", "x": "10"}}},
|
||||||
{`permessage-foo; use_y, permessage-foo`, []map[string]string{
|
{`permessage-foo; use_y, permessage-foo`, []map[string]string{
|
||||||
{"": "permessage-foo", "use_y": ""},
|
map[string]string{"": "permessage-foo", "use_y": ""},
|
||||||
{"": "permessage-foo"}}},
|
map[string]string{"": "permessage-foo"}}},
|
||||||
{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{
|
{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{
|
||||||
{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
|
map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
|
||||||
{"": "permessage-deflate", "client_max_window_bits": ""}}},
|
map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}},
|
||||||
{"permessage-deflate; server_no_context_takeover; client_max_window_bits=15", []map[string]string{
|
|
||||||
{"": "permessage-deflate", "server_no_context_takeover": "", "client_max_window_bits": "15"},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseExtensions(t *testing.T) {
|
func TestParseExtensions(t *testing.T) {
|
||||||
|
|||||||
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
@@ -1,473 +0,0 @@
|
|||||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
|
||||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
|
||||||
|
|
||||||
// Package proxy provides support for a variety of protocols to proxy network
|
|
||||||
// data.
|
|
||||||
//
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type proxy_direct struct{}
|
|
||||||
|
|
||||||
// Direct is a direct proxy: one that makes network connections directly.
|
|
||||||
var proxy_Direct = proxy_direct{}
|
|
||||||
|
|
||||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
return net.Dial(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PerHost directs connections to a default Dialer unless the host name
|
|
||||||
// requested matches one of a number of exceptions.
|
|
||||||
type proxy_PerHost struct {
|
|
||||||
def, bypass proxy_Dialer
|
|
||||||
|
|
||||||
bypassNetworks []*net.IPNet
|
|
||||||
bypassIPs []net.IP
|
|
||||||
bypassZones []string
|
|
||||||
bypassHosts []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
|
||||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
|
||||||
// the configured rules.
|
|
||||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
|
||||||
return &proxy_PerHost{
|
|
||||||
def: defaultDialer,
|
|
||||||
bypass: bypass,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address addr on the given network through either
|
|
||||||
// defaultDialer or bypass.
|
|
||||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
|
||||||
host, _, err := net.SplitHostPort(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.dialerForRequest(host).Dial(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
for _, net := range p.bypassNetworks {
|
|
||||||
if net.Contains(ip) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bypassIP := range p.bypassIPs {
|
|
||||||
if bypassIP.Equal(ip) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.def
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zone := range p.bypassZones {
|
|
||||||
if strings.HasSuffix(host, zone) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
if host == zone[1:] {
|
|
||||||
// For a zone ".example.com", we match "example.com"
|
|
||||||
// too.
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bypassHost := range p.bypassHosts {
|
|
||||||
if bypassHost == host {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.def
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFromString parses a string that contains comma-separated values
|
|
||||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
|
||||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
|
||||||
// (localhost). A best effort is made to parse the string and errors are
|
|
||||||
// ignored.
|
|
||||||
func (p *proxy_PerHost) AddFromString(s string) {
|
|
||||||
hosts := strings.Split(s, ",")
|
|
||||||
for _, host := range hosts {
|
|
||||||
host = strings.TrimSpace(host)
|
|
||||||
if len(host) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(host, "/") {
|
|
||||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
|
||||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
|
||||||
p.AddNetwork(net)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
p.AddIP(ip)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(host, "*.") {
|
|
||||||
p.AddZone(host[1:])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p.AddHost(host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
|
||||||
// this will only take effect if a literal IP address is dialed. A connection
|
|
||||||
// to a named host will never match an IP.
|
|
||||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
|
||||||
p.bypassIPs = append(p.bypassIPs, ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
|
||||||
// this will only take effect if a literal IP address is dialed. A connection
|
|
||||||
// to a named host will never match.
|
|
||||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
|
||||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
|
||||||
// "example.com" matches "example.com" and all of its subdomains.
|
|
||||||
func (p *proxy_PerHost) AddZone(zone string) {
|
|
||||||
if strings.HasSuffix(zone, ".") {
|
|
||||||
zone = zone[:len(zone)-1]
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(zone, ".") {
|
|
||||||
zone = "." + zone
|
|
||||||
}
|
|
||||||
p.bypassZones = append(p.bypassZones, zone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHost specifies a host name that will use the bypass proxy.
|
|
||||||
func (p *proxy_PerHost) AddHost(host string) {
|
|
||||||
if strings.HasSuffix(host, ".") {
|
|
||||||
host = host[:len(host)-1]
|
|
||||||
}
|
|
||||||
p.bypassHosts = append(p.bypassHosts, host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Dialer is a means to establish a connection.
|
|
||||||
type proxy_Dialer interface {
|
|
||||||
// Dial connects to the given address via the proxy.
|
|
||||||
Dial(network, addr string) (c net.Conn, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth contains authentication parameters that specific Dialers may require.
|
|
||||||
type proxy_Auth struct {
|
|
||||||
User, Password string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
|
||||||
// the environment.
|
|
||||||
func proxy_FromEnvironment() proxy_Dialer {
|
|
||||||
allProxy := proxy_allProxyEnv.Get()
|
|
||||||
if len(allProxy) == 0 {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyURL, err := url.Parse(allProxy)
|
|
||||||
if err != nil {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
|
||||||
if err != nil {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
|
|
||||||
noProxy := proxy_noProxyEnv.Get()
|
|
||||||
if len(noProxy) == 0 {
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
|
||||||
perHost.AddFromString(noProxy)
|
|
||||||
return perHost
|
|
||||||
}
|
|
||||||
|
|
||||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
|
||||||
// from a URL with such a scheme.
|
|
||||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
|
||||||
|
|
||||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
|
||||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
|
||||||
// by FromURL.
|
|
||||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
|
||||||
if proxy_proxySchemes == nil {
|
|
||||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
|
||||||
}
|
|
||||||
proxy_proxySchemes[scheme] = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromURL returns a Dialer given a URL specification and an underlying
|
|
||||||
// Dialer for it to make network requests.
|
|
||||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
var auth *proxy_Auth
|
|
||||||
if u.User != nil {
|
|
||||||
auth = new(proxy_Auth)
|
|
||||||
auth.User = u.User.Username()
|
|
||||||
if p, ok := u.User.Password(); ok {
|
|
||||||
auth.Password = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Scheme {
|
|
||||||
case "socks5":
|
|
||||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
|
||||||
// was registered by another package.
|
|
||||||
if proxy_proxySchemes != nil {
|
|
||||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
|
||||||
return f(u, forward)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
proxy_allProxyEnv = &proxy_envOnce{
|
|
||||||
names: []string{"ALL_PROXY", "all_proxy"},
|
|
||||||
}
|
|
||||||
proxy_noProxyEnv = &proxy_envOnce{
|
|
||||||
names: []string{"NO_PROXY", "no_proxy"},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// envOnce looks up an environment variable (optionally by multiple
|
|
||||||
// names) once. It mitigates expensive lookups on some platforms
|
|
||||||
// (e.g. Windows).
|
|
||||||
// (Borrowed from net/http/transport.go)
|
|
||||||
type proxy_envOnce struct {
|
|
||||||
names []string
|
|
||||||
once sync.Once
|
|
||||||
val string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *proxy_envOnce) Get() string {
|
|
||||||
e.once.Do(e.init)
|
|
||||||
return e.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *proxy_envOnce) init() {
|
|
||||||
for _, n := range e.names {
|
|
||||||
e.val = os.Getenv(n)
|
|
||||||
if e.val != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
|
||||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
|
||||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
s := &proxy_socks5{
|
|
||||||
network: network,
|
|
||||||
addr: addr,
|
|
||||||
forward: forward,
|
|
||||||
}
|
|
||||||
if auth != nil {
|
|
||||||
s.user = auth.User
|
|
||||||
s.password = auth.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type proxy_socks5 struct {
|
|
||||||
user, password string
|
|
||||||
network, addr string
|
|
||||||
forward proxy_Dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxy_socks5Version = 5
|
|
||||||
|
|
||||||
const (
|
|
||||||
proxy_socks5AuthNone = 0
|
|
||||||
proxy_socks5AuthPassword = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const proxy_socks5Connect = 1
|
|
||||||
|
|
||||||
const (
|
|
||||||
proxy_socks5IP4 = 1
|
|
||||||
proxy_socks5Domain = 3
|
|
||||||
proxy_socks5IP6 = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var proxy_socks5Errors = []string{
|
|
||||||
"",
|
|
||||||
"general failure",
|
|
||||||
"connection forbidden",
|
|
||||||
"network unreachable",
|
|
||||||
"host unreachable",
|
|
||||||
"connection refused",
|
|
||||||
"TTL expired",
|
|
||||||
"command not supported",
|
|
||||||
"address type not supported",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
|
||||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
switch network {
|
|
||||||
case "tcp", "tcp6", "tcp4":
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := s.forward.Dial(s.network, s.addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := s.connect(conn, addr); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect takes an existing connection to a socks5 proxy server,
|
|
||||||
// and commands the server to extend that connection to target,
|
|
||||||
// which must be a canonical address with a host and port.
|
|
||||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
|
||||||
host, portStr, err := net.SplitHostPort(target)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := strconv.Atoi(portStr)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
|
||||||
}
|
|
||||||
if port < 1 || port > 0xffff {
|
|
||||||
return errors.New("proxy: port number out of range: " + portStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the size here is just an estimate
|
|
||||||
buf := make([]byte, 0, 6+len(host))
|
|
||||||
|
|
||||||
buf = append(buf, proxy_socks5Version)
|
|
||||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
|
||||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
if buf[0] != 5 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
|
||||||
}
|
|
||||||
if buf[1] == 0xff {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
|
||||||
}
|
|
||||||
|
|
||||||
// See RFC 1929
|
|
||||||
if buf[1] == proxy_socks5AuthPassword {
|
|
||||||
buf = buf[:0]
|
|
||||||
buf = append(buf, 1 /* password protocol version */)
|
|
||||||
buf = append(buf, uint8(len(s.user)))
|
|
||||||
buf = append(buf, s.user...)
|
|
||||||
buf = append(buf, uint8(len(s.password)))
|
|
||||||
buf = append(buf, s.password...)
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf[1] != 0 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:0]
|
|
||||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
|
||||||
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
|
||||||
buf = append(buf, proxy_socks5IP4)
|
|
||||||
ip = ip4
|
|
||||||
} else {
|
|
||||||
buf = append(buf, proxy_socks5IP6)
|
|
||||||
}
|
|
||||||
buf = append(buf, ip...)
|
|
||||||
} else {
|
|
||||||
if len(host) > 255 {
|
|
||||||
return errors.New("proxy: destination host name too long: " + host)
|
|
||||||
}
|
|
||||||
buf = append(buf, proxy_socks5Domain)
|
|
||||||
buf = append(buf, byte(len(host)))
|
|
||||||
buf = append(buf, host...)
|
|
||||||
}
|
|
||||||
buf = append(buf, byte(port>>8), byte(port))
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
failure := "unknown error"
|
|
||||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
|
||||||
failure = proxy_socks5Errors[buf[1]]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(failure) > 0 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesToDiscard := 0
|
|
||||||
switch buf[3] {
|
|
||||||
case proxy_socks5IP4:
|
|
||||||
bytesToDiscard = net.IPv4len
|
|
||||||
case proxy_socks5IP6:
|
|
||||||
bytesToDiscard = net.IPv6len
|
|
||||||
case proxy_socks5Domain:
|
|
||||||
_, err := io.ReadFull(conn, buf[:1])
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
bytesToDiscard = int(buf[0])
|
|
||||||
default:
|
|
||||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cap(buf) < bytesToDiscard {
|
|
||||||
buf = make([]byte, bytesToDiscard)
|
|
||||||
} else {
|
|
||||||
buf = buf[:bytesToDiscard]
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also need to discard the port number
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -21,4 +21,3 @@ _testmain.go
|
|||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
|
||||||
362
vendor/github.com/hashicorp/yamux/LICENSE
generated
vendored
Normal file
362
vendor/github.com/hashicorp/yamux/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
||||||
86
vendor/github.com/hashicorp/yamux/README.md
generated
vendored
Normal file
86
vendor/github.com/hashicorp/yamux/README.md
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Yamux
|
||||||
|
|
||||||
|
Yamux (Yet another Multiplexer) is a multiplexing library for Golang.
|
||||||
|
It relies on an underlying connection to provide reliability
|
||||||
|
and ordering, such as TCP or Unix domain sockets, and provides
|
||||||
|
stream-oriented multiplexing. It is inspired by SPDY but is not
|
||||||
|
interoperable with it.
|
||||||
|
|
||||||
|
Yamux features include:
|
||||||
|
|
||||||
|
* Bi-directional streams
|
||||||
|
* Streams can be opened by either client or server
|
||||||
|
* Useful for NAT traversal
|
||||||
|
* Server-side push support
|
||||||
|
* Flow control
|
||||||
|
* Avoid starvation
|
||||||
|
* Back-pressure to prevent overwhelming a receiver
|
||||||
|
* Keep Alives
|
||||||
|
* Enables persistent connections over a load balancer
|
||||||
|
* Efficient
|
||||||
|
* Enables thousands of logical streams with low overhead
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For complete documentation, see the associated [Godoc](http://godoc.org/github.com/hashicorp/yamux).
|
||||||
|
|
||||||
|
## Specification
|
||||||
|
|
||||||
|
The full specification for Yamux is provided in the `spec.md` file.
|
||||||
|
It can be used as a guide to implementors of interoperable libraries.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Using Yamux is remarkably simple:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
func client() {
|
||||||
|
// Get a TCP connection
|
||||||
|
conn, err := net.Dial(...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup client side of yamux
|
||||||
|
session, err := yamux.Client(conn, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a new stream
|
||||||
|
stream, err := session.Open()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream implements net.Conn
|
||||||
|
stream.Write([]byte("ping"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func server() {
|
||||||
|
// Accept a TCP connection
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup server side of yamux
|
||||||
|
session, err := yamux.Server(conn, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept a stream
|
||||||
|
stream, err := session.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for a message
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
stream.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
60
vendor/github.com/hashicorp/yamux/addr.go
generated
vendored
Normal file
60
vendor/github.com/hashicorp/yamux/addr.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hasAddr is used to get the address from the underlying connection
|
||||||
|
type hasAddr interface {
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
RemoteAddr() net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// yamuxAddr is used when we cannot get the underlying address
|
||||||
|
type yamuxAddr struct {
|
||||||
|
Addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*yamuxAddr) Network() string {
|
||||||
|
return "yamux"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (y *yamuxAddr) String() string {
|
||||||
|
return fmt.Sprintf("yamux:%s", y.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr is used to get the address of the listener.
|
||||||
|
func (s *Session) Addr() net.Addr {
|
||||||
|
return s.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr is used to get the local address of the
|
||||||
|
// underlying connection.
|
||||||
|
func (s *Session) LocalAddr() net.Addr {
|
||||||
|
addr, ok := s.conn.(hasAddr)
|
||||||
|
if !ok {
|
||||||
|
return &yamuxAddr{"local"}
|
||||||
|
}
|
||||||
|
return addr.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr is used to get the address of remote end
|
||||||
|
// of the underlying connection
|
||||||
|
func (s *Session) RemoteAddr() net.Addr {
|
||||||
|
addr, ok := s.conn.(hasAddr)
|
||||||
|
if !ok {
|
||||||
|
return &yamuxAddr{"remote"}
|
||||||
|
}
|
||||||
|
return addr.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr returns the local address
|
||||||
|
func (s *Stream) LocalAddr() net.Addr {
|
||||||
|
return s.session.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr returns the remote address
|
||||||
|
func (s *Stream) RemoteAddr() net.Addr {
|
||||||
|
return s.session.RemoteAddr()
|
||||||
|
}
|
||||||
123
vendor/github.com/hashicorp/yamux/bench_test.go
generated
vendored
Normal file
123
vendor/github.com/hashicorp/yamux/bench_test.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkPing(b *testing.B) {
|
||||||
|
client, server := testClientServer()
|
||||||
|
defer client.Close()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rtt, err := client.Ping()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if rtt == 0 {
|
||||||
|
b.Fatalf("bad: %v", rtt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAccept(b *testing.B) {
|
||||||
|
client, server := testClientServer()
|
||||||
|
defer client.Close()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
stream, err := server.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stream.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
stream, err := client.Open()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
stream.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSendRecv(b *testing.B) {
|
||||||
|
client, server := testClientServer()
|
||||||
|
defer client.Close()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
sendBuf := make([]byte, 512)
|
||||||
|
recvBuf := make([]byte, 512)
|
||||||
|
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
stream, err := server.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stream.Close()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := stream.Read(recvBuf); err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(doneCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
stream, err := client.Open()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
defer stream.Close()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := stream.Write(sendBuf); err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<-doneCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSendRecvLarge(b *testing.B) {
|
||||||
|
client, server := testClientServer()
|
||||||
|
defer client.Close()
|
||||||
|
defer server.Close()
|
||||||
|
const sendSize = 512 * 1024 * 1024
|
||||||
|
const recvSize = 4 * 1024
|
||||||
|
|
||||||
|
sendBuf := make([]byte, sendSize)
|
||||||
|
recvBuf := make([]byte, recvSize)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
recvDone := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
stream, err := server.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stream.Close()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < sendSize/recvSize; j++ {
|
||||||
|
if _, err := stream.Read(recvBuf); err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(recvDone)
|
||||||
|
}()
|
||||||
|
|
||||||
|
stream, err := client.Open()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
defer stream.Close()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := stream.Write(sendBuf); err != nil {
|
||||||
|
b.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<-recvDone
|
||||||
|
}
|
||||||
157
vendor/github.com/hashicorp/yamux/const.go
generated
vendored
Normal file
157
vendor/github.com/hashicorp/yamux/const.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidVersion means we received a frame with an
|
||||||
|
// invalid version
|
||||||
|
ErrInvalidVersion = fmt.Errorf("invalid protocol version")
|
||||||
|
|
||||||
|
// ErrInvalidMsgType means we received a frame with an
|
||||||
|
// invalid message type
|
||||||
|
ErrInvalidMsgType = fmt.Errorf("invalid msg type")
|
||||||
|
|
||||||
|
// ErrSessionShutdown is used if there is a shutdown during
|
||||||
|
// an operation
|
||||||
|
ErrSessionShutdown = fmt.Errorf("session shutdown")
|
||||||
|
|
||||||
|
// ErrStreamsExhausted is returned if we have no more
|
||||||
|
// stream ids to issue
|
||||||
|
ErrStreamsExhausted = fmt.Errorf("streams exhausted")
|
||||||
|
|
||||||
|
// ErrDuplicateStream is used if a duplicate stream is
|
||||||
|
// opened inbound
|
||||||
|
ErrDuplicateStream = fmt.Errorf("duplicate stream initiated")
|
||||||
|
|
||||||
|
// ErrReceiveWindowExceeded indicates the window was exceeded
|
||||||
|
ErrRecvWindowExceeded = fmt.Errorf("recv window exceeded")
|
||||||
|
|
||||||
|
// ErrTimeout is used when we reach an IO deadline
|
||||||
|
ErrTimeout = fmt.Errorf("i/o deadline reached")
|
||||||
|
|
||||||
|
// ErrStreamClosed is returned when using a closed stream
|
||||||
|
ErrStreamClosed = fmt.Errorf("stream closed")
|
||||||
|
|
||||||
|
// ErrUnexpectedFlag is set when we get an unexpected flag
|
||||||
|
ErrUnexpectedFlag = fmt.Errorf("unexpected flag")
|
||||||
|
|
||||||
|
// ErrRemoteGoAway is used when we get a go away from the other side
|
||||||
|
ErrRemoteGoAway = fmt.Errorf("remote end is not accepting connections")
|
||||||
|
|
||||||
|
// ErrConnectionReset is sent if a stream is reset. This can happen
|
||||||
|
// if the backlog is exceeded, or if there was a remote GoAway.
|
||||||
|
ErrConnectionReset = fmt.Errorf("connection reset")
|
||||||
|
|
||||||
|
// ErrConnectionWriteTimeout indicates that we hit the "safety valve"
|
||||||
|
// timeout writing to the underlying stream connection.
|
||||||
|
ErrConnectionWriteTimeout = fmt.Errorf("connection write timeout")
|
||||||
|
|
||||||
|
// ErrKeepAliveTimeout is sent if a missed keepalive caused the stream close
|
||||||
|
ErrKeepAliveTimeout = fmt.Errorf("keepalive timeout")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// protoVersion is the only version we support
|
||||||
|
protoVersion uint8 = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Data is used for data frames. They are followed
|
||||||
|
// by length bytes worth of payload.
|
||||||
|
typeData uint8 = iota
|
||||||
|
|
||||||
|
// WindowUpdate is used to change the window of
|
||||||
|
// a given stream. The length indicates the delta
|
||||||
|
// update to the window.
|
||||||
|
typeWindowUpdate
|
||||||
|
|
||||||
|
// Ping is sent as a keep-alive or to measure
|
||||||
|
// the RTT. The StreamID and Length value are echoed
|
||||||
|
// back in the response.
|
||||||
|
typePing
|
||||||
|
|
||||||
|
// GoAway is sent to terminate a session. The StreamID
|
||||||
|
// should be 0 and the length is an error code.
|
||||||
|
typeGoAway
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SYN is sent to signal a new stream. May
|
||||||
|
// be sent with a data payload
|
||||||
|
flagSYN uint16 = 1 << iota
|
||||||
|
|
||||||
|
// ACK is sent to acknowledge a new stream. May
|
||||||
|
// be sent with a data payload
|
||||||
|
flagACK
|
||||||
|
|
||||||
|
// FIN is sent to half-close the given stream.
|
||||||
|
// May be sent with a data payload.
|
||||||
|
flagFIN
|
||||||
|
|
||||||
|
// RST is used to hard close a given stream.
|
||||||
|
flagRST
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// initialStreamWindow is the initial stream window size
|
||||||
|
initialStreamWindow uint32 = 256 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// goAwayNormal is sent on a normal termination
|
||||||
|
goAwayNormal uint32 = iota
|
||||||
|
|
||||||
|
// goAwayProtoErr sent on a protocol error
|
||||||
|
goAwayProtoErr
|
||||||
|
|
||||||
|
// goAwayInternalErr sent on an internal error
|
||||||
|
goAwayInternalErr
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sizeOfVersion = 1
|
||||||
|
sizeOfType = 1
|
||||||
|
sizeOfFlags = 2
|
||||||
|
sizeOfStreamID = 4
|
||||||
|
sizeOfLength = 4
|
||||||
|
headerSize = sizeOfVersion + sizeOfType + sizeOfFlags +
|
||||||
|
sizeOfStreamID + sizeOfLength
|
||||||
|
)
|
||||||
|
|
||||||
|
type header []byte
|
||||||
|
|
||||||
|
func (h header) Version() uint8 {
|
||||||
|
return h[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h header) MsgType() uint8 {
|
||||||
|
return h[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h header) Flags() uint16 {
|
||||||
|
return binary.BigEndian.Uint16(h[2:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h header) StreamID() uint32 {
|
||||||
|
return binary.BigEndian.Uint32(h[4:8])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h header) Length() uint32 {
|
||||||
|
return binary.BigEndian.Uint32(h[8:12])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h header) String() string {
|
||||||
|
return fmt.Sprintf("Vsn:%d Type:%d Flags:%d StreamID:%d Length:%d",
|
||||||
|
h.Version(), h.MsgType(), h.Flags(), h.StreamID(), h.Length())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h header) encode(msgType uint8, flags uint16, streamID uint32, length uint32) {
|
||||||
|
h[0] = protoVersion
|
||||||
|
h[1] = msgType
|
||||||
|
binary.BigEndian.PutUint16(h[2:4], flags)
|
||||||
|
binary.BigEndian.PutUint32(h[4:8], streamID)
|
||||||
|
binary.BigEndian.PutUint32(h[8:12], length)
|
||||||
|
}
|
||||||
72
vendor/github.com/hashicorp/yamux/const_test.go
generated
vendored
Normal file
72
vendor/github.com/hashicorp/yamux/const_test.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConst(t *testing.T) {
|
||||||
|
if protoVersion != 0 {
|
||||||
|
t.Fatalf("bad: %v", protoVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeData != 0 {
|
||||||
|
t.Fatalf("bad: %v", typeData)
|
||||||
|
}
|
||||||
|
if typeWindowUpdate != 1 {
|
||||||
|
t.Fatalf("bad: %v", typeWindowUpdate)
|
||||||
|
}
|
||||||
|
if typePing != 2 {
|
||||||
|
t.Fatalf("bad: %v", typePing)
|
||||||
|
}
|
||||||
|
if typeGoAway != 3 {
|
||||||
|
t.Fatalf("bad: %v", typeGoAway)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flagSYN != 1 {
|
||||||
|
t.Fatalf("bad: %v", flagSYN)
|
||||||
|
}
|
||||||
|
if flagACK != 2 {
|
||||||
|
t.Fatalf("bad: %v", flagACK)
|
||||||
|
}
|
||||||
|
if flagFIN != 4 {
|
||||||
|
t.Fatalf("bad: %v", flagFIN)
|
||||||
|
}
|
||||||
|
if flagRST != 8 {
|
||||||
|
t.Fatalf("bad: %v", flagRST)
|
||||||
|
}
|
||||||
|
|
||||||
|
if goAwayNormal != 0 {
|
||||||
|
t.Fatalf("bad: %v", goAwayNormal)
|
||||||
|
}
|
||||||
|
if goAwayProtoErr != 1 {
|
||||||
|
t.Fatalf("bad: %v", goAwayProtoErr)
|
||||||
|
}
|
||||||
|
if goAwayInternalErr != 2 {
|
||||||
|
t.Fatalf("bad: %v", goAwayInternalErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if headerSize != 12 {
|
||||||
|
t.Fatalf("bad header size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeDecode(t *testing.T) {
|
||||||
|
hdr := header(make([]byte, headerSize))
|
||||||
|
hdr.encode(typeWindowUpdate, flagACK|flagRST, 1234, 4321)
|
||||||
|
|
||||||
|
if hdr.Version() != protoVersion {
|
||||||
|
t.Fatalf("bad: %v", hdr)
|
||||||
|
}
|
||||||
|
if hdr.MsgType() != typeWindowUpdate {
|
||||||
|
t.Fatalf("bad: %v", hdr)
|
||||||
|
}
|
||||||
|
if hdr.Flags() != flagACK|flagRST {
|
||||||
|
t.Fatalf("bad: %v", hdr)
|
||||||
|
}
|
||||||
|
if hdr.StreamID() != 1234 {
|
||||||
|
t.Fatalf("bad: %v", hdr)
|
||||||
|
}
|
||||||
|
if hdr.Length() != 4321 {
|
||||||
|
t.Fatalf("bad: %v", hdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
87
vendor/github.com/hashicorp/yamux/mux.go
generated
vendored
Normal file
87
vendor/github.com/hashicorp/yamux/mux.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to tune the Yamux session
|
||||||
|
type Config struct {
|
||||||
|
// AcceptBacklog is used to limit how many streams may be
|
||||||
|
// waiting an accept.
|
||||||
|
AcceptBacklog int
|
||||||
|
|
||||||
|
// EnableKeepalive is used to do a period keep alive
|
||||||
|
// messages using a ping.
|
||||||
|
EnableKeepAlive bool
|
||||||
|
|
||||||
|
// KeepAliveInterval is how often to perform the keep alive
|
||||||
|
KeepAliveInterval time.Duration
|
||||||
|
|
||||||
|
// ConnectionWriteTimeout is meant to be a "safety valve" timeout after
|
||||||
|
// we which will suspect a problem with the underlying connection and
|
||||||
|
// close it. This is only applied to writes, where's there's generally
|
||||||
|
// an expectation that things will move along quickly.
|
||||||
|
ConnectionWriteTimeout time.Duration
|
||||||
|
|
||||||
|
// MaxStreamWindowSize is used to control the maximum
|
||||||
|
// window size that we allow for a stream.
|
||||||
|
MaxStreamWindowSize uint32
|
||||||
|
|
||||||
|
// LogOutput is used to control the log destination
|
||||||
|
LogOutput io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig is used to return a default configuration
|
||||||
|
func DefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
AcceptBacklog: 256,
|
||||||
|
EnableKeepAlive: true,
|
||||||
|
KeepAliveInterval: 30 * time.Second,
|
||||||
|
ConnectionWriteTimeout: 10 * time.Second,
|
||||||
|
MaxStreamWindowSize: initialStreamWindow,
|
||||||
|
LogOutput: os.Stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyConfig is used to verify the sanity of configuration
|
||||||
|
func VerifyConfig(config *Config) error {
|
||||||
|
if config.AcceptBacklog <= 0 {
|
||||||
|
return fmt.Errorf("backlog must be positive")
|
||||||
|
}
|
||||||
|
if config.KeepAliveInterval == 0 {
|
||||||
|
return fmt.Errorf("keep-alive interval must be positive")
|
||||||
|
}
|
||||||
|
if config.MaxStreamWindowSize < initialStreamWindow {
|
||||||
|
return fmt.Errorf("MaxStreamWindowSize must be larger than %d", initialStreamWindow)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server is used to initialize a new server-side connection.
|
||||||
|
// There must be at most one server-side connection. If a nil config is
|
||||||
|
// provided, the DefaultConfiguration will be used.
|
||||||
|
func Server(conn io.ReadWriteCloser, config *Config) (*Session, error) {
|
||||||
|
if config == nil {
|
||||||
|
config = DefaultConfig()
|
||||||
|
}
|
||||||
|
if err := VerifyConfig(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newSession(config, conn, false), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is used to initialize a new client-side connection.
|
||||||
|
// There must be at most one client-side connection.
|
||||||
|
func Client(conn io.ReadWriteCloser, config *Config) (*Session, error) {
|
||||||
|
if config == nil {
|
||||||
|
config = DefaultConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := VerifyConfig(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newSession(config, conn, true), nil
|
||||||
|
}
|
||||||
646
vendor/github.com/hashicorp/yamux/session.go
generated
vendored
Normal file
646
vendor/github.com/hashicorp/yamux/session.go
generated
vendored
Normal file
@@ -0,0 +1,646 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Session is used to wrap a reliable ordered connection and to
|
||||||
|
// multiplex it into multiple streams.
|
||||||
|
type Session struct {
|
||||||
|
// remoteGoAway indicates the remote side does
|
||||||
|
// not want futher connections. Must be first for alignment.
|
||||||
|
remoteGoAway int32
|
||||||
|
|
||||||
|
// localGoAway indicates that we should stop
|
||||||
|
// accepting futher connections. Must be first for alignment.
|
||||||
|
localGoAway int32
|
||||||
|
|
||||||
|
// nextStreamID is the next stream we should
|
||||||
|
// send. This depends if we are a client/server.
|
||||||
|
nextStreamID uint32
|
||||||
|
|
||||||
|
// config holds our configuration
|
||||||
|
config *Config
|
||||||
|
|
||||||
|
// logger is used for our logs
|
||||||
|
logger *log.Logger
|
||||||
|
|
||||||
|
// conn is the underlying connection
|
||||||
|
conn io.ReadWriteCloser
|
||||||
|
|
||||||
|
// bufRead is a buffered reader
|
||||||
|
bufRead *bufio.Reader
|
||||||
|
|
||||||
|
// pings is used to track inflight pings
|
||||||
|
pings map[uint32]chan struct{}
|
||||||
|
pingID uint32
|
||||||
|
pingLock sync.Mutex
|
||||||
|
|
||||||
|
// streams maps a stream id to a stream, and inflight has an entry
|
||||||
|
// for any outgoing stream that has not yet been established. Both are
|
||||||
|
// protected by streamLock.
|
||||||
|
streams map[uint32]*Stream
|
||||||
|
inflight map[uint32]struct{}
|
||||||
|
streamLock sync.Mutex
|
||||||
|
|
||||||
|
// synCh acts like a semaphore. It is sized to the AcceptBacklog which
|
||||||
|
// is assumed to be symmetric between the client and server. This allows
|
||||||
|
// the client to avoid exceeding the backlog and instead blocks the open.
|
||||||
|
synCh chan struct{}
|
||||||
|
|
||||||
|
// acceptCh is used to pass ready streams to the client
|
||||||
|
acceptCh chan *Stream
|
||||||
|
|
||||||
|
// sendCh is used to mark a stream as ready to send,
|
||||||
|
// or to send a header out directly.
|
||||||
|
sendCh chan sendReady
|
||||||
|
|
||||||
|
// recvDoneCh is closed when recv() exits to avoid a race
|
||||||
|
// between stream registration and stream shutdown
|
||||||
|
recvDoneCh chan struct{}
|
||||||
|
|
||||||
|
// shutdown is used to safely close a session
|
||||||
|
shutdown bool
|
||||||
|
shutdownErr error
|
||||||
|
shutdownCh chan struct{}
|
||||||
|
shutdownLock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendReady is used to either mark a stream as ready
|
||||||
|
// or to directly send a header
|
||||||
|
type sendReady struct {
|
||||||
|
Hdr []byte
|
||||||
|
Body io.Reader
|
||||||
|
Err chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSession is used to construct a new session
|
||||||
|
func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
|
||||||
|
s := &Session{
|
||||||
|
config: config,
|
||||||
|
logger: log.New(config.LogOutput, "", log.LstdFlags),
|
||||||
|
conn: conn,
|
||||||
|
bufRead: bufio.NewReader(conn),
|
||||||
|
pings: make(map[uint32]chan struct{}),
|
||||||
|
streams: make(map[uint32]*Stream),
|
||||||
|
inflight: make(map[uint32]struct{}),
|
||||||
|
synCh: make(chan struct{}, config.AcceptBacklog),
|
||||||
|
acceptCh: make(chan *Stream, config.AcceptBacklog),
|
||||||
|
sendCh: make(chan sendReady, 64),
|
||||||
|
recvDoneCh: make(chan struct{}),
|
||||||
|
shutdownCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
if client {
|
||||||
|
s.nextStreamID = 1
|
||||||
|
} else {
|
||||||
|
s.nextStreamID = 2
|
||||||
|
}
|
||||||
|
go s.recv()
|
||||||
|
go s.send()
|
||||||
|
if config.EnableKeepAlive {
|
||||||
|
go s.keepalive()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClosed does a safe check to see if we have shutdown
|
||||||
|
func (s *Session) IsClosed() bool {
|
||||||
|
select {
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseChan returns a read-only channel which is closed as
|
||||||
|
// soon as the session is closed.
|
||||||
|
func (s *Session) CloseChan() <-chan struct{} {
|
||||||
|
return s.shutdownCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumStreams returns the number of currently open streams
|
||||||
|
func (s *Session) NumStreams() int {
|
||||||
|
s.streamLock.Lock()
|
||||||
|
num := len(s.streams)
|
||||||
|
s.streamLock.Unlock()
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open is used to create a new stream as a net.Conn
|
||||||
|
func (s *Session) Open() (net.Conn, error) {
|
||||||
|
conn, err := s.OpenStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenStream is used to create a new stream
|
||||||
|
func (s *Session) OpenStream() (*Stream, error) {
|
||||||
|
if s.IsClosed() {
|
||||||
|
return nil, ErrSessionShutdown
|
||||||
|
}
|
||||||
|
if atomic.LoadInt32(&s.remoteGoAway) == 1 {
|
||||||
|
return nil, ErrRemoteGoAway
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block if we have too many inflight SYNs
|
||||||
|
select {
|
||||||
|
case s.synCh <- struct{}{}:
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return nil, ErrSessionShutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_ID:
|
||||||
|
// Get an ID, and check for stream exhaustion
|
||||||
|
id := atomic.LoadUint32(&s.nextStreamID)
|
||||||
|
if id >= math.MaxUint32-1 {
|
||||||
|
return nil, ErrStreamsExhausted
|
||||||
|
}
|
||||||
|
if !atomic.CompareAndSwapUint32(&s.nextStreamID, id, id+2) {
|
||||||
|
goto GET_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the stream
|
||||||
|
stream := newStream(s, id, streamInit)
|
||||||
|
s.streamLock.Lock()
|
||||||
|
s.streams[id] = stream
|
||||||
|
s.inflight[id] = struct{}{}
|
||||||
|
s.streamLock.Unlock()
|
||||||
|
|
||||||
|
// Send the window update to create
|
||||||
|
if err := stream.sendWindowUpdate(); err != nil {
|
||||||
|
select {
|
||||||
|
case <-s.synCh:
|
||||||
|
default:
|
||||||
|
s.logger.Printf("[ERR] yamux: aborted stream open without inflight syn semaphore")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept is used to block until the next available stream
|
||||||
|
// is ready to be accepted.
|
||||||
|
func (s *Session) Accept() (net.Conn, error) {
|
||||||
|
conn, err := s.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptStream is used to block until the next available stream
|
||||||
|
// is ready to be accepted.
|
||||||
|
func (s *Session) AcceptStream() (*Stream, error) {
|
||||||
|
select {
|
||||||
|
case stream := <-s.acceptCh:
|
||||||
|
if err := stream.sendWindowUpdate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stream, nil
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return nil, s.shutdownErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is used to close the session and all streams.
|
||||||
|
// Attempts to send a GoAway before closing the connection.
|
||||||
|
func (s *Session) Close() error {
|
||||||
|
s.shutdownLock.Lock()
|
||||||
|
defer s.shutdownLock.Unlock()
|
||||||
|
|
||||||
|
if s.shutdown {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.shutdown = true
|
||||||
|
if s.shutdownErr == nil {
|
||||||
|
s.shutdownErr = ErrSessionShutdown
|
||||||
|
}
|
||||||
|
close(s.shutdownCh)
|
||||||
|
s.conn.Close()
|
||||||
|
<-s.recvDoneCh
|
||||||
|
|
||||||
|
s.streamLock.Lock()
|
||||||
|
defer s.streamLock.Unlock()
|
||||||
|
for _, stream := range s.streams {
|
||||||
|
stream.forceClose()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// exitErr is used to handle an error that is causing the
|
||||||
|
// session to terminate.
|
||||||
|
func (s *Session) exitErr(err error) {
|
||||||
|
s.shutdownLock.Lock()
|
||||||
|
if s.shutdownErr == nil {
|
||||||
|
s.shutdownErr = err
|
||||||
|
}
|
||||||
|
s.shutdownLock.Unlock()
|
||||||
|
s.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoAway can be used to prevent accepting further
|
||||||
|
// connections. It does not close the underlying conn.
|
||||||
|
func (s *Session) GoAway() error {
|
||||||
|
return s.waitForSend(s.goAway(goAwayNormal), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// goAway is used to send a goAway message
|
||||||
|
func (s *Session) goAway(reason uint32) header {
|
||||||
|
atomic.SwapInt32(&s.localGoAway, 1)
|
||||||
|
hdr := header(make([]byte, headerSize))
|
||||||
|
hdr.encode(typeGoAway, 0, 0, reason)
|
||||||
|
return hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping is used to measure the RTT response time
|
||||||
|
func (s *Session) Ping() (time.Duration, error) {
|
||||||
|
// Get a channel for the ping
|
||||||
|
ch := make(chan struct{})
|
||||||
|
|
||||||
|
// Get a new ping id, mark as pending
|
||||||
|
s.pingLock.Lock()
|
||||||
|
id := s.pingID
|
||||||
|
s.pingID++
|
||||||
|
s.pings[id] = ch
|
||||||
|
s.pingLock.Unlock()
|
||||||
|
|
||||||
|
// Send the ping request
|
||||||
|
hdr := header(make([]byte, headerSize))
|
||||||
|
hdr.encode(typePing, flagSYN, 0, id)
|
||||||
|
if err := s.waitForSend(hdr, nil); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a response
|
||||||
|
start := time.Now()
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
case <-time.After(s.config.ConnectionWriteTimeout):
|
||||||
|
s.pingLock.Lock()
|
||||||
|
delete(s.pings, id) // Ignore it if a response comes later.
|
||||||
|
s.pingLock.Unlock()
|
||||||
|
return 0, ErrTimeout
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return 0, ErrSessionShutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the RTT
|
||||||
|
return time.Now().Sub(start), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// keepalive is a long running goroutine that periodically does
|
||||||
|
// a ping to keep the connection alive.
|
||||||
|
func (s *Session) keepalive() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(s.config.KeepAliveInterval):
|
||||||
|
_, err := s.Ping()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("[ERR] yamux: keepalive failed: %v", err)
|
||||||
|
s.exitErr(ErrKeepAliveTimeout)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForSendErr waits to send a header, checking for a potential shutdown
|
||||||
|
func (s *Session) waitForSend(hdr header, body io.Reader) error {
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
return s.waitForSendErr(hdr, body, errCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForSendErr waits to send a header with optional data, checking for a
|
||||||
|
// potential shutdown. Since there's the expectation that sends can happen
|
||||||
|
// in a timely manner, we enforce the connection write timeout here.
|
||||||
|
func (s *Session) waitForSendErr(hdr header, body io.Reader, errCh chan error) error {
|
||||||
|
t := timerPool.Get()
|
||||||
|
timer := t.(*time.Timer)
|
||||||
|
timer.Reset(s.config.ConnectionWriteTimeout)
|
||||||
|
defer func() {
|
||||||
|
timer.Stop()
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
timerPool.Put(t)
|
||||||
|
}()
|
||||||
|
|
||||||
|
ready := sendReady{Hdr: hdr, Body: body, Err: errCh}
|
||||||
|
select {
|
||||||
|
case s.sendCh <- ready:
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return ErrSessionShutdown
|
||||||
|
case <-timer.C:
|
||||||
|
return ErrConnectionWriteTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
return err
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return ErrSessionShutdown
|
||||||
|
case <-timer.C:
|
||||||
|
return ErrConnectionWriteTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendNoWait does a send without waiting. Since there's the expectation that
|
||||||
|
// the send happens right here, we enforce the connection write timeout if we
|
||||||
|
// can't queue the header to be sent.
|
||||||
|
func (s *Session) sendNoWait(hdr header) error {
|
||||||
|
t := timerPool.Get()
|
||||||
|
timer := t.(*time.Timer)
|
||||||
|
timer.Reset(s.config.ConnectionWriteTimeout)
|
||||||
|
defer func() {
|
||||||
|
timer.Stop()
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
timerPool.Put(t)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case s.sendCh <- sendReady{Hdr: hdr}:
|
||||||
|
return nil
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return ErrSessionShutdown
|
||||||
|
case <-timer.C:
|
||||||
|
return ErrConnectionWriteTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send is a long running goroutine that sends data
|
||||||
|
func (s *Session) send() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ready := <-s.sendCh:
|
||||||
|
// Send a header if ready
|
||||||
|
if ready.Hdr != nil {
|
||||||
|
sent := 0
|
||||||
|
for sent < len(ready.Hdr) {
|
||||||
|
n, err := s.conn.Write(ready.Hdr[sent:])
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("[ERR] yamux: Failed to write header: %v", err)
|
||||||
|
asyncSendErr(ready.Err, err)
|
||||||
|
s.exitErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sent += n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send data from a body if given
|
||||||
|
if ready.Body != nil {
|
||||||
|
_, err := io.Copy(s.conn, ready.Body)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("[ERR] yamux: Failed to write body: %v", err)
|
||||||
|
asyncSendErr(ready.Err, err)
|
||||||
|
s.exitErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No error, successful send
|
||||||
|
asyncSendErr(ready.Err, nil)
|
||||||
|
case <-s.shutdownCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recv is a long running goroutine that accepts new data
|
||||||
|
func (s *Session) recv() {
|
||||||
|
if err := s.recvLoop(); err != nil {
|
||||||
|
s.exitErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the index of the handler (typeData/typeWindowUpdate/etc) matches the message type
|
||||||
|
var (
|
||||||
|
handlers = []func(*Session, header) error{
|
||||||
|
typeData: (*Session).handleStreamMessage,
|
||||||
|
typeWindowUpdate: (*Session).handleStreamMessage,
|
||||||
|
typePing: (*Session).handlePing,
|
||||||
|
typeGoAway: (*Session).handleGoAway,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// recvLoop continues to receive data until a fatal error is encountered
|
||||||
|
func (s *Session) recvLoop() error {
|
||||||
|
defer close(s.recvDoneCh)
|
||||||
|
hdr := header(make([]byte, headerSize))
|
||||||
|
for {
|
||||||
|
// Read the header
|
||||||
|
if _, err := io.ReadFull(s.bufRead, hdr); err != nil {
|
||||||
|
if err != io.EOF && !strings.Contains(err.Error(), "closed") && !strings.Contains(err.Error(), "reset by peer") {
|
||||||
|
s.logger.Printf("[ERR] yamux: Failed to read header: %v", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the version
|
||||||
|
if hdr.Version() != protoVersion {
|
||||||
|
s.logger.Printf("[ERR] yamux: Invalid protocol version: %d", hdr.Version())
|
||||||
|
return ErrInvalidVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := hdr.MsgType()
|
||||||
|
if mt < typeData || mt > typeGoAway {
|
||||||
|
return ErrInvalidMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := handlers[mt](s, hdr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleStreamMessage handles either a data or window update frame
|
||||||
|
func (s *Session) handleStreamMessage(hdr header) error {
|
||||||
|
// Check for a new stream creation
|
||||||
|
id := hdr.StreamID()
|
||||||
|
flags := hdr.Flags()
|
||||||
|
if flags&flagSYN == flagSYN {
|
||||||
|
if err := s.incomingStream(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the stream
|
||||||
|
s.streamLock.Lock()
|
||||||
|
stream := s.streams[id]
|
||||||
|
s.streamLock.Unlock()
|
||||||
|
|
||||||
|
// If we do not have a stream, likely we sent a RST
|
||||||
|
if stream == nil {
|
||||||
|
// Drain any data on the wire
|
||||||
|
if hdr.MsgType() == typeData && hdr.Length() > 0 {
|
||||||
|
s.logger.Printf("[WARN] yamux: Discarding data for stream: %d", id)
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, s.bufRead, int64(hdr.Length())); err != nil {
|
||||||
|
s.logger.Printf("[ERR] yamux: Failed to discard data: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.logger.Printf("[WARN] yamux: frame for missing stream: %v", hdr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a window update
|
||||||
|
if hdr.MsgType() == typeWindowUpdate {
|
||||||
|
if err := stream.incrSendWindow(hdr, flags); err != nil {
|
||||||
|
if sendErr := s.sendNoWait(s.goAway(goAwayProtoErr)); sendErr != nil {
|
||||||
|
s.logger.Printf("[WARN] yamux: failed to send go away: %v", sendErr)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the new data
|
||||||
|
if err := stream.readData(hdr, flags, s.bufRead); err != nil {
|
||||||
|
if sendErr := s.sendNoWait(s.goAway(goAwayProtoErr)); sendErr != nil {
|
||||||
|
s.logger.Printf("[WARN] yamux: failed to send go away: %v", sendErr)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlePing is invokde for a typePing frame
|
||||||
|
func (s *Session) handlePing(hdr header) error {
|
||||||
|
flags := hdr.Flags()
|
||||||
|
pingID := hdr.Length()
|
||||||
|
|
||||||
|
// Check if this is a query, respond back in a separate context so we
|
||||||
|
// don't interfere with the receiving thread blocking for the write.
|
||||||
|
if flags&flagSYN == flagSYN {
|
||||||
|
go func() {
|
||||||
|
hdr := header(make([]byte, headerSize))
|
||||||
|
hdr.encode(typePing, flagACK, 0, pingID)
|
||||||
|
if err := s.sendNoWait(hdr); err != nil {
|
||||||
|
s.logger.Printf("[WARN] yamux: failed to send ping reply: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a response
|
||||||
|
s.pingLock.Lock()
|
||||||
|
ch := s.pings[pingID]
|
||||||
|
if ch != nil {
|
||||||
|
delete(s.pings, pingID)
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
s.pingLock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleGoAway is invokde for a typeGoAway frame
|
||||||
|
func (s *Session) handleGoAway(hdr header) error {
|
||||||
|
code := hdr.Length()
|
||||||
|
switch code {
|
||||||
|
case goAwayNormal:
|
||||||
|
atomic.SwapInt32(&s.remoteGoAway, 1)
|
||||||
|
case goAwayProtoErr:
|
||||||
|
s.logger.Printf("[ERR] yamux: received protocol error go away")
|
||||||
|
return fmt.Errorf("yamux protocol error")
|
||||||
|
case goAwayInternalErr:
|
||||||
|
s.logger.Printf("[ERR] yamux: received internal error go away")
|
||||||
|
return fmt.Errorf("remote yamux internal error")
|
||||||
|
default:
|
||||||
|
s.logger.Printf("[ERR] yamux: received unexpected go away")
|
||||||
|
return fmt.Errorf("unexpected go away received")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// incomingStream is used to create a new incoming stream
|
||||||
|
func (s *Session) incomingStream(id uint32) error {
|
||||||
|
// Reject immediately if we are doing a go away
|
||||||
|
if atomic.LoadInt32(&s.localGoAway) == 1 {
|
||||||
|
hdr := header(make([]byte, headerSize))
|
||||||
|
hdr.encode(typeWindowUpdate, flagRST, id, 0)
|
||||||
|
return s.sendNoWait(hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a new stream
|
||||||
|
stream := newStream(s, id, streamSYNReceived)
|
||||||
|
|
||||||
|
s.streamLock.Lock()
|
||||||
|
defer s.streamLock.Unlock()
|
||||||
|
|
||||||
|
// Check if stream already exists
|
||||||
|
if _, ok := s.streams[id]; ok {
|
||||||
|
s.logger.Printf("[ERR] yamux: duplicate stream declared")
|
||||||
|
if sendErr := s.sendNoWait(s.goAway(goAwayProtoErr)); sendErr != nil {
|
||||||
|
s.logger.Printf("[WARN] yamux: failed to send go away: %v", sendErr)
|
||||||
|
}
|
||||||
|
return ErrDuplicateStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the stream
|
||||||
|
s.streams[id] = stream
|
||||||
|
|
||||||
|
// Check if we've exceeded the backlog
|
||||||
|
select {
|
||||||
|
case s.acceptCh <- stream:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
// Backlog exceeded! RST the stream
|
||||||
|
s.logger.Printf("[WARN] yamux: backlog exceeded, forcing connection reset")
|
||||||
|
delete(s.streams, id)
|
||||||
|
stream.sendHdr.encode(typeWindowUpdate, flagRST, id, 0)
|
||||||
|
return s.sendNoWait(stream.sendHdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeStream is used to close a stream once both sides have
|
||||||
|
// issued a close. If there was an in-flight SYN and the stream
|
||||||
|
// was not yet established, then this will give the credit back.
|
||||||
|
func (s *Session) closeStream(id uint32) {
|
||||||
|
s.streamLock.Lock()
|
||||||
|
if _, ok := s.inflight[id]; ok {
|
||||||
|
select {
|
||||||
|
case <-s.synCh:
|
||||||
|
default:
|
||||||
|
s.logger.Printf("[ERR] yamux: SYN tracking out of sync")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(s.streams, id)
|
||||||
|
s.streamLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// establishStream is used to mark a stream that was in the
|
||||||
|
// SYN Sent state as established.
|
||||||
|
func (s *Session) establishStream(id uint32) {
|
||||||
|
s.streamLock.Lock()
|
||||||
|
if _, ok := s.inflight[id]; ok {
|
||||||
|
delete(s.inflight, id)
|
||||||
|
} else {
|
||||||
|
s.logger.Printf("[ERR] yamux: established stream without inflight SYN (no tracking entry)")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.synCh:
|
||||||
|
default:
|
||||||
|
s.logger.Printf("[ERR] yamux: established stream without inflight SYN (didn't have semaphore)")
|
||||||
|
}
|
||||||
|
s.streamLock.Unlock()
|
||||||
|
}
|
||||||
1256
vendor/github.com/hashicorp/yamux/session_test.go
generated
vendored
Normal file
1256
vendor/github.com/hashicorp/yamux/session_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
140
vendor/github.com/hashicorp/yamux/spec.md
generated
vendored
Normal file
140
vendor/github.com/hashicorp/yamux/spec.md
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Specification
|
||||||
|
|
||||||
|
We use this document to detail the internal specification of Yamux.
|
||||||
|
This is used both as a guide for implementing Yamux, but also for
|
||||||
|
alternative interoperable libraries to be built.
|
||||||
|
|
||||||
|
# Framing
|
||||||
|
|
||||||
|
Yamux uses a streaming connection underneath, but imposes a message
|
||||||
|
framing so that it can be shared between many logical streams. Each
|
||||||
|
frame contains a header like:
|
||||||
|
|
||||||
|
* Version (8 bits)
|
||||||
|
* Type (8 bits)
|
||||||
|
* Flags (16 bits)
|
||||||
|
* StreamID (32 bits)
|
||||||
|
* Length (32 bits)
|
||||||
|
|
||||||
|
This means that each header has a 12 byte overhead.
|
||||||
|
All fields are encoded in network order (big endian).
|
||||||
|
Each field is described below:
|
||||||
|
|
||||||
|
## Version Field
|
||||||
|
|
||||||
|
The version field is used for future backward compatibility. At the
|
||||||
|
current time, the field is always set to 0, to indicate the initial
|
||||||
|
version.
|
||||||
|
|
||||||
|
## Type Field
|
||||||
|
|
||||||
|
The type field is used to switch the frame message type. The following
|
||||||
|
message types are supported:
|
||||||
|
|
||||||
|
* 0x0 Data - Used to transmit data. May transmit zero length payloads
|
||||||
|
depending on the flags.
|
||||||
|
|
||||||
|
* 0x1 Window Update - Used to updated the senders receive window size.
|
||||||
|
This is used to implement per-session flow control.
|
||||||
|
|
||||||
|
* 0x2 Ping - Used to measure RTT. It can also be used to heart-beat
|
||||||
|
and do keep-alives over TCP.
|
||||||
|
|
||||||
|
* 0x3 Go Away - Used to close a session.
|
||||||
|
|
||||||
|
## Flag Field
|
||||||
|
|
||||||
|
The flags field is used to provide additional information related
|
||||||
|
to the message type. The following flags are supported:
|
||||||
|
|
||||||
|
* 0x1 SYN - Signals the start of a new stream. May be sent with a data or
|
||||||
|
window update message. Also sent with a ping to indicate outbound.
|
||||||
|
|
||||||
|
* 0x2 ACK - Acknowledges the start of a new stream. May be sent with a data
|
||||||
|
or window update message. Also sent with a ping to indicate response.
|
||||||
|
|
||||||
|
* 0x4 FIN - Performs a half-close of a stream. May be sent with a data
|
||||||
|
message or window update.
|
||||||
|
|
||||||
|
* 0x8 RST - Reset a stream immediately. May be sent with a data or
|
||||||
|
window update message.
|
||||||
|
|
||||||
|
## StreamID Field
|
||||||
|
|
||||||
|
The StreamID field is used to identify the logical stream the frame
|
||||||
|
is addressing. The client side should use odd ID's, and the server even.
|
||||||
|
This prevents any collisions. Additionally, the 0 ID is reserved to represent
|
||||||
|
the session.
|
||||||
|
|
||||||
|
Both Ping and Go Away messages should always use the 0 StreamID.
|
||||||
|
|
||||||
|
## Length Field
|
||||||
|
|
||||||
|
The meaning of the length field depends on the message type:
|
||||||
|
|
||||||
|
* Data - provides the length of bytes following the header
|
||||||
|
* Window update - provides a delta update to the window size
|
||||||
|
* Ping - Contains an opaque value, echoed back
|
||||||
|
* Go Away - Contains an error code
|
||||||
|
|
||||||
|
# Message Flow
|
||||||
|
|
||||||
|
There is no explicit connection setup, as Yamux relies on an underlying
|
||||||
|
transport to be provided. However, there is a distinction between client
|
||||||
|
and server side of the connection.
|
||||||
|
|
||||||
|
## Opening a stream
|
||||||
|
|
||||||
|
To open a stream, an initial data or window update frame is sent
|
||||||
|
with a new StreamID. The SYN flag should be set to signal a new stream.
|
||||||
|
|
||||||
|
The receiver must then reply with either a data or window update frame
|
||||||
|
with the StreamID along with the ACK flag to accept the stream or with
|
||||||
|
the RST flag to reject the stream.
|
||||||
|
|
||||||
|
Because we are relying on the reliable stream underneath, a connection
|
||||||
|
can begin sending data once the SYN flag is sent. The corresponding
|
||||||
|
ACK does not need to be received. This is particularly well suited
|
||||||
|
for an RPC system where a client wants to open a stream and immediately
|
||||||
|
fire a request without waiting for the RTT of the ACK.
|
||||||
|
|
||||||
|
This does introduce the possibility of a connection being rejected
|
||||||
|
after data has been sent already. This is a slight semantic difference
|
||||||
|
from TCP, where the conection cannot be refused after it is opened.
|
||||||
|
Clients should be prepared to handle this by checking for an error
|
||||||
|
that indicates a RST was received.
|
||||||
|
|
||||||
|
## Closing a stream
|
||||||
|
|
||||||
|
To close a stream, either side sends a data or window update frame
|
||||||
|
along with the FIN flag. This does a half-close indicating the sender
|
||||||
|
will send no further data.
|
||||||
|
|
||||||
|
Once both sides have closed the connection, the stream is closed.
|
||||||
|
|
||||||
|
Alternatively, if an error occurs, the RST flag can be used to
|
||||||
|
hard close a stream immediately.
|
||||||
|
|
||||||
|
## Flow Control
|
||||||
|
|
||||||
|
When Yamux is initially starts each stream with a 256KB window size.
|
||||||
|
There is no window size for the session.
|
||||||
|
|
||||||
|
To prevent the streams from stalling, window update frames should be
|
||||||
|
sent regularly. Yamux can be configured to provide a larger limit for
|
||||||
|
windows sizes. Both sides assume the initial 256KB window, but can
|
||||||
|
immediately send a window update as part of the SYN/ACK indicating a
|
||||||
|
larger window.
|
||||||
|
|
||||||
|
Both sides should track the number of bytes sent in Data frames
|
||||||
|
only, as only they are tracked as part of the window size.
|
||||||
|
|
||||||
|
## Session termination
|
||||||
|
|
||||||
|
When a session is being terminated, the Go Away message should
|
||||||
|
be sent. The Length should be set to one of the following to
|
||||||
|
provide an error code:
|
||||||
|
|
||||||
|
* 0x0 Normal termination
|
||||||
|
* 0x1 Protocol error
|
||||||
|
* 0x2 Internal error
|
||||||
470
vendor/github.com/hashicorp/yamux/stream.go
generated
vendored
Normal file
470
vendor/github.com/hashicorp/yamux/stream.go
generated
vendored
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type streamState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
streamInit streamState = iota
|
||||||
|
streamSYNSent
|
||||||
|
streamSYNReceived
|
||||||
|
streamEstablished
|
||||||
|
streamLocalClose
|
||||||
|
streamRemoteClose
|
||||||
|
streamClosed
|
||||||
|
streamReset
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stream is used to represent a logical stream
|
||||||
|
// within a session.
|
||||||
|
type Stream struct {
|
||||||
|
recvWindow uint32
|
||||||
|
sendWindow uint32
|
||||||
|
|
||||||
|
id uint32
|
||||||
|
session *Session
|
||||||
|
|
||||||
|
state streamState
|
||||||
|
stateLock sync.Mutex
|
||||||
|
|
||||||
|
recvBuf *bytes.Buffer
|
||||||
|
recvLock sync.Mutex
|
||||||
|
|
||||||
|
controlHdr header
|
||||||
|
controlErr chan error
|
||||||
|
controlHdrLock sync.Mutex
|
||||||
|
|
||||||
|
sendHdr header
|
||||||
|
sendErr chan error
|
||||||
|
sendLock sync.Mutex
|
||||||
|
|
||||||
|
recvNotifyCh chan struct{}
|
||||||
|
sendNotifyCh chan struct{}
|
||||||
|
|
||||||
|
readDeadline atomic.Value // time.Time
|
||||||
|
writeDeadline atomic.Value // time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// newStream is used to construct a new stream within
|
||||||
|
// a given session for an ID
|
||||||
|
func newStream(session *Session, id uint32, state streamState) *Stream {
|
||||||
|
s := &Stream{
|
||||||
|
id: id,
|
||||||
|
session: session,
|
||||||
|
state: state,
|
||||||
|
controlHdr: header(make([]byte, headerSize)),
|
||||||
|
controlErr: make(chan error, 1),
|
||||||
|
sendHdr: header(make([]byte, headerSize)),
|
||||||
|
sendErr: make(chan error, 1),
|
||||||
|
recvWindow: initialStreamWindow,
|
||||||
|
sendWindow: initialStreamWindow,
|
||||||
|
recvNotifyCh: make(chan struct{}, 1),
|
||||||
|
sendNotifyCh: make(chan struct{}, 1),
|
||||||
|
}
|
||||||
|
s.readDeadline.Store(time.Time{})
|
||||||
|
s.writeDeadline.Store(time.Time{})
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session returns the associated stream session
|
||||||
|
func (s *Stream) Session() *Session {
|
||||||
|
return s.session
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamID returns the ID of this stream
|
||||||
|
func (s *Stream) StreamID() uint32 {
|
||||||
|
return s.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read is used to read from the stream
|
||||||
|
func (s *Stream) Read(b []byte) (n int, err error) {
|
||||||
|
defer asyncNotify(s.recvNotifyCh)
|
||||||
|
START:
|
||||||
|
s.stateLock.Lock()
|
||||||
|
switch s.state {
|
||||||
|
case streamLocalClose:
|
||||||
|
fallthrough
|
||||||
|
case streamRemoteClose:
|
||||||
|
fallthrough
|
||||||
|
case streamClosed:
|
||||||
|
s.recvLock.Lock()
|
||||||
|
if s.recvBuf == nil || s.recvBuf.Len() == 0 {
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
case streamReset:
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
return 0, ErrConnectionReset
|
||||||
|
}
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
|
||||||
|
// If there is no data available, block
|
||||||
|
s.recvLock.Lock()
|
||||||
|
if s.recvBuf == nil || s.recvBuf.Len() == 0 {
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
goto WAIT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read any bytes
|
||||||
|
n, _ = s.recvBuf.Read(b)
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
|
||||||
|
// Send a window update potentially
|
||||||
|
err = s.sendWindowUpdate()
|
||||||
|
return n, err
|
||||||
|
|
||||||
|
WAIT:
|
||||||
|
var timeout <-chan time.Time
|
||||||
|
var timer *time.Timer
|
||||||
|
readDeadline := s.readDeadline.Load().(time.Time)
|
||||||
|
if !readDeadline.IsZero() {
|
||||||
|
delay := readDeadline.Sub(time.Now())
|
||||||
|
timer = time.NewTimer(delay)
|
||||||
|
timeout = timer.C
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.recvNotifyCh:
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
goto START
|
||||||
|
case <-timeout:
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write is used to write to the stream
|
||||||
|
func (s *Stream) Write(b []byte) (n int, err error) {
|
||||||
|
s.sendLock.Lock()
|
||||||
|
defer s.sendLock.Unlock()
|
||||||
|
total := 0
|
||||||
|
for total < len(b) {
|
||||||
|
n, err := s.write(b[total:])
|
||||||
|
total += n
|
||||||
|
if err != nil {
|
||||||
|
return total, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// write is used to write to the stream, may return on
|
||||||
|
// a short write.
|
||||||
|
func (s *Stream) write(b []byte) (n int, err error) {
|
||||||
|
var flags uint16
|
||||||
|
var max uint32
|
||||||
|
var body io.Reader
|
||||||
|
START:
|
||||||
|
s.stateLock.Lock()
|
||||||
|
switch s.state {
|
||||||
|
case streamLocalClose:
|
||||||
|
fallthrough
|
||||||
|
case streamClosed:
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
return 0, ErrStreamClosed
|
||||||
|
case streamReset:
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
return 0, ErrConnectionReset
|
||||||
|
}
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
|
||||||
|
// If there is no data available, block
|
||||||
|
window := atomic.LoadUint32(&s.sendWindow)
|
||||||
|
if window == 0 {
|
||||||
|
goto WAIT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the flags if any
|
||||||
|
flags = s.sendFlags()
|
||||||
|
|
||||||
|
// Send up to our send window
|
||||||
|
max = min(window, uint32(len(b)))
|
||||||
|
body = bytes.NewReader(b[:max])
|
||||||
|
|
||||||
|
// Send the header
|
||||||
|
s.sendHdr.encode(typeData, flags, s.id, max)
|
||||||
|
if err = s.session.waitForSendErr(s.sendHdr, body, s.sendErr); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce our send window
|
||||||
|
atomic.AddUint32(&s.sendWindow, ^uint32(max-1))
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
return int(max), err
|
||||||
|
|
||||||
|
WAIT:
|
||||||
|
var timeout <-chan time.Time
|
||||||
|
writeDeadline := s.writeDeadline.Load().(time.Time)
|
||||||
|
if !writeDeadline.IsZero() {
|
||||||
|
delay := writeDeadline.Sub(time.Now())
|
||||||
|
timeout = time.After(delay)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.sendNotifyCh:
|
||||||
|
goto START
|
||||||
|
case <-timeout:
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendFlags determines any flags that are appropriate
|
||||||
|
// based on the current stream state
|
||||||
|
func (s *Stream) sendFlags() uint16 {
|
||||||
|
s.stateLock.Lock()
|
||||||
|
defer s.stateLock.Unlock()
|
||||||
|
var flags uint16
|
||||||
|
switch s.state {
|
||||||
|
case streamInit:
|
||||||
|
flags |= flagSYN
|
||||||
|
s.state = streamSYNSent
|
||||||
|
case streamSYNReceived:
|
||||||
|
flags |= flagACK
|
||||||
|
s.state = streamEstablished
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendWindowUpdate potentially sends a window update enabling
|
||||||
|
// further writes to take place. Must be invoked with the lock.
|
||||||
|
func (s *Stream) sendWindowUpdate() error {
|
||||||
|
s.controlHdrLock.Lock()
|
||||||
|
defer s.controlHdrLock.Unlock()
|
||||||
|
|
||||||
|
// Determine the delta update
|
||||||
|
max := s.session.config.MaxStreamWindowSize
|
||||||
|
var bufLen uint32
|
||||||
|
s.recvLock.Lock()
|
||||||
|
if s.recvBuf != nil {
|
||||||
|
bufLen = uint32(s.recvBuf.Len())
|
||||||
|
}
|
||||||
|
delta := (max - bufLen) - s.recvWindow
|
||||||
|
|
||||||
|
// Determine the flags if any
|
||||||
|
flags := s.sendFlags()
|
||||||
|
|
||||||
|
// Check if we can omit the update
|
||||||
|
if delta < (max/2) && flags == 0 {
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update our window
|
||||||
|
s.recvWindow += delta
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
|
||||||
|
// Send the header
|
||||||
|
s.controlHdr.encode(typeWindowUpdate, flags, s.id, delta)
|
||||||
|
if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendClose is used to send a FIN
|
||||||
|
func (s *Stream) sendClose() error {
|
||||||
|
s.controlHdrLock.Lock()
|
||||||
|
defer s.controlHdrLock.Unlock()
|
||||||
|
|
||||||
|
flags := s.sendFlags()
|
||||||
|
flags |= flagFIN
|
||||||
|
s.controlHdr.encode(typeWindowUpdate, flags, s.id, 0)
|
||||||
|
if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is used to close the stream
|
||||||
|
func (s *Stream) Close() error {
|
||||||
|
closeStream := false
|
||||||
|
s.stateLock.Lock()
|
||||||
|
switch s.state {
|
||||||
|
// Opened means we need to signal a close
|
||||||
|
case streamSYNSent:
|
||||||
|
fallthrough
|
||||||
|
case streamSYNReceived:
|
||||||
|
fallthrough
|
||||||
|
case streamEstablished:
|
||||||
|
s.state = streamLocalClose
|
||||||
|
goto SEND_CLOSE
|
||||||
|
|
||||||
|
case streamLocalClose:
|
||||||
|
case streamRemoteClose:
|
||||||
|
s.state = streamClosed
|
||||||
|
closeStream = true
|
||||||
|
goto SEND_CLOSE
|
||||||
|
|
||||||
|
case streamClosed:
|
||||||
|
case streamReset:
|
||||||
|
default:
|
||||||
|
panic("unhandled state")
|
||||||
|
}
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
return nil
|
||||||
|
SEND_CLOSE:
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
s.sendClose()
|
||||||
|
s.notifyWaiting()
|
||||||
|
if closeStream {
|
||||||
|
s.session.closeStream(s.id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// forceClose is used for when the session is exiting
|
||||||
|
func (s *Stream) forceClose() {
|
||||||
|
s.stateLock.Lock()
|
||||||
|
s.state = streamClosed
|
||||||
|
s.stateLock.Unlock()
|
||||||
|
s.notifyWaiting()
|
||||||
|
}
|
||||||
|
|
||||||
|
// processFlags is used to update the state of the stream
|
||||||
|
// based on set flags, if any. Lock must be held
|
||||||
|
func (s *Stream) processFlags(flags uint16) error {
|
||||||
|
// Close the stream without holding the state lock
|
||||||
|
closeStream := false
|
||||||
|
defer func() {
|
||||||
|
if closeStream {
|
||||||
|
s.session.closeStream(s.id)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.stateLock.Lock()
|
||||||
|
defer s.stateLock.Unlock()
|
||||||
|
if flags&flagACK == flagACK {
|
||||||
|
if s.state == streamSYNSent {
|
||||||
|
s.state = streamEstablished
|
||||||
|
}
|
||||||
|
s.session.establishStream(s.id)
|
||||||
|
}
|
||||||
|
if flags&flagFIN == flagFIN {
|
||||||
|
switch s.state {
|
||||||
|
case streamSYNSent:
|
||||||
|
fallthrough
|
||||||
|
case streamSYNReceived:
|
||||||
|
fallthrough
|
||||||
|
case streamEstablished:
|
||||||
|
s.state = streamRemoteClose
|
||||||
|
s.notifyWaiting()
|
||||||
|
case streamLocalClose:
|
||||||
|
s.state = streamClosed
|
||||||
|
closeStream = true
|
||||||
|
s.notifyWaiting()
|
||||||
|
default:
|
||||||
|
s.session.logger.Printf("[ERR] yamux: unexpected FIN flag in state %d", s.state)
|
||||||
|
return ErrUnexpectedFlag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if flags&flagRST == flagRST {
|
||||||
|
s.state = streamReset
|
||||||
|
closeStream = true
|
||||||
|
s.notifyWaiting()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifyWaiting notifies all the waiting channels
|
||||||
|
func (s *Stream) notifyWaiting() {
|
||||||
|
asyncNotify(s.recvNotifyCh)
|
||||||
|
asyncNotify(s.sendNotifyCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// incrSendWindow updates the size of our send window
|
||||||
|
func (s *Stream) incrSendWindow(hdr header, flags uint16) error {
|
||||||
|
if err := s.processFlags(flags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase window, unblock a sender
|
||||||
|
atomic.AddUint32(&s.sendWindow, hdr.Length())
|
||||||
|
asyncNotify(s.sendNotifyCh)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readData is used to handle a data frame
|
||||||
|
func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
|
||||||
|
if err := s.processFlags(flags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that our recv window is not exceeded
|
||||||
|
length := hdr.Length()
|
||||||
|
if length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap in a limited reader
|
||||||
|
conn = &io.LimitedReader{R: conn, N: int64(length)}
|
||||||
|
|
||||||
|
// Copy into buffer
|
||||||
|
s.recvLock.Lock()
|
||||||
|
|
||||||
|
if length > s.recvWindow {
|
||||||
|
s.session.logger.Printf("[ERR] yamux: receive window exceeded (stream: %d, remain: %d, recv: %d)", s.id, s.recvWindow, length)
|
||||||
|
return ErrRecvWindowExceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.recvBuf == nil {
|
||||||
|
// Allocate the receive buffer just-in-time to fit the full data frame.
|
||||||
|
// This way we can read in the whole packet without further allocations.
|
||||||
|
s.recvBuf = bytes.NewBuffer(make([]byte, 0, length))
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(s.recvBuf, conn); err != nil {
|
||||||
|
s.session.logger.Printf("[ERR] yamux: Failed to read stream data: %v", err)
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement the receive window
|
||||||
|
s.recvWindow -= length
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
|
||||||
|
// Unblock any readers
|
||||||
|
asyncNotify(s.recvNotifyCh)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline sets the read and write deadlines
|
||||||
|
func (s *Stream) SetDeadline(t time.Time) error {
|
||||||
|
if err := s.SetReadDeadline(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.SetWriteDeadline(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets the deadline for future Read calls.
|
||||||
|
func (s *Stream) SetReadDeadline(t time.Time) error {
|
||||||
|
s.readDeadline.Store(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline sets the deadline for future Write calls
|
||||||
|
func (s *Stream) SetWriteDeadline(t time.Time) error {
|
||||||
|
s.writeDeadline.Store(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink is used to compact the amount of buffers utilized
|
||||||
|
// This is useful when using Yamux in a connection pool to reduce
|
||||||
|
// the idle memory utilization.
|
||||||
|
func (s *Stream) Shrink() {
|
||||||
|
s.recvLock.Lock()
|
||||||
|
if s.recvBuf != nil && s.recvBuf.Len() == 0 {
|
||||||
|
s.recvBuf = nil
|
||||||
|
}
|
||||||
|
s.recvLock.Unlock()
|
||||||
|
}
|
||||||
43
vendor/github.com/hashicorp/yamux/util.go
generated
vendored
Normal file
43
vendor/github.com/hashicorp/yamux/util.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timerPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
timer := time.NewTimer(time.Hour * 1e6)
|
||||||
|
timer.Stop()
|
||||||
|
return timer
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// asyncSendErr is used to try an async send of an error
|
||||||
|
func asyncSendErr(ch chan error, err error) {
|
||||||
|
if ch == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case ch <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncNotify is used to signal a waiting goroutine
|
||||||
|
func asyncNotify(ch chan struct{}) {
|
||||||
|
select {
|
||||||
|
case ch <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// min computes the minimum of two values
|
||||||
|
func min(a, b uint32) uint32 {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
50
vendor/github.com/hashicorp/yamux/util_test.go
generated
vendored
Normal file
50
vendor/github.com/hashicorp/yamux/util_test.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAsyncSendErr(t *testing.T) {
|
||||||
|
ch := make(chan error)
|
||||||
|
asyncSendErr(ch, ErrTimeout)
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
t.Fatalf("should not get")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = make(chan error, 1)
|
||||||
|
asyncSendErr(ch, ErrTimeout)
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
default:
|
||||||
|
t.Fatalf("should get")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsyncNotify(t *testing.T) {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
asyncNotify(ch)
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
t.Fatalf("should not get")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = make(chan struct{}, 1)
|
||||||
|
asyncNotify(ch)
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
default:
|
||||||
|
t.Fatalf("should get")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMin(t *testing.T) {
|
||||||
|
if min(1, 2) != 1 {
|
||||||
|
t.Fatalf("bad")
|
||||||
|
}
|
||||||
|
if min(2, 1) != 1 {
|
||||||
|
t.Fatalf("bad")
|
||||||
|
}
|
||||||
|
}
|
||||||
13
vendor/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
Normal file
13
vendor/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2014 Alan Shreve
|
||||||
|
|
||||||
|
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.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user