mirror of
https://github.com/fatedier/frp.git
synced 2026-03-09 11:29:11 +08:00
Compare commits
33 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 | ||
|
|
1325c59a4c | ||
|
|
82dc1e924f | ||
|
|
3166bdf3f0 | ||
|
|
8af70c8822 | ||
|
|
87763e8251 | ||
|
|
e9241aeb94 | ||
|
|
2eaf134042 | ||
|
|
1739e012d6 | ||
|
|
9e8980429f | ||
|
|
1d0865ca49 | ||
|
|
5c9909aeef |
@@ -2,8 +2,7 @@ sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
|
||||
install:
|
||||
- make
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.8
|
||||
FROM golang:1.10
|
||||
|
||||
COPY . /go/src/github.com/fatedier/frp
|
||||
|
||||
|
||||
9
Makefile
9
Makefile
@@ -15,12 +15,7 @@ file:
|
||||
go generate ./assets/...
|
||||
|
||||
fmt:
|
||||
go fmt ./assets/...
|
||||
go fmt ./client/...
|
||||
go fmt ./cmd/...
|
||||
go fmt ./models/...
|
||||
go fmt ./server/...
|
||||
go fmt ./utils/...
|
||||
go fmt ./...
|
||||
|
||||
frps:
|
||||
go build -o bin/frps ./cmd/frps
|
||||
@@ -44,7 +39,7 @@ ci:
|
||||
go test -v ./tests/...
|
||||
cd ./tests && ./clean_test.sh && cd -
|
||||
|
||||
ciclean:
|
||||
cic:
|
||||
cd ./tests && ./clean_test.sh && cd -
|
||||
|
||||
alltest: gotest ci
|
||||
|
||||
@@ -9,6 +9,10 @@ build: app
|
||||
app:
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_darwin_amd64 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_darwin_amd64 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_386 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_386 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_amd64 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_amd64 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_386 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_386 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_amd64 ./cmd/frpc
|
||||
@@ -23,10 +27,10 @@ app:
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips64le ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64le ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
|
||||
|
||||
temp:
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_amd64 ./cmd/frps
|
||||
|
||||
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)
|
||||
* [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
|
||||
* [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)
|
||||
* [Support KCP Protocol](#support-kcp-protocol)
|
||||
* [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)
|
||||
* [URL routing](#url-routing)
|
||||
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
|
||||
* [Range ports mapping](#range-ports-mapping)
|
||||
* [Plugin](#plugin)
|
||||
* [Development Plan](#development-plan)
|
||||
* [Contributing](#contributing)
|
||||
@@ -383,7 +383,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
|
||||
|
||||
### Authentication
|
||||
|
||||
Since v0.10.0, you only need to set `privilege_token` in frps.ini and frpc.ini.
|
||||
Since v0.10.0, you only need to set `token` in frps.ini and frpc.ini.
|
||||
|
||||
Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
|
||||
|
||||
@@ -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.
|
||||
|
||||
### 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.
|
||||
|
||||
#### Port White List
|
||||
|
||||
`privilege_allow_ports` in frps.ini is used for preventing abuse of ports:
|
||||
`allow_ports` in frps.ini is used for preventing abuse of ports:
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
[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
|
||||
|
||||
@@ -539,7 +535,7 @@ type = http
|
||||
local_port = 80
|
||||
custom_domains = test.yourdomain.com
|
||||
http_user = abc
|
||||
http_pwd = abc
|
||||
http_passwd = abc
|
||||
```
|
||||
|
||||
Visit `http://test.yourdomain.com` and now you need to input username and password.
|
||||
|
||||
17
README_zh.md
17
README_zh.md
@@ -29,8 +29,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
||||
* [加密与压缩](#加密与压缩)
|
||||
* [客户端热加载配置文件](#客户端热加载配置文件)
|
||||
* [客户端查看代理状态](#客户端查看代理状态)
|
||||
* [特权模式](#特权模式)
|
||||
* [端口白名单](#端口白名单)
|
||||
* [端口白名单](#端口白名单)
|
||||
* [TCP 多路复用](#tcp-多路复用)
|
||||
* [底层通信可选 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 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
|
||||
|
||||
@@ -450,21 +449,17 @@ admin_port = 7400
|
||||
|
||||
frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。
|
||||
|
||||
### 特权模式
|
||||
### 端口白名单
|
||||
|
||||
由于从 v0.10.0 版本开始,所有 proxy 都在客户端配置,原先的特权模式是目前唯一支持的模式。
|
||||
|
||||
#### 端口白名单
|
||||
|
||||
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 privilege_allow_ports 来指定:
|
||||
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 `allow_ports` 来指定:
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
[common]
|
||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
```
|
||||
|
||||
privilege_allow_ports 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
||||
`allow_ports` 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
||||
|
||||
### TCP 多路复用
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
@@ -35,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
|
||||
// url router
|
||||
router := httprouter.New()
|
||||
|
||||
user, passwd := config.ClientCommonCfg.AdminUser, config.ClientCommonCfg.AdminPwd
|
||||
user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd
|
||||
|
||||
// api, see dashboard_api.go
|
||||
router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))
|
||||
|
||||
@@ -17,6 +17,7 @@ package client
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
)
|
||||
@@ -51,15 +53,16 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
||||
|
||||
log.Info("Http request: [/api/reload]")
|
||||
|
||||
conf, err := ini.LoadFile(config.ClientCommonCfg.ConfigFile)
|
||||
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile)
|
||||
if err != nil {
|
||||
res.Code = 1
|
||||
res.Msg = err.Error()
|
||||
log.Error("reload frpc config file error: %v", err)
|
||||
return
|
||||
}
|
||||
content := string(b)
|
||||
|
||||
newCommonCfg, err := config.LoadClientCommonConf(conf)
|
||||
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
|
||||
if err != nil {
|
||||
res.Code = 2
|
||||
res.Msg = err.Error()
|
||||
@@ -67,7 +70,15 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
||||
return
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, newCommonCfg.Start)
|
||||
conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile)
|
||||
if err != nil {
|
||||
res.Code = 1
|
||||
res.Msg = err.Error()
|
||||
log.Error("reload frpc config file error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start)
|
||||
if err != nil {
|
||||
res.Code = 3
|
||||
res.Msg = err.Error()
|
||||
@@ -125,18 +136,18 @@ func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp {
|
||||
}
|
||||
psr.Plugin = cfg.Plugin
|
||||
if status.Err != "" {
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
|
||||
} else {
|
||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
||||
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||
}
|
||||
case *config.UdpProxyConf:
|
||||
if cfg.LocalPort != 0 {
|
||||
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
|
||||
}
|
||||
if status.Err != "" {
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
|
||||
} else {
|
||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
||||
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||
}
|
||||
case *config.HttpProxyConf:
|
||||
if cfg.LocalPort != 0 {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/crypto"
|
||||
@@ -29,7 +30,8 @@ import (
|
||||
"github.com/fatedier/frp/utils/shutdown"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/xtaci/smux"
|
||||
|
||||
fmux "github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -49,7 +51,7 @@ type Control struct {
|
||||
conn frpNet.Conn
|
||||
|
||||
// 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
|
||||
sendCh chan (msg.Message)
|
||||
@@ -82,8 +84,8 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
|
||||
loginMsg := &msg.Login{
|
||||
Arch: runtime.GOARCH,
|
||||
Os: runtime.GOOS,
|
||||
PoolCount: config.ClientCommonCfg.PoolCount,
|
||||
User: config.ClientCommonCfg.User,
|
||||
PoolCount: g.GlbClientCfg.PoolCount,
|
||||
User: g.GlbClientCfg.User,
|
||||
Version: version.Full(),
|
||||
}
|
||||
ctl := &Control{
|
||||
@@ -110,7 +112,7 @@ func (ctl *Control) Run() (err error) {
|
||||
|
||||
// if login_fail_exit is true, just exit this program
|
||||
// otherwise sleep a while and continues relogin to server
|
||||
if config.ClientCommonCfg.LoginFailExit {
|
||||
if g.GlbClientCfg.LoginFailExit {
|
||||
return
|
||||
} else {
|
||||
time.Sleep(10 * time.Second)
|
||||
@@ -183,8 +185,8 @@ func (ctl *Control) login() (err error) {
|
||||
ctl.session.Close()
|
||||
}
|
||||
|
||||
conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
||||
conn, err := frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,8 +197,8 @@ func (ctl *Control) login() (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
if config.ClientCommonCfg.TcpMux {
|
||||
session, errRet := smux.Client(conn, nil)
|
||||
if g.GlbClientCfg.TcpMux {
|
||||
session, errRet := fmux.Client(conn, nil)
|
||||
if errRet != nil {
|
||||
return errRet
|
||||
}
|
||||
@@ -210,7 +212,7 @@ func (ctl *Control) login() (err error) {
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
|
||||
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(g.GlbClientCfg.Token, now)
|
||||
ctl.loginMsg.Timestamp = now
|
||||
ctl.loginMsg.RunId = ctl.runId
|
||||
|
||||
@@ -234,7 +236,7 @@ func (ctl *Control) login() (err error) {
|
||||
ctl.conn = conn
|
||||
// update runId got from server
|
||||
ctl.runId = loginRespMsg.RunId
|
||||
config.ClientCommonCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
||||
g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
||||
ctl.ClearLogPrefix()
|
||||
ctl.AddLogPrefix(loginRespMsg.RunId)
|
||||
ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
|
||||
@@ -242,7 +244,7 @@ func (ctl *Control) login() (err error) {
|
||||
}
|
||||
|
||||
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
||||
if config.ClientCommonCfg.TcpMux {
|
||||
if g.GlbClientCfg.TcpMux {
|
||||
stream, errRet := ctl.session.OpenStream()
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
@@ -251,8 +253,8 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
||||
}
|
||||
conn = frpNet.WrapConn(stream)
|
||||
} else {
|
||||
conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
||||
conn, err = frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||
if err != nil {
|
||||
ctl.Warn("start new connection to server error: %v", err)
|
||||
return
|
||||
@@ -271,7 +273,7 @@ func (ctl *Control) reader() {
|
||||
defer ctl.readerShutdown.Done()
|
||||
defer close(ctl.closedCh)
|
||||
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||
for {
|
||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -290,7 +292,7 @@ func (ctl *Control) reader() {
|
||||
// writer writes messages got from sendCh to frps
|
||||
func (ctl *Control) writer() {
|
||||
defer ctl.writerShutdown.Done()
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||
if err != nil {
|
||||
ctl.conn.Error("crypto new writer error: %v", err)
|
||||
ctl.conn.Close()
|
||||
@@ -318,7 +320,7 @@ func (ctl *Control) msgHandler() {
|
||||
}()
|
||||
defer ctl.msgHandlerShutdown.Done()
|
||||
|
||||
hbSend := time.NewTicker(time.Duration(config.ClientCommonCfg.HeartBeatInterval) * time.Second)
|
||||
hbSend := time.NewTicker(time.Duration(g.GlbClientCfg.HeartBeatInterval) * time.Second)
|
||||
defer hbSend.Stop()
|
||||
hbCheck := time.NewTicker(time.Second)
|
||||
defer hbCheck.Stop()
|
||||
@@ -332,7 +334,7 @@ func (ctl *Control) msgHandler() {
|
||||
ctl.Debug("send heartbeat to server")
|
||||
ctl.sendCh <- &msg.Ping{}
|
||||
case <-hbCheck.C:
|
||||
if time.Since(ctl.lastPong) > time.Duration(config.ClientCommonCfg.HeartBeatTimeout)*time.Second {
|
||||
if time.Since(ctl.lastPong) > time.Duration(g.GlbClientCfg.HeartBeatTimeout)*time.Second {
|
||||
ctl.Warn("heartbeat timeout")
|
||||
// let reader() stop
|
||||
ctl.conn.Close()
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/models/plugin"
|
||||
@@ -46,7 +47,7 @@ type Proxy interface {
|
||||
|
||||
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
||||
baseProxy := BaseProxy{
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||
}
|
||||
switch cfg := pxyConf.(type) {
|
||||
case *config.TcpProxyConf:
|
||||
@@ -115,7 +116,7 @@ func (pxy *TcpProxy) Close() {
|
||||
|
||||
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// HTTP
|
||||
@@ -144,7 +145,7 @@ func (pxy *HttpProxy) Close() {
|
||||
|
||||
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// HTTPS
|
||||
@@ -173,7 +174,7 @@ func (pxy *HttpsProxy) Close() {
|
||||
|
||||
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// STCP
|
||||
@@ -202,7 +203,7 @@ func (pxy *StcpProxy) Close() {
|
||||
|
||||
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// XTCP
|
||||
@@ -243,7 +244,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
Sid: natHoleSidMsg.Sid,
|
||||
}
|
||||
raddr, _ := net.ResolveUDPAddr("udp",
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
|
||||
clientConn, err := net.DialUDP("udp", nil, raddr)
|
||||
defer clientConn.Close()
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ type ProxyStatus struct {
|
||||
|
||||
func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
|
||||
return &ProxyWrapper{
|
||||
Name: cfg.GetName(),
|
||||
Type: cfg.GetType(),
|
||||
Name: cfg.GetBaseInfo().ProxyName,
|
||||
Type: cfg.GetBaseInfo().ProxyType,
|
||||
Status: ProxyStatusNew,
|
||||
Cfg: cfg,
|
||||
pxy: nil,
|
||||
@@ -227,7 +227,7 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
|
||||
for _, s := range pxyStatus {
|
||||
if status == s {
|
||||
var newProxyMsg msg.NewProxy
|
||||
pxy.Cfg.UnMarshalToMsg(&newProxyMsg)
|
||||
pxy.Cfg.MarshalToMsg(&newProxyMsg)
|
||||
err := pm.sendMsg(&newProxyMsg)
|
||||
if err != nil {
|
||||
pm.Warn("[%s] proxy send NewProxy message error")
|
||||
@@ -240,15 +240,16 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
|
||||
}
|
||||
|
||||
for _, cfg := range pm.visitorCfgs {
|
||||
if _, exist := pm.visitors[cfg.GetName()]; !exist {
|
||||
pm.Info("try to start visitor [%s]", cfg.GetName())
|
||||
name := cfg.GetBaseInfo().ProxyName
|
||||
if _, exist := pm.visitors[name]; !exist {
|
||||
pm.Info("try to start visitor [%s]", name)
|
||||
visitor := NewVisitor(pm.ctl, cfg)
|
||||
err := visitor.Run()
|
||||
if err != nil {
|
||||
visitor.Warn("start error: %v", err)
|
||||
continue
|
||||
}
|
||||
pm.visitors[cfg.GetName()] = visitor
|
||||
pm.visitors[name] = visitor
|
||||
visitor.Info("start visitor success")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
)
|
||||
@@ -41,12 +42,12 @@ func (svr *Service) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.ClientCommonCfg.AdminPort != 0 {
|
||||
err = svr.RunAdminServer(config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
||||
if g.GlbClientCfg.AdminPort != 0 {
|
||||
err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||
if err != nil {
|
||||
log.Warn("run admin server error: %v", err)
|
||||
}
|
||||
log.Info("admin server listen on %s:%d", config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
||||
log.Info("admin server listen on %s:%d", g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||
}
|
||||
|
||||
<-svr.closedCh
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
@@ -45,7 +46,7 @@ type Visitor interface {
|
||||
func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
|
||||
baseVisitor := BaseVisitor{
|
||||
ctl: ctl,
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||
}
|
||||
switch cfg := pxyConf.(type) {
|
||||
case *config.StcpProxyConf:
|
||||
@@ -193,13 +194,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
|
||||
defer userConn.Close()
|
||||
|
||||
sv.Debug("get a new xtcp user connection")
|
||||
if config.ClientCommonCfg.ServerUdpPort == 0 {
|
||||
if g.GlbClientCfg.ServerUdpPort == 0 {
|
||||
sv.Error("xtcp is not supported by server")
|
||||
return
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp",
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
|
||||
visitorConn, err := net.DialUDP("udp", nil, raddr)
|
||||
defer visitorConn.Close()
|
||||
|
||||
|
||||
286
cmd/frpc/main.go
286
cmd/frpc/main.go
@@ -15,291 +15,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
docopt "github.com/docopt/docopt-go"
|
||||
"github.com/rodaine/table"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string = "./frpc.ini"
|
||||
)
|
||||
|
||||
var usage string = `frpc is the client of frp
|
||||
|
||||
Usage:
|
||||
frpc [-c config_file] [-L log_file] [--log-level=<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() {
|
||||
var err error
|
||||
confFile := "./frpc.ini"
|
||||
// the configures parsed from file will be replaced by those from command line if exist
|
||||
args, err := docopt.Parse(usage, nil, true, version.Full(), false)
|
||||
|
||||
if args["-c"] != nil {
|
||||
confFile = args["-c"].(string)
|
||||
}
|
||||
|
||||
conf, err := ini.LoadFile(confFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config.ClientCommonCfg, err = config.LoadClientCommonConf(conf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ClientCommonCfg.ConfigFile = confFile
|
||||
|
||||
// check if reload command
|
||||
if args["reload"] != nil {
|
||||
if args["reload"].(bool) {
|
||||
if err = CmdReload(); err != nil {
|
||||
fmt.Printf("frps reload error: %v\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("reload success\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if status command
|
||||
if args["status"] != nil {
|
||||
if args["status"].(bool) {
|
||||
if err = CmdStatus(); err != nil {
|
||||
fmt.Printf("frpc get status error: %v\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args["-L"] != nil {
|
||||
if args["-L"].(string) == "console" {
|
||||
config.ClientCommonCfg.LogWay = "console"
|
||||
} else {
|
||||
config.ClientCommonCfg.LogWay = "file"
|
||||
config.ClientCommonCfg.LogFile = args["-L"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
if args["--log-level"] != nil {
|
||||
config.ClientCommonCfg.LogLevel = args["--log-level"].(string)
|
||||
}
|
||||
|
||||
if args["--server-addr"] != nil {
|
||||
addr := strings.Split(args["--server-addr"].(string), ":")
|
||||
if len(addr) != 2 {
|
||||
fmt.Println("--server-addr format error: example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
serverPort, err := strconv.ParseInt(addr[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println("--server-addr format error, example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ClientCommonCfg.ServerAddr = addr[0]
|
||||
config.ClientCommonCfg.ServerPort = int(serverPort)
|
||||
}
|
||||
|
||||
if args["-v"] != nil {
|
||||
if args["-v"].(bool) {
|
||||
fmt.Println(version.Full())
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, config.ClientCommonCfg.Start)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.InitLog(config.ClientCommonCfg.LogWay, config.ClientCommonCfg.LogFile,
|
||||
config.ClientCommonCfg.LogLevel, config.ClientCommonCfg.LogMaxDays)
|
||||
|
||||
svr := client.NewService(pxyCfgs, visitorCfgs)
|
||||
|
||||
// Capture the exit signal if we use kcp.
|
||||
if config.ClientCommonCfg.Protocol == "kcp" {
|
||||
go HandleSignal(svr)
|
||||
}
|
||||
|
||||
err = svr.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSignal(svr *client.Service) {
|
||||
ch := make(chan os.Signal)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-ch
|
||||
svr.Close()
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func CmdReload() error {
|
||||
if config.ClientCommonCfg.AdminPort == 0 {
|
||||
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+
|
||||
config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/reload", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
|
||||
config.ClientCommonCfg.AdminPwd))
|
||||
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := &client.GeneralResponse{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
} else if res.Code != 0 {
|
||||
return fmt.Errorf(res.Msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CmdStatus() error {
|
||||
if config.ClientCommonCfg.AdminPort == 0 {
|
||||
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+
|
||||
config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/status", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
|
||||
config.ClientCommonCfg.AdminPwd))
|
||||
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := &client.StatusResp{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
}
|
||||
|
||||
fmt.Println("Proxy Status...")
|
||||
if len(res.Tcp) > 0 {
|
||||
fmt.Printf("TCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Tcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Udp) > 0 {
|
||||
fmt.Printf("UDP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Udp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Http) > 0 {
|
||||
fmt.Printf("HTTP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Http {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Https) > 0 {
|
||||
fmt.Printf("HTTPS")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Https {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Stcp) > 0 {
|
||||
fmt.Printf("STCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Stcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Xtcp) > 0 {
|
||||
fmt.Printf("XTCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Xtcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
sub.Execute()
|
||||
}
|
||||
|
||||
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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -14,105 +14,6 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
docopt "github.com/docopt/docopt-go"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/server"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
)
|
||||
|
||||
var usage string = `frps is the server of frp
|
||||
|
||||
Usage:
|
||||
frps [-c config_file] [-L log_file] [--log-level=<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() {
|
||||
var err error
|
||||
confFile := "./frps.ini"
|
||||
// the configures parsed from file will be replaced by those from command line if exist
|
||||
args, err := docopt.Parse(usage, nil, true, version.Full(), false)
|
||||
|
||||
if args["-c"] != nil {
|
||||
confFile = args["-c"].(string)
|
||||
}
|
||||
|
||||
conf, err := ini.LoadFile(confFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ServerCommonCfg, err = config.LoadServerCommonConf(conf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if args["-L"] != nil {
|
||||
if args["-L"].(string) == "console" {
|
||||
config.ServerCommonCfg.LogWay = "console"
|
||||
} else {
|
||||
config.ServerCommonCfg.LogWay = "file"
|
||||
config.ServerCommonCfg.LogFile = args["-L"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
if args["--log-level"] != nil {
|
||||
config.ServerCommonCfg.LogLevel = args["--log-level"].(string)
|
||||
}
|
||||
|
||||
if args["--addr"] != nil {
|
||||
addr := strings.Split(args["--addr"].(string), ":")
|
||||
if len(addr) != 2 {
|
||||
fmt.Println("--addr format error: example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
bindPort, err := strconv.ParseInt(addr[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println("--addr format error, example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ServerCommonCfg.BindAddr = addr[0]
|
||||
config.ServerCommonCfg.BindPort = int(bindPort)
|
||||
}
|
||||
|
||||
if args["-v"] != nil {
|
||||
if args["-v"].(bool) {
|
||||
fmt.Println(version.Full())
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
log.InitLog(config.ServerCommonCfg.LogWay, config.ServerCommonCfg.LogFile,
|
||||
config.ServerCommonCfg.LogLevel, config.ServerCommonCfg.LogMaxDays)
|
||||
|
||||
svr, err := server.NewService()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Start frps success")
|
||||
if config.ServerCommonCfg.PrivilegeMode == true {
|
||||
log.Info("PrivilegeMode is enabled, you should pay more attention to security issues")
|
||||
}
|
||||
server.ServerService = svr
|
||||
svr.Run()
|
||||
Execute()
|
||||
}
|
||||
|
||||
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
|
||||
# it only works when protocol is tcp
|
||||
# http_proxy = http://user:pwd@192.168.1.128:8080
|
||||
# http_proxy = http://user:passwd@192.168.1.128:8080
|
||||
|
||||
# console or real logFile path like ./frpc.log
|
||||
log_file = ./frpc.log
|
||||
@@ -18,13 +18,13 @@ log_level = info
|
||||
log_max_days = 3
|
||||
|
||||
# for authentication
|
||||
privilege_token = 12345678
|
||||
token = 12345678
|
||||
|
||||
# set admin address for control frpc's action by http api such as reload
|
||||
admin_addr = 127.0.0.1
|
||||
admin_port = 7400
|
||||
admin_user = admin
|
||||
admin_pwd = admin
|
||||
admin_passwd = admin
|
||||
|
||||
# connections will be established in advance, default value is zero
|
||||
pool_count = 5
|
||||
@@ -43,6 +43,9 @@ login_fail_exit = true
|
||||
# now it supports tcp and kcp, default is 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 ','
|
||||
# default is empty, means all proxies
|
||||
# start = ssh,dns
|
||||
@@ -73,7 +76,7 @@ local_port = 22
|
||||
# if remote_port is 0, frps will assgin a random port for you
|
||||
remote_port = 0
|
||||
|
||||
# if you want tp expose multiple ports, add 'range:' prefix to the section name
|
||||
# if you want to expose multiple ports, add 'range:' prefix to the section name
|
||||
# frpc will generate multiple proxies such as 'tcp_port_6010', 'tcp_port_6011' and so on.
|
||||
[range:tcp_port]
|
||||
type = tcp
|
||||
|
||||
@@ -25,9 +25,9 @@ vhost_https_port = 443
|
||||
dashboard_addr = 0.0.0.0
|
||||
dashboard_port = 7500
|
||||
|
||||
# dashboard user and pwd for basic auth protect, if not set, both default value is admin
|
||||
# dashboard user and passwd for basic auth protect, if not set, both default value is admin
|
||||
dashboard_user = admin
|
||||
dashboard_pwd = admin
|
||||
dashboard_passwd = admin
|
||||
|
||||
# dashboard assets directory(only for debug mode)
|
||||
# assets_dir = ./static
|
||||
@@ -39,15 +39,15 @@ log_level = info
|
||||
|
||||
log_max_days = 3
|
||||
|
||||
# privilege mode is the only supported mode since v0.10.0
|
||||
privilege_token = 12345678
|
||||
# auth token
|
||||
token = 12345678
|
||||
|
||||
# heartbeat configure, it's not recommended to modify the default value
|
||||
# the default value of heartbeat_timeout is 90
|
||||
# heartbeat_timeout = 90
|
||||
|
||||
# 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
|
||||
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
|
||||
updated: 2018-01-23T14:48:38.764359+08:00
|
||||
hash: e2a62cbc49d9da8ff95682f5c0b7731a7047afdd139acddb691c51ea98f726e1
|
||||
updated: 2018-04-25T02:41:38.15698+08:00
|
||||
imports:
|
||||
- name: github.com/armon/go-socks5
|
||||
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
||||
@@ -7,8 +7,6 @@ imports:
|
||||
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/docopt/docopt-go
|
||||
version: 784ddc588536785e7299f7272f39101f7faccc3f
|
||||
- name: github.com/fatedier/beego
|
||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||
subpackages:
|
||||
@@ -18,7 +16,11 @@ imports:
|
||||
- name: github.com/golang/snappy
|
||||
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
||||
- 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
|
||||
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
||||
- name: github.com/klauspost/cpuid
|
||||
@@ -37,6 +39,10 @@ imports:
|
||||
- fs
|
||||
- name: github.com/rodaine/table
|
||||
version: 212a2ad1c462ed4d5b5511ea2b480a573281dbbd
|
||||
- name: github.com/spf13/cobra
|
||||
version: a1f051bc3eba734da4772d60e2d677f47cf93ef4
|
||||
- name: github.com/spf13/pflag
|
||||
version: 583c0c0531f06d5278b7d917446061adc344b5cd
|
||||
- name: github.com/stretchr/testify
|
||||
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
||||
subpackages:
|
||||
@@ -53,10 +59,6 @@ imports:
|
||||
- sm4
|
||||
- name: github.com/vaughan0/go-ini
|
||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||
- name: github.com/xtaci/kcp-go
|
||||
version: df437e2b8ec365a336200f9d9da53441cf72ed47
|
||||
- name: github.com/xtaci/smux
|
||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
||||
- name: golang.org/x/crypto
|
||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||
subpackages:
|
||||
|
||||
10
glide.yaml
10
glide.yaml
@@ -6,8 +6,6 @@ import:
|
||||
version: v1.1.0
|
||||
subpackages:
|
||||
- spew
|
||||
- package: github.com/docopt/docopt-go
|
||||
version: 0.6.2
|
||||
- package: github.com/fatedier/beego
|
||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||
subpackages:
|
||||
@@ -48,10 +46,6 @@ import:
|
||||
- sm4
|
||||
- package: github.com/vaughan0/go-ini
|
||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||
- package: github.com/xtaci/kcp-go
|
||||
version: v3.17
|
||||
- package: github.com/xtaci/smux
|
||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
||||
- package: golang.org/x/crypto
|
||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||
subpackages:
|
||||
@@ -74,3 +68,7 @@ import:
|
||||
- package: github.com/rodaine/table
|
||||
version: v1.0.0
|
||||
- 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"
|
||||
)
|
||||
|
||||
var ClientCommonCfg *ClientCommonConf
|
||||
|
||||
// client common config
|
||||
type ClientCommonConf struct {
|
||||
ConfigFile string
|
||||
ServerAddr string
|
||||
ServerPort int
|
||||
ServerUdpPort int // this is specified by login response message from frps
|
||||
HttpProxy string
|
||||
LogFile string
|
||||
LogWay string
|
||||
LogLevel string
|
||||
LogMaxDays int64
|
||||
PrivilegeToken string
|
||||
AdminAddr string
|
||||
AdminPort int
|
||||
AdminUser string
|
||||
AdminPwd string
|
||||
PoolCount int
|
||||
TcpMux bool
|
||||
User string
|
||||
LoginFailExit bool
|
||||
Start map[string]struct{}
|
||||
Protocol string
|
||||
HeartBeatInterval int64
|
||||
HeartBeatTimeout int64
|
||||
ServerAddr string `json:"server_addr"`
|
||||
ServerPort int `json:"server_port"`
|
||||
HttpProxy string `json:"http_proxy"`
|
||||
LogFile string `json:"log_file"`
|
||||
LogWay string `json:"log_way"`
|
||||
LogLevel string `json:"log_level"`
|
||||
LogMaxDays int64 `json:"log_max_days"`
|
||||
Token string `json:"token"`
|
||||
AdminAddr string `json:"admin_addr"`
|
||||
AdminPort int `json:"admin_port"`
|
||||
AdminUser string `json:"admin_user"`
|
||||
AdminPwd string `json:"admin_pwd"`
|
||||
PoolCount int `json:"pool_count"`
|
||||
TcpMux bool `json:"tcp_mux"`
|
||||
User string `json:"user"`
|
||||
DnsServer string `json:"dns_server"`
|
||||
LoginFailExit bool `json:"login_fail_exit"`
|
||||
Start map[string]struct{} `json:"start"`
|
||||
Protocol string `json:"protocol"`
|
||||
HeartBeatInterval int64 `json:"heartbeat_interval"`
|
||||
HeartBeatTimeout int64 `json:"heartbeat_timeout"`
|
||||
}
|
||||
|
||||
func GetDeaultClientCommonConf() *ClientCommonConf {
|
||||
func GetDefaultClientConf() *ClientCommonConf {
|
||||
return &ClientCommonConf{
|
||||
ConfigFile: "./frpc.ini",
|
||||
ServerAddr: "0.0.0.0",
|
||||
ServerPort: 7000,
|
||||
ServerUdpPort: 0,
|
||||
HttpProxy: "",
|
||||
HttpProxy: os.Getenv("http_proxy"),
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
PrivilegeToken: "",
|
||||
Token: "",
|
||||
AdminAddr: "127.0.0.1",
|
||||
AdminPort: 0,
|
||||
AdminUser: "",
|
||||
@@ -70,6 +65,7 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
||||
PoolCount: 1,
|
||||
TcpMux: true,
|
||||
User: "",
|
||||
DnsServer: "",
|
||||
LoginFailExit: true,
|
||||
Start: make(map[string]struct{}),
|
||||
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 (
|
||||
tmpStr string
|
||||
ok bool
|
||||
v int64
|
||||
)
|
||||
cfg = GetDeaultClientCommonConf()
|
||||
|
||||
tmpStr, ok = conf.Get("common", "server_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "server_addr"); ok {
|
||||
cfg.ServerAddr = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "server_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "server_port"); ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid server_port")
|
||||
@@ -101,16 +104,11 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
cfg.ServerPort = int(v)
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "http_proxy")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
|
||||
cfg.HttpProxy = tmpStr
|
||||
} else {
|
||||
// get http_proxy from env
|
||||
cfg.HttpProxy = os.Getenv("http_proxy")
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_file")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||
cfg.LogFile = tmpStr
|
||||
if cfg.LogFile == "console" {
|
||||
cfg.LogWay = "console"
|
||||
@@ -119,30 +117,25 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_level")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||
cfg.LogLevel = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||
cfg.LogMaxDays = v
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "privilege_token")
|
||||
if ok {
|
||||
cfg.PrivilegeToken = tmpStr
|
||||
if tmpStr, ok = conf.Get("common", "token"); ok {
|
||||
cfg.Token = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
|
||||
cfg.AdminAddr = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||
cfg.AdminPort = int(v)
|
||||
} else {
|
||||
@@ -151,55 +144,48 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_user")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_user"); ok {
|
||||
cfg.AdminUser = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_pwd")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
|
||||
cfg.AdminPwd = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "pool_count")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
cfg.PoolCount = 1
|
||||
} else {
|
||||
if tmpStr, ok = conf.Get("common", "pool_count"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||
cfg.PoolCount = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
||||
if ok && tmpStr == "false" {
|
||||
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||
cfg.TcpMux = false
|
||||
} else {
|
||||
cfg.TcpMux = true
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "user")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "user"); ok {
|
||||
cfg.User = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "start")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dns_server"); ok {
|
||||
cfg.DnsServer = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "start"); ok {
|
||||
proxyNames := strings.Split(tmpStr, ",")
|
||||
for _, name := range proxyNames {
|
||||
cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "login_fail_exit")
|
||||
if ok && tmpStr == "false" {
|
||||
if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
|
||||
cfg.LoginFailExit = false
|
||||
} else {
|
||||
cfg.LoginFailExit = true
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "protocol")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "protocol"); ok {
|
||||
// Now it only support tcp and kcp.
|
||||
if tmpStr != "kcp" {
|
||||
tmpStr = "tcp"
|
||||
@@ -207,10 +193,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
cfg.Protocol = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
||||
return
|
||||
} else {
|
||||
@@ -218,17 +202,18 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "heartbeat_interval")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||
return
|
||||
} else {
|
||||
cfg.HeartBeatInterval = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *ClientCommonConf) Check() (err error) {
|
||||
if cfg.HeartBeatInterval <= 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||
return
|
||||
|
||||
@@ -27,7 +27,9 @@ import (
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
var proxyConfTypeMap map[string]reflect.Type
|
||||
var (
|
||||
proxyConfTypeMap map[string]reflect.Type
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxyConfTypeMap = make(map[string]reflect.Type)
|
||||
@@ -51,17 +53,16 @@ func NewConfByType(proxyType string) ProxyConf {
|
||||
}
|
||||
|
||||
type ProxyConf interface {
|
||||
GetName() string
|
||||
GetType() string
|
||||
GetBaseInfo() *BaseProxyConf
|
||||
LoadFromMsg(pMsg *msg.NewProxy)
|
||||
LoadFromFile(name string, conf ini.Section) error
|
||||
UnMarshalToMsg(pMsg *msg.NewProxy)
|
||||
Check() error
|
||||
UnmarshalFromMsg(pMsg *msg.NewProxy)
|
||||
UnmarshalFromIni(prefix string, name string, conf ini.Section) error
|
||||
MarshalToMsg(pMsg *msg.NewProxy)
|
||||
CheckForCli() error
|
||||
CheckForSvr() error
|
||||
Compare(conf ProxyConf) bool
|
||||
}
|
||||
|
||||
func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||
func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||
if pMsg.ProxyType == "" {
|
||||
pMsg.ProxyType = consts.TcpProxy
|
||||
}
|
||||
@@ -71,12 +72,12 @@ func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||
err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType)
|
||||
return
|
||||
}
|
||||
cfg.LoadFromMsg(pMsg)
|
||||
err = cfg.Check()
|
||||
cfg.UnmarshalFromMsg(pMsg)
|
||||
err = cfg.CheckForSvr()
|
||||
return
|
||||
}
|
||||
|
||||
func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err error) {
|
||||
func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg ProxyConf, err error) {
|
||||
proxyType := section["type"]
|
||||
if proxyType == "" {
|
||||
proxyType = consts.TcpProxy
|
||||
@@ -87,7 +88,10 @@ func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err
|
||||
err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType)
|
||||
return
|
||||
}
|
||||
err = cfg.LoadFromFile(name, section)
|
||||
if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
err = cfg.CheckForCli()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -100,14 +104,6 @@ type BaseProxyConf struct {
|
||||
UseCompression bool `json:"use_compression"`
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) GetName() string {
|
||||
return cfg.ProxyName
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) GetType() string {
|
||||
return cfg.ProxyType
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf {
|
||||
return cfg
|
||||
}
|
||||
@@ -122,23 +118,19 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.ProxyName = pMsg.ProxyName
|
||||
cfg.ProxyType = pMsg.ProxyType
|
||||
cfg.UseEncryption = pMsg.UseEncryption
|
||||
cfg.UseCompression = pMsg.UseCompression
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
||||
func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) error {
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
)
|
||||
if ClientCommonCfg.User != "" {
|
||||
cfg.ProxyName = ClientCommonCfg.User + "." + name
|
||||
} else {
|
||||
cfg.ProxyName = name
|
||||
}
|
||||
cfg.ProxyName = prefix + name
|
||||
cfg.ProxyType = section["type"]
|
||||
|
||||
tmpStr, ok = section["use_encryption"]
|
||||
@@ -153,7 +145,7 @@ func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.ProxyName = cfg.ProxyName
|
||||
pMsg.ProxyType = cfg.ProxyType
|
||||
pMsg.UseEncryption = cfg.UseEncryption
|
||||
@@ -162,24 +154,21 @@ func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
|
||||
// Bind info
|
||||
type BindInfoConf struct {
|
||||
BindAddr string `json:"bind_addr"`
|
||||
RemotePort int `json:"remote_port"`
|
||||
RemotePort int `json:"remote_port"`
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool {
|
||||
if cfg.BindAddr != cmp.BindAddr ||
|
||||
cfg.RemotePort != cmp.RemotePort {
|
||||
if cfg.RemotePort != cmp.RemotePort {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BindAddr = ServerCommonCfg.ProxyBindAddr
|
||||
func (cfg *BindInfoConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.RemotePort = pMsg.RemotePort
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
func (cfg *BindInfoConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
@@ -197,14 +186,10 @@ func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *BindInfoConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.RemotePort = cfg.RemotePort
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) check() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Domain info
|
||||
type DomainConf struct {
|
||||
CustomDomains []string `json:"custom_domains"`
|
||||
@@ -219,12 +204,12 @@ func (cfg *DomainConf) compare(cmp *DomainConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *DomainConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.CustomDomains = pMsg.CustomDomains
|
||||
cfg.SubDomain = pMsg.SubDomain
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
func (cfg *DomainConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
@@ -239,42 +224,60 @@ func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error
|
||||
if tmpStr, ok = section["subdomain"]; ok {
|
||||
cfg.SubDomain = tmpStr
|
||||
}
|
||||
|
||||
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *DomainConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.CustomDomains = cfg.CustomDomains
|
||||
pMsg.SubDomain = cfg.SubDomain
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) check() (err error) {
|
||||
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
||||
err = fmt.Errorf("custom_domains and subdomain should set at least one of them")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) checkForCli() (err error) {
|
||||
if err = cfg.check(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) checkForSvr() (err error) {
|
||||
if err = cfg.check(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, domain := range cfg.CustomDomains {
|
||||
if ServerCommonCfg.SubDomainHost != "" && len(strings.Split(ServerCommonCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||
if strings.Contains(domain, ServerCommonCfg.SubDomainHost) {
|
||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, ServerCommonCfg.SubDomainHost)
|
||||
if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||
if strings.Contains(domain, subDomainHost) {
|
||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.SubDomain != "" {
|
||||
if ServerCommonCfg.SubDomainHost == "" {
|
||||
if subDomainHost == "" {
|
||||
return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
|
||||
}
|
||||
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
||||
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Local service info
|
||||
type LocalSvrConf struct {
|
||||
LocalIp string `json:"-"`
|
||||
LocalPort int `json:"-"`
|
||||
LocalIp string `json:"local_ip"`
|
||||
LocalPort int `json:"local_port"`
|
||||
|
||||
Plugin string `json:"plugin"`
|
||||
PluginParams map[string]string `json:"plugin_params"`
|
||||
}
|
||||
|
||||
func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
||||
@@ -282,30 +285,6 @@ func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
||||
cfg.LocalPort != cmp.LocalPort {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *LocalSvrConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
|
||||
cfg.LocalIp = "127.0.0.1"
|
||||
}
|
||||
|
||||
if tmpStr, ok := section["local_port"]; ok {
|
||||
if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PluginConf struct {
|
||||
Plugin string `json:"-"`
|
||||
PluginParams map[string]string `json:"-"`
|
||||
}
|
||||
|
||||
func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
||||
if cfg.Plugin != cmp.Plugin ||
|
||||
len(cfg.PluginParams) != len(cmp.PluginParams) {
|
||||
return false
|
||||
@@ -319,7 +298,7 @@ func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
cfg.Plugin = section["plugin"]
|
||||
cfg.PluginParams = make(map[string]string)
|
||||
if cfg.Plugin != "" {
|
||||
@@ -330,7 +309,17 @@ func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] no plugin info found", name)
|
||||
if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
|
||||
cfg.LocalIp = "127.0.0.1"
|
||||
}
|
||||
|
||||
if tmpStr, ok := section["local_port"]; ok {
|
||||
if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -341,7 +330,6 @@ type TcpProxyConf struct {
|
||||
BindInfoConf
|
||||
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
@@ -352,43 +340,38 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
||||
func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) Check() (err error) {
|
||||
err = cfg.BindInfoConf.check()
|
||||
return
|
||||
}
|
||||
func (cfg *TcpProxyConf) CheckForCli() error { return nil }
|
||||
|
||||
func (cfg *TcpProxyConf) CheckForSvr() error { return nil }
|
||||
|
||||
// UDP
|
||||
type UdpProxyConf struct {
|
||||
@@ -412,33 +395,32 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
||||
func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) Check() (err error) {
|
||||
err = cfg.BindInfoConf.check()
|
||||
return
|
||||
}
|
||||
func (cfg *UdpProxyConf) CheckForCli() error { return nil }
|
||||
|
||||
func (cfg *UdpProxyConf) CheckForSvr() error { return nil }
|
||||
|
||||
// HTTP
|
||||
type HttpProxyConf struct {
|
||||
@@ -446,12 +428,11 @@ type HttpProxyConf struct {
|
||||
DomainConf
|
||||
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
|
||||
Locations []string `json:"locations"`
|
||||
HostHeaderRewrite string `json:"host_header_rewrite"`
|
||||
HttpUser string `json:"-"`
|
||||
HttpPwd string `json:"-"`
|
||||
HttpUser string `json:"http_user"`
|
||||
HttpPwd string `json:"http_pwd"`
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
@@ -463,7 +444,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
||||
strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
|
||||
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
|
||||
cfg.HttpUser != cmpConf.HttpUser ||
|
||||
@@ -473,9 +453,9 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
||||
func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||
|
||||
cfg.Locations = pMsg.Locations
|
||||
cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
|
||||
@@ -483,17 +463,15 @@ func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.HttpPwd = pMsg.HttpPwd
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -512,9 +490,9 @@ func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||
|
||||
pMsg.Locations = cfg.Locations
|
||||
pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
|
||||
@@ -522,11 +500,20 @@ func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.HttpPwd = cfg.HttpPwd
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) Check() (err error) {
|
||||
if ServerCommonCfg.VhostHttpPort == 0 {
|
||||
return fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
||||
func (cfg *HttpProxyConf) CheckForCli() (err error) {
|
||||
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) CheckForSvr() (err error) {
|
||||
if vhostHttpPort == 0 {
|
||||
err = fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
||||
}
|
||||
if err = cfg.DomainConf.checkForSvr(); err != nil {
|
||||
return
|
||||
}
|
||||
err = cfg.DomainConf.check()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -536,7 +523,6 @@ type HttpsProxyConf struct {
|
||||
DomainConf
|
||||
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
||||
@@ -547,43 +533,49 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
||||
func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) Check() (err error) {
|
||||
if ServerCommonCfg.VhostHttpsPort == 0 {
|
||||
func (cfg *HttpsProxyConf) CheckForCli() (err error) {
|
||||
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) CheckForSvr() (err error) {
|
||||
if vhostHttpsPort == 0 {
|
||||
return fmt.Errorf("type [https] not support when vhost_https_port is not set")
|
||||
}
|
||||
err = cfg.DomainConf.check()
|
||||
if err = cfg.DomainConf.checkForSvr(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -596,7 +588,6 @@ type StcpProxyConf struct {
|
||||
|
||||
// used in role server
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
|
||||
// used in role visitor
|
||||
ServerName string `json:"server_name"`
|
||||
@@ -612,7 +603,6 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
||||
cfg.Role != cmpConf.Role ||
|
||||
cfg.Sk != cmpConf.Sk ||
|
||||
cfg.ServerName != cmpConf.ServerName ||
|
||||
@@ -624,13 +614,13 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
}
|
||||
|
||||
// Only for role server.
|
||||
func (cfg *StcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.Sk = pMsg.Sk
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -661,21 +651,33 @@ func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||
}
|
||||
} else {
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
pMsg.Sk = cfg.Sk
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) Check() (err error) {
|
||||
func (cfg *StcpProxyConf) CheckForCli() (err error) {
|
||||
if cfg.Role != "server" && cfg.Role != "visitor" {
|
||||
err = fmt.Errorf("role should be 'server' or 'visitor'")
|
||||
return
|
||||
}
|
||||
if cfg.Role == "visitor" {
|
||||
if cfg.BindAddr == "" {
|
||||
err = fmt.Errorf("bind_addr shouldn't be empty")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) CheckForSvr() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -688,7 +690,6 @@ type XtcpProxyConf struct {
|
||||
|
||||
// used in role server
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
|
||||
// used in role visitor
|
||||
ServerName string `json:"server_name"`
|
||||
@@ -704,7 +705,6 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
||||
cfg.Role != cmpConf.Role ||
|
||||
cfg.Sk != cmpConf.Sk ||
|
||||
cfg.ServerName != cmpConf.ServerName ||
|
||||
@@ -716,13 +716,13 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
}
|
||||
|
||||
// Only for role server.
|
||||
func (cfg *XtcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.Sk = pMsg.Sk
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -753,21 +753,33 @@ func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||
}
|
||||
} else {
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
pMsg.Sk = cfg.Sk
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) Check() (err error) {
|
||||
func (cfg *XtcpProxyConf) CheckForCli() (err error) {
|
||||
if cfg.Role != "server" && cfg.Role != "visitor" {
|
||||
err = fmt.Errorf("role should be 'server' or 'visitor'")
|
||||
return
|
||||
}
|
||||
if cfg.Role == "visitor" {
|
||||
if cfg.BindAddr == "" {
|
||||
err = fmt.Errorf("bind_addr shouldn't be empty")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) CheckForSvr() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -788,7 +800,7 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in
|
||||
return
|
||||
}
|
||||
if len(localPorts) == 0 {
|
||||
err = fmt.Errorf("Parse conf error: range section [%s] local_port and remote_port is necessary")
|
||||
err = fmt.Errorf("Parse conf error: range section [%s] local_port and remote_port is necessary", name)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -805,7 +817,7 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in
|
||||
|
||||
// if len(startProxy) is 0, start all
|
||||
// otherwise just start proxies in startProxy map
|
||||
func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]struct{}) (
|
||||
func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) (
|
||||
proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) {
|
||||
|
||||
if prefix != "" {
|
||||
@@ -842,9 +854,7 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s
|
||||
}
|
||||
|
||||
for subName, subSection := range subSections {
|
||||
// some proxy or visotr configure may be used this prefix
|
||||
subSection["prefix"] = prefix
|
||||
cfg, err := NewProxyConfFromFile(subName, subSection)
|
||||
cfg, err := NewProxyConfFromIni(prefix, subName, subSection)
|
||||
if err != nil {
|
||||
return proxyConfs, visitorConfs, err
|
||||
}
|
||||
|
||||
@@ -24,94 +24,109 @@ import (
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
)
|
||||
|
||||
var ServerCommonCfg *ServerCommonConf
|
||||
var (
|
||||
// server global configure used for generate proxy conf used in frps
|
||||
proxyBindAddr string
|
||||
subDomainHost string
|
||||
vhostHttpPort int
|
||||
vhostHttpsPort int
|
||||
)
|
||||
|
||||
func InitServerCfg(cfg *ServerCommonConf) {
|
||||
proxyBindAddr = cfg.ProxyBindAddr
|
||||
subDomainHost = cfg.SubDomainHost
|
||||
vhostHttpPort = cfg.VhostHttpPort
|
||||
vhostHttpsPort = cfg.VhostHttpsPort
|
||||
}
|
||||
|
||||
// common config
|
||||
type ServerCommonConf struct {
|
||||
ConfigFile string
|
||||
BindAddr string
|
||||
BindPort int
|
||||
BindUdpPort int
|
||||
KcpBindPort int
|
||||
ProxyBindAddr string
|
||||
BindAddr string `json:"bind_addr"`
|
||||
BindPort int `json:"bind_port"`
|
||||
BindUdpPort int `json:"bind_udp_port"`
|
||||
KcpBindPort int `json:"kcp_bind_port"`
|
||||
ProxyBindAddr string `json:"proxy_bind_addr"`
|
||||
|
||||
// If VhostHttpPort equals 0, don't listen a public port for http protocol.
|
||||
VhostHttpPort int
|
||||
VhostHttpPort int `json:"vhost_http_port"`
|
||||
|
||||
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
||||
VhostHttpsPort int
|
||||
DashboardAddr string
|
||||
VhostHttpsPort int `json:"vhost_http_port"`
|
||||
DashboardAddr string `json:"dashboard_addr"`
|
||||
|
||||
// if DashboardPort equals 0, dashboard is not available
|
||||
DashboardPort int
|
||||
DashboardUser string
|
||||
DashboardPwd string
|
||||
AssetsDir string
|
||||
LogFile string
|
||||
LogWay string // console or file
|
||||
LogLevel string
|
||||
LogMaxDays int64
|
||||
PrivilegeMode bool
|
||||
PrivilegeToken string
|
||||
AuthTimeout int64
|
||||
SubDomainHost string
|
||||
TcpMux bool
|
||||
DashboardPort int `json:"dashboard_port"`
|
||||
DashboardUser string `json:"dashboard_user"`
|
||||
DashboardPwd string `json:"dashboard_pwd"`
|
||||
AssetsDir string `json:"asserts_dir"`
|
||||
LogFile string `json:"log_file"`
|
||||
LogWay string `json:"log_way"` // console or file
|
||||
LogLevel string `json:"log_level"`
|
||||
LogMaxDays int64 `json:"log_max_days"`
|
||||
Token string `json:"token"`
|
||||
AuthTimeout int64 `json:"auth_timeout"`
|
||||
SubDomainHost string `json:"subdomain_host"`
|
||||
TcpMux bool `json:"tcp_mux"`
|
||||
|
||||
PrivilegeAllowPorts map[int]struct{}
|
||||
MaxPoolCount int64
|
||||
MaxPortsPerClient int64
|
||||
HeartBeatTimeout int64
|
||||
UserConnTimeout int64
|
||||
AllowPorts map[int]struct{}
|
||||
MaxPoolCount int64 `json:"max_pool_count"`
|
||||
MaxPortsPerClient int64 `json:"max_ports_per_client"`
|
||||
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
|
||||
UserConnTimeout int64 `json:"user_conn_timeout"`
|
||||
}
|
||||
|
||||
func GetDefaultServerCommonConf() *ServerCommonConf {
|
||||
func GetDefaultServerConf() *ServerCommonConf {
|
||||
return &ServerCommonConf{
|
||||
ConfigFile: "./frps.ini",
|
||||
BindAddr: "0.0.0.0",
|
||||
BindPort: 7000,
|
||||
BindUdpPort: 0,
|
||||
KcpBindPort: 0,
|
||||
ProxyBindAddr: "0.0.0.0",
|
||||
VhostHttpPort: 0,
|
||||
VhostHttpsPort: 0,
|
||||
DashboardAddr: "0.0.0.0",
|
||||
DashboardPort: 0,
|
||||
DashboardUser: "admin",
|
||||
DashboardPwd: "admin",
|
||||
AssetsDir: "",
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
PrivilegeMode: true,
|
||||
PrivilegeToken: "",
|
||||
AuthTimeout: 900,
|
||||
SubDomainHost: "",
|
||||
TcpMux: true,
|
||||
PrivilegeAllowPorts: make(map[int]struct{}),
|
||||
MaxPoolCount: 5,
|
||||
MaxPortsPerClient: 0,
|
||||
HeartBeatTimeout: 90,
|
||||
UserConnTimeout: 10,
|
||||
BindAddr: "0.0.0.0",
|
||||
BindPort: 7000,
|
||||
BindUdpPort: 0,
|
||||
KcpBindPort: 0,
|
||||
ProxyBindAddr: "0.0.0.0",
|
||||
VhostHttpPort: 0,
|
||||
VhostHttpsPort: 0,
|
||||
DashboardAddr: "0.0.0.0",
|
||||
DashboardPort: 0,
|
||||
DashboardUser: "admin",
|
||||
DashboardPwd: "admin",
|
||||
AssetsDir: "",
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
Token: "",
|
||||
AuthTimeout: 900,
|
||||
SubDomainHost: "",
|
||||
TcpMux: true,
|
||||
AllowPorts: make(map[int]struct{}),
|
||||
MaxPoolCount: 5,
|
||||
MaxPortsPerClient: 0,
|
||||
HeartBeatTimeout: 90,
|
||||
UserConnTimeout: 10,
|
||||
}
|
||||
}
|
||||
|
||||
// Load server common configure.
|
||||
func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) {
|
||||
cfg = defaultCfg
|
||||
if cfg == nil {
|
||||
cfg = GetDefaultServerConf()
|
||||
}
|
||||
|
||||
conf, err := ini.Load(strings.NewReader(content))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("parse ini conf file error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
v int64
|
||||
)
|
||||
cfg = GetDefaultServerCommonConf()
|
||||
|
||||
tmpStr, ok = conf.Get("common", "bind_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
|
||||
cfg.BindAddr = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "bind_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "bind_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
||||
return
|
||||
@@ -120,8 +135,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "bind_udp_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
||||
return
|
||||
@@ -130,8 +144,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "kcp_bind_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
||||
return
|
||||
@@ -140,15 +153,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "proxy_bind_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
|
||||
cfg.ProxyBindAddr = tmpStr
|
||||
} else {
|
||||
cfg.ProxyBindAddr = cfg.BindAddr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "vhost_http_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
||||
return
|
||||
@@ -159,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
cfg.VhostHttpPort = 0
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "vhost_https_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
||||
return
|
||||
@@ -171,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
cfg.VhostHttpsPort = 0
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
|
||||
cfg.DashboardAddr = tmpStr
|
||||
} else {
|
||||
cfg.DashboardAddr = cfg.BindAddr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
||||
return
|
||||
@@ -190,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
cfg.DashboardPort = 0
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_user")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
|
||||
cfg.DashboardUser = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_pwd")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
|
||||
cfg.DashboardPwd = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "assets_dir")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
|
||||
cfg.AssetsDir = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_file")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||
cfg.LogFile = tmpStr
|
||||
if cfg.LogFile == "console" {
|
||||
cfg.LogWay = "console"
|
||||
@@ -215,47 +219,33 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_level")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||
cfg.LogLevel = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err == nil {
|
||||
cfg.LogMaxDays = v
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "privilege_mode")
|
||||
if ok {
|
||||
if tmpStr == "true" {
|
||||
cfg.PrivilegeMode = true
|
||||
cfg.Token, _ = conf.Get("common", "token")
|
||||
|
||||
if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
|
||||
// e.g. 1000-2000,2001,2002,3000-4000
|
||||
ports, errRet := util.ParseRangeNumbers(allowPortsStr)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
|
||||
return
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
cfg.AllowPorts[int(port)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// PrivilegeMode configure
|
||||
if cfg.PrivilegeMode == true {
|
||||
cfg.PrivilegeToken, _ = conf.Get("common", "privilege_token")
|
||||
|
||||
allowPortsStr, ok := conf.Get("common", "privilege_allow_ports")
|
||||
if ok {
|
||||
// e.g. 1000-2000,2001,2002,3000-4000
|
||||
ports, errRet := util.ParseRangeNumbers(allowPortsStr)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports: %v", errRet)
|
||||
return
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
cfg.PrivilegeAllowPorts[int(port)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "max_pool_count")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||
return
|
||||
@@ -268,8 +258,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "max_ports_per_client")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||
return
|
||||
@@ -282,8 +271,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "authentication_timeout")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "authentication_timeout"); ok {
|
||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
|
||||
@@ -293,20 +281,17 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "subdomain_host")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
|
||||
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
||||
if ok && tmpStr == "false" {
|
||||
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||
cfg.TcpMux = false
|
||||
} else {
|
||||
cfg.TcpMux = true
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
||||
@@ -317,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *ServerCommonConf) Check() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<-
|
||||
mu.Lock()
|
||||
delete(udpConnMap, addr)
|
||||
mu.Unlock()
|
||||
udpConn.Close()
|
||||
}()
|
||||
|
||||
buf := pool.GetBuf(1500)
|
||||
|
||||
@@ -14,7 +14,7 @@ make -f ./Makefile.cross-compiles
|
||||
rm -rf ./packages
|
||||
mkdir ./packages
|
||||
|
||||
os_all='linux windows darwin'
|
||||
os_all='linux windows darwin freebsd'
|
||||
arch_all='386 amd64 arm mips64 mips64le mips mipsle'
|
||||
|
||||
for os in $os_all; do
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
@@ -103,7 +104,7 @@ func (ctl *Control) Start() {
|
||||
loginRespMsg := &msg.LoginResp{
|
||||
Version: version.Full(),
|
||||
RunId: ctl.runId,
|
||||
ServerUdpPort: config.ServerCommonCfg.BindUdpPort,
|
||||
ServerUdpPort: g.GlbServerCfg.BindUdpPort,
|
||||
Error: "",
|
||||
}
|
||||
msg.WriteMsg(ctl.conn, loginRespMsg)
|
||||
@@ -172,7 +173,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
case <-time.After(time.Duration(config.ServerCommonCfg.UserConnTimeout) * time.Second):
|
||||
case <-time.After(time.Duration(g.GlbServerCfg.UserConnTimeout) * time.Second):
|
||||
err = fmt.Errorf("timeout trying to get work connection")
|
||||
ctl.conn.Warn("%v", err)
|
||||
return
|
||||
@@ -202,7 +203,7 @@ func (ctl *Control) writer() {
|
||||
defer ctl.allShutdown.Start()
|
||||
defer ctl.writerShutdown.Done()
|
||||
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||
if err != nil {
|
||||
ctl.conn.Error("crypto new writer error: %v", err)
|
||||
ctl.allShutdown.Start()
|
||||
@@ -231,7 +232,7 @@ func (ctl *Control) reader() {
|
||||
defer ctl.allShutdown.Start()
|
||||
defer ctl.readerShutdown.Done()
|
||||
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||
for {
|
||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -265,13 +266,14 @@ func (ctl *Control) stoper() {
|
||||
ctl.conn.Close()
|
||||
ctl.readerShutdown.WaitDone()
|
||||
|
||||
ctl.mu.Lock()
|
||||
defer ctl.mu.Unlock()
|
||||
|
||||
close(ctl.workConnCh)
|
||||
for workConn := range ctl.workConnCh {
|
||||
workConn.Close()
|
||||
}
|
||||
|
||||
ctl.mu.Lock()
|
||||
defer ctl.mu.Unlock()
|
||||
for _, pxy := range ctl.proxies {
|
||||
pxy.Close()
|
||||
ctl.svr.DelProxy(pxy.GetName())
|
||||
@@ -300,9 +302,10 @@ func (ctl *Control) manager() {
|
||||
for {
|
||||
select {
|
||||
case <-heartbeat.C:
|
||||
if time.Since(ctl.lastPing) > time.Duration(config.ServerCommonCfg.HeartBeatTimeout)*time.Second {
|
||||
if time.Since(ctl.lastPing) > time.Duration(g.GlbServerCfg.HeartBeatTimeout)*time.Second {
|
||||
ctl.conn.Warn("heartbeat timeout")
|
||||
ctl.allShutdown.Start()
|
||||
return
|
||||
}
|
||||
case rawMsg, ok := <-ctl.readCh:
|
||||
if !ok {
|
||||
@@ -340,7 +343,7 @@ func (ctl *Control) manager() {
|
||||
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
||||
var pxyConf config.ProxyConf
|
||||
// Load configures from NewProxy message and check.
|
||||
pxyConf, err = config.NewProxyConf(pxyMsg)
|
||||
pxyConf, err = config.NewProxyConfFromMsg(pxyMsg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -353,9 +356,9 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
|
||||
}
|
||||
|
||||
// Check ports used number in each client
|
||||
if config.ServerCommonCfg.MaxPortsPerClient > 0 {
|
||||
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||
ctl.mu.Lock()
|
||||
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(config.ServerCommonCfg.MaxPortsPerClient) {
|
||||
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) {
|
||||
ctl.mu.Unlock()
|
||||
err = fmt.Errorf("exceed the max_ports_per_client")
|
||||
return
|
||||
@@ -402,7 +405,7 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if config.ServerCommonCfg.MaxPortsPerClient > 0 {
|
||||
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
||||
}
|
||||
pxy.Close()
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/assets"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
@@ -36,10 +36,14 @@ func RunDashboardServer(addr string, port int) (err error) {
|
||||
// url router
|
||||
router := httprouter.New()
|
||||
|
||||
user, passwd := config.ServerCommonCfg.DashboardUser, config.ServerCommonCfg.DashboardPwd
|
||||
user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd
|
||||
|
||||
// api, see dashboard_api.go
|
||||
router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd))
|
||||
router.GET("/api/proxy/tcp/:name", frpNet.HttprouterBasicAuth(apiProxyTcpByName, user, passwd))
|
||||
router.GET("/api/proxy/udp/:name", frpNet.HttprouterBasicAuth(apiProxyUdpByName, user, passwd))
|
||||
router.GET("/api/proxy/http/:name", frpNet.HttprouterBasicAuth(apiProxyHttpByName, user, passwd))
|
||||
router.GET("/api/proxy/https/:name", frpNet.HttprouterBasicAuth(apiProxyHttpsByName, user, passwd))
|
||||
router.GET("/api/proxy/tcp", frpNet.HttprouterBasicAuth(apiProxyTcp, user, passwd))
|
||||
router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd))
|
||||
router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd))
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
@@ -60,7 +61,7 @@ func apiServerInfo(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||
}()
|
||||
|
||||
log.Info("Http request: [/api/serverinfo]")
|
||||
cfg := config.ServerCommonCfg
|
||||
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||
serverStats := StatsGetServer()
|
||||
res = ServerInfoResp{
|
||||
Version: version.Full(),
|
||||
@@ -189,6 +190,119 @@ func getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get proxy info by name.
|
||||
type GetProxyStatsResp struct {
|
||||
GeneralResponse
|
||||
|
||||
Name string `json:"name"`
|
||||
Conf config.ProxyConf `json:"conf"`
|
||||
TodayTrafficIn int64 `json:"today_traffic_in"`
|
||||
TodayTrafficOut int64 `json:"today_traffic_out"`
|
||||
CurConns int64 `json:"cur_conns"`
|
||||
LastStartTime string `json:"last_start_time"`
|
||||
LastCloseTime string `json:"last_close_time"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// api/proxy/tcp/:name
|
||||
func apiProxyTcpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/tcp/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/tcp/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.TcpProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// api/proxy/udp/:name
|
||||
func apiProxyUdpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/udp/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/udp/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.UdpProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// api/proxy/http/:name
|
||||
func apiProxyHttpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/http/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/http/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.HttpProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// api/proxy/https/:name
|
||||
func apiProxyHttpsByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/https/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/https/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.HttpsProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
func getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp) {
|
||||
proxyInfo.Name = proxyName
|
||||
ps := StatsGetProxiesByTypeAndName(proxyType, proxyName)
|
||||
if ps == nil {
|
||||
proxyInfo.Code = 1
|
||||
proxyInfo.Msg = "no proxy info found"
|
||||
} else {
|
||||
if pxy, ok := ServerService.pxyManager.GetByName(proxyName); ok {
|
||||
proxyInfo.Conf = pxy.GetConf()
|
||||
proxyInfo.Status = consts.Online
|
||||
} else {
|
||||
proxyInfo.Status = consts.Offline
|
||||
}
|
||||
proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
|
||||
proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
|
||||
proxyInfo.CurConns = ps.CurConns
|
||||
proxyInfo.LastStartTime = ps.LastStartTime
|
||||
proxyInfo.LastCloseTime = ps.LastCloseTime
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// api/proxy/traffic/:name
|
||||
type GetProxyTrafficResp struct {
|
||||
GeneralResponse
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/metric"
|
||||
)
|
||||
@@ -92,19 +92,19 @@ func StatsClearUselessInfo() {
|
||||
}
|
||||
|
||||
func StatsNewClient() {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.ClientCounts.Inc(1)
|
||||
}
|
||||
}
|
||||
|
||||
func StatsCloseClient() {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.ClientCounts.Dec(1)
|
||||
}
|
||||
}
|
||||
|
||||
func StatsNewProxy(name string, proxyType string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.mu.Lock()
|
||||
defer globalStats.mu.Unlock()
|
||||
counter, ok := globalStats.ProxyTypeCounts[proxyType]
|
||||
@@ -130,7 +130,7 @@ func StatsNewProxy(name string, proxyType string) {
|
||||
}
|
||||
|
||||
func StatsCloseProxy(proxyName string, proxyType string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.mu.Lock()
|
||||
defer globalStats.mu.Unlock()
|
||||
if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
|
||||
@@ -143,7 +143,7 @@ func StatsCloseProxy(proxyName string, proxyType string) {
|
||||
}
|
||||
|
||||
func StatsOpenConnection(name string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.CurConns.Inc(1)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -157,7 +157,7 @@ func StatsOpenConnection(name string) {
|
||||
}
|
||||
|
||||
func StatsCloseConnection(name string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.CurConns.Dec(1)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -171,7 +171,7 @@ func StatsCloseConnection(name string) {
|
||||
}
|
||||
|
||||
func StatsAddTrafficIn(name string, trafficIn int64) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.TotalTrafficIn.Inc(trafficIn)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -186,7 +186,7 @@ func StatsAddTrafficIn(name string, trafficIn int64) {
|
||||
}
|
||||
|
||||
func StatsAddTrafficOut(name string, trafficOut int64) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.TotalTrafficOut.Inc(trafficOut)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -263,6 +263,37 @@ func StatsGetProxiesByType(proxyType string) []*ProxyStats {
|
||||
return res
|
||||
}
|
||||
|
||||
func StatsGetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) {
|
||||
globalStats.mu.Lock()
|
||||
defer globalStats.mu.Unlock()
|
||||
|
||||
for name, proxyStats := range globalStats.ProxyStatistics {
|
||||
if proxyStats.ProxyType != proxyType {
|
||||
continue
|
||||
}
|
||||
|
||||
if name != proxyName {
|
||||
continue
|
||||
}
|
||||
|
||||
res = &ProxyStats{
|
||||
Name: name,
|
||||
Type: proxyStats.ProxyType,
|
||||
TodayTrafficIn: proxyStats.TrafficIn.TodayCount(),
|
||||
TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
|
||||
CurConns: proxyStats.CurConns.Count(),
|
||||
}
|
||||
if !proxyStats.LastStartTime.IsZero() {
|
||||
res.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
|
||||
}
|
||||
if !proxyStats.LastCloseTime.IsZero() {
|
||||
res.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ProxyTrafficInfo struct {
|
||||
Name string
|
||||
TrafficIn []int64
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/models/proto/udp"
|
||||
@@ -126,7 +127,7 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Con
|
||||
|
||||
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
||||
basePxy := BaseProxy{
|
||||
name: pxyConf.GetName(),
|
||||
name: pxyConf.GetBaseInfo().ProxyName,
|
||||
ctl: ctl,
|
||||
listeners: make([]frpNet.Listener, 0),
|
||||
Logger: log.NewPrefixLogger(ctl.runId),
|
||||
@@ -191,7 +192,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
|
||||
|
||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||
pxy.cfg.RemotePort = pxy.realPort
|
||||
listener, errRet := frpNet.ListenTcp(config.ServerCommonCfg.ProxyBindAddr, pxy.realPort)
|
||||
listener, errRet := frpNet.ListenTcp(g.GlbServerCfg.ProxyBindAddr, pxy.realPort)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
@@ -244,7 +245,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
||||
}
|
||||
tmpDomain := routeConfig.Domain
|
||||
tmpLocation := routeConfig.Location
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(g.GlbServerCfg.VhostHttpPort)))
|
||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||
})
|
||||
@@ -253,7 +254,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
||||
}
|
||||
|
||||
if pxy.cfg.SubDomain != "" {
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||
for _, location := range locations {
|
||||
routeConfig.Location = location
|
||||
err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
|
||||
@@ -262,7 +263,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
||||
}
|
||||
tmpDomain := routeConfig.Domain
|
||||
tmpLocation := routeConfig.Location
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, g.GlbServerCfg.VhostHttpPort))
|
||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||
})
|
||||
@@ -286,7 +287,7 @@ func (pxy *HttpProxy) GetRealConn() (workConn frpNet.Conn, err error) {
|
||||
|
||||
var rwc io.ReadWriteCloser = tmpConn
|
||||
if pxy.cfg.UseEncryption {
|
||||
rwc, err = frpIo.WithEncryption(rwc, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token))
|
||||
if err != nil {
|
||||
pxy.Error("create encryption stream error: %v", err)
|
||||
return
|
||||
@@ -334,11 +335,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
||||
l.AddLogPrefix(pxy.name)
|
||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||
pxy.listeners = append(pxy.listeners, l)
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, g.GlbServerCfg.VhostHttpsPort))
|
||||
}
|
||||
|
||||
if pxy.cfg.SubDomain != "" {
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||
l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
@@ -347,7 +348,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
||||
l.AddLogPrefix(pxy.name)
|
||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||
pxy.listeners = append(pxy.listeners, l)
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpsPort)))
|
||||
}
|
||||
|
||||
pxy.startListenHandler(pxy, HandleUserTcpConnection)
|
||||
@@ -478,7 +479,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
|
||||
|
||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||
pxy.cfg.RemotePort = pxy.realPort
|
||||
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.ServerCommonCfg.ProxyBindAddr, pxy.realPort))
|
||||
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbServerCfg.ProxyBindAddr, pxy.realPort))
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
@@ -644,7 +645,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) {
|
||||
var local io.ReadWriteCloser = workConn
|
||||
cfg := pxy.GetConf().GetBaseInfo()
|
||||
if cfg.UseEncryption {
|
||||
local, err = frpIo.WithEncryption(local, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token))
|
||||
if err != nil {
|
||||
pxy.Error("create encryption stream error: %v", err)
|
||||
return
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/assets"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/fatedier/frp/utils/vhost"
|
||||
|
||||
"github.com/xtaci/smux"
|
||||
fmux "github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -71,13 +71,13 @@ type Service struct {
|
||||
}
|
||||
|
||||
func NewService() (svr *Service, err error) {
|
||||
cfg := config.ServerCommonCfg
|
||||
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||
svr = &Service{
|
||||
ctlManager: NewControlManager(),
|
||||
pxyManager: NewProxyManager(),
|
||||
visitorManager: NewVisitorManager(),
|
||||
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
||||
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
||||
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||
}
|
||||
|
||||
// Init assets.
|
||||
@@ -170,7 +170,7 @@ func (svr *Service) Run() {
|
||||
if svr.natHoleController != nil {
|
||||
go svr.natHoleController.Run()
|
||||
}
|
||||
if config.ServerCommonCfg.KcpBindPort > 0 {
|
||||
if g.GlbServerCfg.KcpBindPort > 0 {
|
||||
go svr.HandleListener(svr.kcpListener)
|
||||
}
|
||||
svr.HandleListener(svr.listener)
|
||||
@@ -233,8 +233,8 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
|
||||
}
|
||||
}
|
||||
|
||||
if config.ServerCommonCfg.TcpMux {
|
||||
session, err := smux.Server(frpConn, nil)
|
||||
if g.GlbServerCfg.TcpMux {
|
||||
session, err := fmux.Server(frpConn, nil)
|
||||
if err != nil {
|
||||
log.Warn("Failed to create mux connection: %v", err)
|
||||
frpConn.Close()
|
||||
@@ -270,11 +270,11 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
|
||||
|
||||
// Check auth.
|
||||
nowTime := time.Now().Unix()
|
||||
if config.ServerCommonCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > config.ServerCommonCfg.AuthTimeout {
|
||||
if g.GlbServerCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > g.GlbServerCfg.AuthTimeout {
|
||||
err = fmt.Errorf("authorization timeout")
|
||||
return
|
||||
}
|
||||
if util.GetAuthKey(config.ServerCommonCfg.PrivilegeToken, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
|
||||
if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
|
||||
err = fmt.Errorf("authorization failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ server_port = 10700
|
||||
log_file = ./frpc.log
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
privilege_token = 123456
|
||||
token = 123456
|
||||
admin_port = 10600
|
||||
admin_user = abc
|
||||
admin_pwd = abc
|
||||
|
||||
@@ -4,7 +4,7 @@ server_port = 10700
|
||||
log_file = ./frpc_visitor.log
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
privilege_token = 123456
|
||||
token = 123456
|
||||
|
||||
[stcp_visitor]
|
||||
type = stcp
|
||||
|
||||
@@ -4,6 +4,6 @@ bind_port = 10700
|
||||
vhost_http_port = 10804
|
||||
log_file = ./frps.log
|
||||
log_level = debug
|
||||
privilege_token = 123456
|
||||
privilege_allow_ports = 10000-20000,20002,30000-50000
|
||||
token = 123456
|
||||
allow_ports = 10000-20000,20002,30000-50000
|
||||
subdomain_host = sub.com
|
||||
|
||||
@@ -209,7 +209,7 @@ func TestWebSocket(t *testing.T) {
|
||||
assert.Equal(TEST_HTTP_NORMAL_STR, string(msg))
|
||||
}
|
||||
|
||||
func TestPrivilegeAllowPorts(t *testing.T) {
|
||||
func TestAllowPorts(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// Port not allowed
|
||||
status, err := getProxyStatus(ProxyTcpPortNotAllowed)
|
||||
|
||||
@@ -21,11 +21,12 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
@@ -178,6 +179,7 @@ func (sc *SharedConn) WriteBuff(buffer []byte) (err error) {
|
||||
type StatsConn struct {
|
||||
Conn
|
||||
|
||||
closed int64 // 1 means closed
|
||||
totalRead int64
|
||||
totalWrite int64
|
||||
statsFunc func(totalRead, totalWrite int64)
|
||||
@@ -203,9 +205,12 @@ func (statsConn *StatsConn) Write(p []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (statsConn *StatsConn) Close() (err error) {
|
||||
err = statsConn.Conn.Close()
|
||||
if statsConn.statsFunc != nil {
|
||||
statsConn.statsFunc(statsConn.totalRead, statsConn.totalWrite)
|
||||
old := atomic.SwapInt64(&statsConn.closed, 1)
|
||||
if old != 1 {
|
||||
err = statsConn.Conn.Close()
|
||||
if statsConn.statsFunc != nil {
|
||||
statsConn.statsFunc(statsConn.totalRead, statsConn.totalWrite)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var version string = "0.16.0"
|
||||
var version string = "0.18.0"
|
||||
|
||||
func Full() string {
|
||||
return version
|
||||
|
||||
@@ -108,7 +108,7 @@ func readHandshake(rd io.Reader) (host string, err error) {
|
||||
return
|
||||
}
|
||||
if len(data) < 2 {
|
||||
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short")
|
||||
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short", len(data))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
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.7
|
||||
- go: 1.8
|
||||
- go: 1.9
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- 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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -86,6 +88,50 @@ type Dialer struct {
|
||||
|
||||
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) {
|
||||
hostPort = u.Host
|
||||
hostNoPort = u.Host
|
||||
@@ -104,7 +150,7 @@ func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||
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{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
@@ -131,7 +177,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(urlStr)
|
||||
u, err := parseURL(urlStr)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
if d.HandshakeTimeout != 0 {
|
||||
deadline = time.Now().Add(d.HandshakeTimeout)
|
||||
}
|
||||
|
||||
// Get network dial function.
|
||||
netDial := d.NetDial
|
||||
if netDial == nil {
|
||||
netDialer := &net.Dialer{Deadline: deadline}
|
||||
netDial = netDialer.Dial
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to set the connection deadline.
|
||||
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)
|
||||
netConn, err := netDial("tcp", targetHostPort)
|
||||
if err != nil {
|
||||
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" {
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httptest"
|
||||
@@ -34,10 +31,9 @@ var cstUpgrader = Upgrader{
|
||||
}
|
||||
|
||||
var cstDialer = Dialer{
|
||||
Subprotocols: []string{"p1", "p2"},
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
HandshakeTimeout: 30 * time.Second,
|
||||
Subprotocols: []string{"p1", "p2"},
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
type cstHandler struct{ *testing.T }
|
||||
@@ -147,9 +143,8 @@ func TestProxyDial(t *testing.T) {
|
||||
s := newServer(t)
|
||||
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)
|
||||
|
||||
connect := false
|
||||
@@ -165,8 +160,8 @@ func TestProxyDial(t *testing.T) {
|
||||
}
|
||||
|
||||
if !connect {
|
||||
t.Log("connect not received")
|
||||
http.Error(w, "connect not received", 405)
|
||||
t.Log("connect not recieved")
|
||||
http.Error(w, "connect not recieved", 405)
|
||||
return
|
||||
}
|
||||
origHandler.ServeHTTP(w, r)
|
||||
@@ -178,16 +173,16 @@ func TestProxyDial(t *testing.T) {
|
||||
}
|
||||
defer ws.Close()
|
||||
sendRecv(t, ws)
|
||||
|
||||
cstDialer.Proxy = http.ProxyFromEnvironment
|
||||
}
|
||||
|
||||
func TestProxyAuthorizationDial(t *testing.T) {
|
||||
s := newServer(t)
|
||||
defer s.Close()
|
||||
|
||||
surl, _ := url.Parse(s.Server.URL)
|
||||
surl, _ := url.Parse(s.URL)
|
||||
surl.User = url.UserPassword("username", "password")
|
||||
|
||||
cstDialer := cstDialer // make local copy for modification on next line.
|
||||
cstDialer.Proxy = http.ProxyURL(surl)
|
||||
|
||||
connect := false
|
||||
@@ -205,8 +200,8 @@ func TestProxyAuthorizationDial(t *testing.T) {
|
||||
}
|
||||
|
||||
if !connect {
|
||||
t.Log("connect with proxy authorization not received")
|
||||
http.Error(w, "connect with proxy authorization not received", 405)
|
||||
t.Log("connect with proxy authorization not recieved")
|
||||
http.Error(w, "connect with proxy authorization not recieved", 405)
|
||||
return
|
||||
}
|
||||
origHandler.ServeHTTP(w, r)
|
||||
@@ -218,6 +213,8 @@ func TestProxyAuthorizationDial(t *testing.T) {
|
||||
}
|
||||
defer ws.Close()
|
||||
sendRecv(t, ws)
|
||||
|
||||
cstDialer.Proxy = http.ProxyFromEnvironment
|
||||
}
|
||||
|
||||
func TestDial(t *testing.T) {
|
||||
@@ -240,7 +237,7 @@ func TestDialCookieJar(t *testing.T) {
|
||||
d := cstDialer
|
||||
d.Jar = jar
|
||||
|
||||
u, _ := url.Parse(s.URL)
|
||||
u, _ := parseURL(s.URL)
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
@@ -249,7 +246,7 @@ func TestDialCookieJar(t *testing.T) {
|
||||
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)
|
||||
|
||||
ws, _, err := d.Dial(s.URL, nil)
|
||||
@@ -401,17 +398,9 @@ func TestBadMethod(t *testing.T) {
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", s.URL, strings.NewReader(""))
|
||||
resp, err := http.PostForm(s.URL, url.Values{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest 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)
|
||||
t.Fatalf("PostForm returned error %v", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||
@@ -521,82 +510,3 @@ func TestDialCompression(t *testing.T) {
|
||||
defer ws.Close()
|
||||
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 (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"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 {
|
||||
u *url.URL
|
||||
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.
|
||||
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.
|
||||
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) Timeout() bool { return e.timeout }
|
||||
|
||||
// CloseError represents a close message.
|
||||
// CloseError represents close frame.
|
||||
type CloseError struct {
|
||||
|
||||
// Code is defined in RFC 6455, section 11.7.
|
||||
Code int
|
||||
|
||||
@@ -342,8 +343,7 @@ func (c *Conn) Subprotocol() string {
|
||||
return c.subprotocol
|
||||
}
|
||||
|
||||
// Close closes the underlying network connection without sending or waiting
|
||||
// for a close message.
|
||||
// Close closes the underlying network connection without sending or waiting for a close frame.
|
||||
func (c *Conn) Close() error {
|
||||
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
|
||||
// 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) {
|
||||
if err := c.prepWrite(messageType); err != nil {
|
||||
return nil, err
|
||||
@@ -767,6 +764,7 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
// Read methods
|
||||
|
||||
func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
// 1. Skip remainder of previous frame.
|
||||
|
||||
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
|
||||
// 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.
|
||||
func (c *Conn) SetReadLimit(limit int64) {
|
||||
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.
|
||||
// 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
|
||||
// message back to the peer.
|
||||
// if the close message is empty. The default close handler sends a close frame
|
||||
// back to the peer.
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||
if h == nil {
|
||||
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))
|
||||
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.
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
if h == nil {
|
||||
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.
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
if h == 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.
|
||||
// An empty message is returned for code CloseNoStatusReceived.
|
||||
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))
|
||||
binary.BigEndian.PutUint16(buf, uint16(closeCode))
|
||||
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) {
|
||||
|
||||
// Test calling bufio.ReadBytes for value longer than read buffer size.
|
||||
|
||||
m := make([]byte, 512)
|
||||
@@ -365,7 +366,7 @@ func TestBufioReadBytes(t *testing.T) {
|
||||
t.Fatalf("ReadBytes() returned %v", err)
|
||||
}
|
||||
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
|
||||
//
|
||||
// The Conn type represents a WebSocket connection. A server application calls
|
||||
// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
|
||||
// The Conn type represents a WebSocket connection. A server application uses
|
||||
// the Upgrade function from an Upgrader object with a HTTP request handler
|
||||
// to get a pointer to a Conn:
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// ReadBufferSize: 1024,
|
||||
@@ -30,12 +31,10 @@
|
||||
// for {
|
||||
// messageType, p, err := conn.ReadMessage()
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err := conn.WriteMessage(messageType, p); err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// if err = conn.WriteMessage(messageType, p); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
@@ -86,26 +85,20 @@
|
||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
||||
// methods to send a control message to the peer.
|
||||
//
|
||||
// Connections handle received close messages by calling the handler function
|
||||
// set with the SetCloseHandler method and by returning a *CloseError from the
|
||||
// NextReader, ReadMessage or the message Read method. The default close
|
||||
// handler sends a close message to the peer.
|
||||
// Connections handle received close messages by sending a close message to the
|
||||
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
|
||||
// message Read method.
|
||||
//
|
||||
// Connections handle received ping messages by calling the handler function
|
||||
// set with the SetPingHandler method. The default ping handler sends a pong
|
||||
// message to the peer.
|
||||
// Connections handle received ping and pong messages by invoking callback
|
||||
// functions set with SetPingHandler and SetPongHandler methods. The callback
|
||||
// functions are called from the NextReader, ReadMessage and the message Read
|
||||
// methods.
|
||||
//
|
||||
// Connections handle received pong messages by calling the handler function
|
||||
// set with the SetPongHandler method. The default pong handler does nothing.
|
||||
// If an application sends ping messages, then the application should set a
|
||||
// pong handler to receive the corresponding pong.
|
||||
// The default ping handler sends a pong to the peer. The application's reading
|
||||
// goroutine can block for a short time while the handler writes the pong data
|
||||
// to the connection.
|
||||
//
|
||||
// The control message handler functions are called from the NextReader,
|
||||
// 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
|
||||
// The application must read the connection to process ping, pong and close
|
||||
// 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
|
||||
// read and discard messages from the peer. A simple example is:
|
||||
@@ -154,9 +147,9 @@
|
||||
// CheckOrigin: func(r *http.Request) bool { return true },
|
||||
// }
|
||||
//
|
||||
// The deprecated package-level Upgrade function does not perform origin
|
||||
// checking. The application is responsible for checking the Origin header
|
||||
// before calling the Upgrade function.
|
||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||
// application's responsibility to check the Origin header before calling
|
||||
// Upgrade.
|
||||
//
|
||||
// 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
|
||||
|
||||
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
|
||||
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 {
|
||||
_, message, err := c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
log.Printf("error: %v", err)
|
||||
}
|
||||
break
|
||||
@@ -113,7 +113,7 @@ func (c *Client) writePump() {
|
||||
}
|
||||
case <-ticker.C:
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<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"
|
||||
)
|
||||
|
||||
// WriteJSON writes the JSON encoding of v as a message.
|
||||
//
|
||||
// Deprecated: Use c.WriteJSON instead.
|
||||
// WriteJSON is deprecated, use c.WriteJSON instead.
|
||||
func WriteJSON(c *Conn, v interface{}) error {
|
||||
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
|
||||
// conversion of Go values to JSON.
|
||||
@@ -33,10 +31,7 @@ func (c *Conn) WriteJSON(v interface{}) error {
|
||||
return err2
|
||||
}
|
||||
|
||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
||||
// it in the value pointed to by v.
|
||||
//
|
||||
// Deprecated: Use c.ReadJSON instead.
|
||||
// ReadJSON is deprecated, use c.ReadJSON instead.
|
||||
func ReadJSON(c *Conn, v interface{}) error {
|
||||
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)))
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
|
||||
// Mask one byte at a time for small buffers.
|
||||
if len(b) < 2*wordSize {
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
return equalASCIIFold(u.Host, r.Host)
|
||||
return u.Host == r.Host
|
||||
}
|
||||
|
||||
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
|
||||
// response.
|
||||
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" {
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"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")
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||
}
|
||||
|
||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||
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
|
||||
if checkOrigin == nil {
|
||||
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.
|
||||
//
|
||||
// Deprecated: Use websocket.Upgrader instead.
|
||||
// This function is deprecated, use websocket.Upgrader instead.
|
||||
//
|
||||
// Upgrade does not perform origin checking. The application is responsible for
|
||||
// checking the Origin header before calling Upgrade. An example implementation
|
||||
// of the same origin policy check is:
|
||||
// The application is responsible for checking the request origin before
|
||||
// calling Upgrade. An example implementation of the same origin policy is:
|
||||
//
|
||||
// if req.Header.Get("Origin") != "http://"+req.Host {
|
||||
// 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"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
@@ -112,14 +111,14 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j++
|
||||
j += 1
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j++
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
@@ -128,31 +127,8 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
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
|
||||
// name contains a token equal to value with ASCII case folding.
|
||||
// name contains token.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
headers:
|
||||
for _, s := range header[name] {
|
||||
@@ -166,7 +142,7 @@ headers:
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
if equalASCIIFold(t, value) {
|
||||
if strings.EqualFold(t, value) {
|
||||
return true
|
||||
}
|
||||
if s == "" {
|
||||
@@ -180,6 +156,7 @@ headers:
|
||||
|
||||
// parseExtensiosn parses WebSocket extensions from a header.
|
||||
func parseExtensions(header http.Header) []map[string]string {
|
||||
|
||||
// From RFC 6455:
|
||||
//
|
||||
// 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"
|
||||
)
|
||||
|
||||
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 {
|
||||
value string
|
||||
ok bool
|
||||
@@ -56,32 +38,29 @@ var parseExtensionTests = []struct {
|
||||
value 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{"": "bar", "baz": "2"}}},
|
||||
{`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{"": "bar", "baz": "2"}}},
|
||||
{`foo, bar; baz=2 junk`, []map[string]string{
|
||||
{"": "foo"}}},
|
||||
map[string]string{"": "foo"}}},
|
||||
{`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{"": "deflate-stream"}}},
|
||||
{`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"}}},
|
||||
{`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": ""}}},
|
||||
{"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"},
|
||||
}},
|
||||
map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
|
||||
map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}},
|
||||
}
|
||||
|
||||
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
|
||||
*.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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user