mirror of
https://github.com/fatedier/frp.git
synced 2025-05-25 17:28:27 +00:00
commit
997d406ec2
.circleci
MakefileRelease.mdassets
frpc
frps
client/proxy
cmd/frpc/sub
conf
go.modgo.sumhack
pkg
config
plugin
transport
util
server/proxy
test/e2e
basic
e2e_test.goexamples.gofeatures
framework
mock/server
pkg
cert
port
process
request
rpc
sdk/client
utils
plugin
tests
ci
auto_test_frpc.iniauto_test_frpc_visitor.iniauto_test_frps.inicmd_test.go
health
normal_test.goreconnect_test.goreload_test.gotemplate_test.goconfig
consts
mock
util
web
frpc/src
frps/src/components
@ -1,6 +1,6 @@
|
||||
version: 2
|
||||
jobs:
|
||||
test1:
|
||||
go-version-latest:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-node
|
||||
working_directory: /go/src/github.com/fatedier/frp
|
||||
@ -8,7 +8,7 @@ jobs:
|
||||
- checkout
|
||||
- run: make
|
||||
- run: make alltest
|
||||
test2:
|
||||
go-version-last:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-node
|
||||
working_directory: /go/src/github.com/fatedier/frp
|
||||
@ -21,5 +21,5 @@ workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- test1
|
||||
- test2
|
||||
- go-version-latest
|
||||
- go-version-last
|
||||
|
8
Makefile
8
Makefile
@ -34,13 +34,13 @@ gotest:
|
||||
go test -v --cover ./server/...
|
||||
go test -v --cover ./pkg/...
|
||||
|
||||
ci:
|
||||
go test -count=1 -p=1 -v ./tests/...
|
||||
|
||||
e2e:
|
||||
./hack/run-e2e.sh
|
||||
|
||||
alltest: gotest ci e2e
|
||||
e2e-trace:
|
||||
DEBUG=true LOG_LEVEL=trace ./hack/run-e2e.sh
|
||||
|
||||
alltest: gotest e2e
|
||||
|
||||
clean:
|
||||
rm -f ./bin/frpc
|
||||
|
@ -0,0 +1,5 @@
|
||||
### Fix
|
||||
|
||||
* Plugin `https2https` not work.
|
||||
* `context canceled` problem for `http_proxy` plugin when multiple requests reuse same connection.
|
||||
* In some cases, frps can't get server name for `https` proxy.
|
@ -1 +1 @@
|
||||
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?5a0eb52788515d02ca46"></script><script type="text/javascript" src="vendor.js?3e5221f064f1295497bf"></script></body> </html>
|
||||
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?5d5774096cf5c1b4d5af"></script><script type="text/javascript" src="vendor.js?dc42700731a508d39009"></script></body> </html>
|
@ -1 +1 @@
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,u,c){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in u)Object.prototype.hasOwnProperty.call(u,i)&&(e[i]=u[i]);for(r&&r(t,u,c);s.length;)s.shift()();if(c)for(l=0;l<c.length;l++)f=n(n.s=c[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var u=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=u;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"3e5221f064f1295497bf"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,c.appendChild(i),u},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"dc42700731a508d39009"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frps dashboard</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?41dbccdbc87e6bcbd79c"></script><script type="text/javascript" src="vendor.js?d7109b07f8f86bab2eeb"></script></body> </html>
|
||||
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frps dashboard</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?5d154ba4c6b342d8c0c3"></script><script type="text/javascript" src="vendor.js?ddbd1f69fb6e67be4b78"></script></body> </html>
|
@ -1 +1 @@
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,u,c){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in u)Object.prototype.hasOwnProperty.call(u,i)&&(e[i]=u[i]);for(r&&r(t,u,c);s.length;)s.shift()();if(c)for(l=0;l<c.length;l++)f=n(n.s=c[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var u=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=u;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"d7109b07f8f86bab2eeb"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,c.appendChild(i),u},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,u,c){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in u)Object.prototype.hasOwnProperty.call(u,i)&&(e[i]=u[i]);for(r&&r(t,u,c);s.length;)s.shift()();if(c)for(l=0;l<c.length;l++)f=n(n.s=c[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var u=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=u;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"ddbd1f69fb6e67be4b78"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,c.appendChild(i),u},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -757,12 +757,12 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
|
||||
if m.DstAddr == "" {
|
||||
m.DstAddr = "127.0.0.1"
|
||||
}
|
||||
srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
|
||||
dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
|
||||
h := &pp.Header{
|
||||
Command: pp.PROXY,
|
||||
SourceAddress: net.ParseIP(m.SrcAddr),
|
||||
SourcePort: m.SrcPort,
|
||||
DestinationAddress: net.ParseIP(m.DstAddr),
|
||||
DestinationPort: m.DstPort,
|
||||
Command: pp.PROXY,
|
||||
SourceAddr: srcAddr,
|
||||
DestinationAddr: dstAddr,
|
||||
}
|
||||
|
||||
if strings.Contains(m.SrcAddr, ".") {
|
||||
|
@ -89,7 +89,7 @@ func status(clientCfg config.ClientCommonConf) error {
|
||||
|
||||
fmt.Println("Proxy Status...")
|
||||
if len(res.TCP) > 0 {
|
||||
fmt.Printf("TCP")
|
||||
fmt.Println("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)
|
||||
@ -98,7 +98,7 @@ func status(clientCfg config.ClientCommonConf) error {
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.UDP) > 0 {
|
||||
fmt.Printf("UDP")
|
||||
fmt.Println("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)
|
||||
@ -107,7 +107,7 @@ func status(clientCfg config.ClientCommonConf) error {
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.HTTP) > 0 {
|
||||
fmt.Printf("HTTP")
|
||||
fmt.Println("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)
|
||||
@ -116,7 +116,7 @@ func status(clientCfg config.ClientCommonConf) error {
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.HTTPS) > 0 {
|
||||
fmt.Printf("HTTPS")
|
||||
fmt.Println("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)
|
||||
@ -125,7 +125,7 @@ func status(clientCfg config.ClientCommonConf) error {
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.STCP) > 0 {
|
||||
fmt.Printf("STCP")
|
||||
fmt.Println("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)
|
||||
@ -134,7 +134,7 @@ func status(clientCfg config.ClientCommonConf) error {
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.XTCP) > 0 {
|
||||
fmt.Printf("XTCP")
|
||||
fmt.Println("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)
|
||||
|
@ -36,7 +36,7 @@ vhost_https_port = 443
|
||||
dashboard_addr = 0.0.0.0
|
||||
dashboard_port = 7500
|
||||
|
||||
# dashboard user and passwd for basic auth protect, if not set, both default value is admin
|
||||
# dashboard user and passwd for basic auth protect
|
||||
dashboard_user = admin
|
||||
dashboard_pwd = admin
|
||||
|
||||
|
@ -9,6 +9,7 @@ Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecStart=/usr/bin/frpc -c /etc/frp/frpc.ini
|
||||
ExecReload=/usr/bin/frpc reload -c /etc/frp/frpc.ini
|
||||
LimitNOFILE=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -3,12 +3,13 @@ Description=Frp Client Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
Type=simple
|
||||
User=nobody
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecStart=/usr/bin/frpc -c /etc/frp/%i.ini
|
||||
ExecReload=/usr/bin/frpc reload -c /etc/frp/%i.ini
|
||||
LimitNOFILE=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -8,6 +8,7 @@ User=nobody
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecStart=/usr/bin/frps -c /etc/frp/frps.ini
|
||||
LimitNOFILE=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -8,6 +8,7 @@ User=nobody
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecStart=/usr/bin/frps -c /etc/frp/%i.ini
|
||||
LimitNOFILE=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
36
go.mod
36
go.mod
@ -8,33 +8,33 @@ require (
|
||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
|
||||
github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.6.1
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c
|
||||
github.com/klauspost/cpuid v1.2.0 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/onsi/ginkgo v1.12.3
|
||||
github.com/onsi/gomega v1.10.1
|
||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/gomega v1.13.0
|
||||
github.com/pires/go-proxyproto v0.5.0
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/client_golang v1.4.1
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/rakyll/statik v0.1.1
|
||||
github.com/rodaine/table v1.0.0
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/rodaine/table v1.0.1
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect
|
||||
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect
|
||||
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
gopkg.in/square/go-jose.v2 v2.4.1 // indirect
|
||||
k8s.io/apimachinery v0.18.3
|
||||
k8s.io/apimachinery v0.21.2
|
||||
k8s.io/client-go v0.21.2
|
||||
)
|
||||
|
573
go.sum
573
go.sum
@ -1,269 +1,688 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
|
||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
|
||||
github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185 h1:2p4W5xYizIYwhiGQgeHOQcRD2O84j0tjD40P6gUCRrk=
|
||||
github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185/go.mod h1:MUs+IH/MGJNz5Cj2JVJBPZBKw2exON7LzO3HrJHmGiQ=
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.6.1 h1:W6TRDXt4WcWp4c4nf/G+6BkGdhiIo0k417gfr+V6u4I=
|
||||
github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864 h1:Y4V+SFe7d3iH+9pJCoeWIOS5/xBJIFsltS7E+KJSsJY=
|
||||
github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c h1:nqkErwUGfpZZMqj29WZ9U/wz2OpJVDuiokLhE/3Y7IQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8=
|
||||
github.com/klauspost/reedsolomon v1.9.1/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ=
|
||||
github.com/onsi/ginkgo v1.12.3/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8=
|
||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo=
|
||||
github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8=
|
||||
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg=
|
||||
github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
||||
github.com/rodaine/table v1.0.0 h1:UaCJG5Axc/cNXVGXqnCrffm1KxP0OfYLe1HuJLf5sFY=
|
||||
github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I=
|
||||
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
|
||||
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8=
|
||||
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
||||
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk=
|
||||
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
|
||||
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw=
|
||||
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
|
||||
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
|
||||
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU=
|
||||
k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc=
|
||||
k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM=
|
||||
k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0=
|
||||
k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
@ -5,11 +5,16 @@ ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd)
|
||||
which ginkgo &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ginkgo not found, try to install..."
|
||||
go install github.com/onsi/ginkgo/ginkgo
|
||||
go get -u github.com/onsi/ginkgo/ginkgo
|
||||
fi
|
||||
|
||||
debug=false
|
||||
if [ x${DEBUG} == x"true" ]; then
|
||||
debug=true
|
||||
fi
|
||||
ginkgo -nodes=4 ${ROOT}/test/e2e -- -frpc-path=${ROOT}/bin/frpc -frps-path=${ROOT}/bin/frps -log-level=debug -debug=${debug}
|
||||
logLevel=debug
|
||||
if [ x${LOG_LEVEL} != x"" ]; then
|
||||
logLevel=${LOG_LEVEL}
|
||||
fi
|
||||
|
||||
ginkgo -nodes=8 -slowSpecThreshold=20 ${ROOT}/test/e2e -- -frpc-path=${ROOT}/bin/frpc -frps-path=${ROOT}/bin/frps -log-level=${logLevel} -debug=${debug}
|
||||
|
@ -34,7 +34,7 @@ type ClientCommonConf struct {
|
||||
|
||||
// ServerAddr specifies the address of the server to connect to. By
|
||||
// default, this value is "0.0.0.0".
|
||||
ServerAddr string `ini:"server_addr" josn:"server_addr"`
|
||||
ServerAddr string `ini:"server_addr" json:"server_addr"`
|
||||
// ServerPort specifies the port to connect to the server on. By default,
|
||||
// this value is 7000.
|
||||
ServerPort int `ini:"server_port" json:"server_port"`
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||
"github.com/fatedier/frp/pkg/util/util"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
@ -36,31 +37,31 @@ type ServerCommonConf struct {
|
||||
BindAddr string `ini:"bind_addr" json:"bind_addr"`
|
||||
// BindPort specifies the port that the server listens on. By default, this
|
||||
// value is 7000.
|
||||
BindPort int `ini:"bind_port" json:"bind_port"`
|
||||
BindPort int `ini:"bind_port" json:"bind_port" validate:"gte=0,lte=65535"`
|
||||
// BindUDPPort specifies the UDP port that the server listens on. If this
|
||||
// value is 0, the server will not listen for UDP connections. By default,
|
||||
// this value is 0
|
||||
BindUDPPort int `ini:"bind_udp_port" json:"bind_udp_port"`
|
||||
BindUDPPort int `ini:"bind_udp_port" json:"bind_udp_port" validate:"gte=0,lte=65535"`
|
||||
// KCPBindPort specifies the KCP port that the server listens on. If this
|
||||
// value is 0, the server will not listen for KCP connections. By default,
|
||||
// this value is 0.
|
||||
KCPBindPort int `ini:"kcp_bind_port" json:"kcp_bind_port"`
|
||||
KCPBindPort int `ini:"kcp_bind_port" json:"kcp_bind_port" validate:"gte=0,lte=65535"`
|
||||
// ProxyBindAddr specifies the address that the proxy binds to. This value
|
||||
// may be the same as BindAddr.
|
||||
ProxyBindAddr string `ini:"proxy_bind_addr" json:"proxy_bind_addr"`
|
||||
// VhostHTTPPort specifies the port that the server listens for HTTP Vhost
|
||||
// requests. If this value is 0, the server will not listen for HTTP
|
||||
// requests. By default, this value is 0.
|
||||
VhostHTTPPort int `ini:"vhost_http_port" json:"vhost_http_port"`
|
||||
VhostHTTPPort int `ini:"vhost_http_port" json:"vhost_http_port" validate:"gte=0,lte=65535"`
|
||||
// VhostHTTPSPort specifies the port that the server listens for HTTPS
|
||||
// Vhost requests. If this value is 0, the server will not listen for HTTPS
|
||||
// requests. By default, this value is 0.
|
||||
VhostHTTPSPort int `ini:"vhost_https_port" json:"vhost_https_port"`
|
||||
VhostHTTPSPort int `ini:"vhost_https_port" json:"vhost_https_port" validate:"gte=0,lte=65535"`
|
||||
// TCPMuxHTTPConnectPort specifies the port that the server listens for TCP
|
||||
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
||||
// requests on one single port. If it's not - it will listen on this value for
|
||||
// HTTP CONNECT requests. By default, this value is 0.
|
||||
TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port"`
|
||||
TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port" validate:"gte=0,lte=65535"`
|
||||
// VhostHTTPTimeout specifies the response header timeout for the Vhost
|
||||
// HTTP server, in seconds. By default, this value is 60.
|
||||
VhostHTTPTimeout int64 `ini:"vhost_http_timeout" json:"vhost_http_timeout"`
|
||||
@ -70,7 +71,7 @@ type ServerCommonConf struct {
|
||||
// DashboardPort specifies the port that the dashboard listens on. If this
|
||||
// value is 0, the dashboard will not be started. By default, this value is
|
||||
// 0.
|
||||
DashboardPort int `ini:"dashboard_port" json:"dashboard_port"`
|
||||
DashboardPort int `ini:"dashboard_port" json:"dashboard_port" validate:"gte=0,lte=65535"`
|
||||
// DashboardUser specifies the username that the dashboard will use for
|
||||
// login.
|
||||
DashboardUser string `ini:"dashboard_user" json:"dashboard_user"`
|
||||
@ -281,7 +282,7 @@ func (cfg *ServerCommonConf) Complete() {
|
||||
}
|
||||
|
||||
func (cfg *ServerCommonConf) Validate() error {
|
||||
return nil
|
||||
return validator.New().Struct(cfg)
|
||||
}
|
||||
|
||||
func loadHTTPPluginOpt(section *ini.Section) (*plugin.HTTPPluginOptions, error) {
|
||||
|
@ -60,7 +60,7 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
||||
|
||||
listener := NewProxyListener()
|
||||
|
||||
p := &HTTPS2HTTPPlugin{
|
||||
p := &HTTP2HTTPSPlugin{
|
||||
localAddr: localAddr,
|
||||
hostHeaderRewrite: hostHeaderRewrite,
|
||||
headers: headers,
|
||||
|
@ -179,7 +179,7 @@ func (m *Manager) NewWorkConn(content *NewWorkConnContent) (*NewWorkConnContent,
|
||||
ctx := xlog.NewContext(context.Background(), xl)
|
||||
ctx = NewReqidContext(ctx, reqid)
|
||||
|
||||
for _, p := range m.pingPlugins {
|
||||
for _, p := range m.newWorkConnPlugins {
|
||||
res, retContent, err = p.Handle(ctx, OpPing, *content)
|
||||
if err != nil {
|
||||
xl.Warn("send NewWorkConn request to plugin [%s] error: %v", p.Name(), err)
|
||||
|
@ -86,7 +86,7 @@ func NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
|
||||
return base, nil
|
||||
}
|
||||
|
||||
func NewClientTLSConfig(certPath, keyPath, caPath, servearName string) (*tls.Config, error) {
|
||||
func NewClientTLSConfig(certPath, keyPath, caPath, serverName string) (*tls.Config, error) {
|
||||
var base = &tls.Config{}
|
||||
|
||||
if certPath == "" || keyPath == "" {
|
||||
@ -107,7 +107,7 @@ func NewClientTLSConfig(certPath, keyPath, caPath, servearName string) (*tls.Con
|
||||
}
|
||||
|
||||
base.RootCAs = pool
|
||||
base.ServerName = servearName
|
||||
base.ServerName = serverName
|
||||
base.InsecureSkipVerify = false
|
||||
} else {
|
||||
base.InsecureSkipVerify = true
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var version string = "0.37.0"
|
||||
var version string = "0.37.1"
|
||||
|
||||
func Full() string {
|
||||
return version
|
||||
|
@ -15,32 +15,12 @@
|
||||
package vhost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
const (
|
||||
typeClientHello uint8 = 1 // Type client hello
|
||||
)
|
||||
|
||||
// TLS extension numbers
|
||||
const (
|
||||
extensionServerName uint16 = 0
|
||||
extensionStatusRequest uint16 = 5
|
||||
extensionSupportedCurves uint16 = 10
|
||||
extensionSupportedPoints uint16 = 11
|
||||
extensionSignatureAlgorithms uint16 = 13
|
||||
extensionALPN uint16 = 16
|
||||
extensionSCT uint16 = 18
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
|
||||
extensionRenegotiationInfo uint16 = 0xff01
|
||||
)
|
||||
|
||||
type HTTPSMuxer struct {
|
||||
@ -52,142 +32,49 @@ func NewHTTPSMuxer(listener net.Listener, timeout time.Duration) (*HTTPSMuxer, e
|
||||
return &HTTPSMuxer{mux}, err
|
||||
}
|
||||
|
||||
func readHandshake(rd io.Reader) (host string, err error) {
|
||||
data := pool.GetBuf(1024)
|
||||
origin := data
|
||||
defer pool.PutBuf(origin)
|
||||
|
||||
_, err = io.ReadFull(rd, data[:47])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
length, err := rd.Read(data[47:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length += 47
|
||||
data = data[:length]
|
||||
if uint8(data[5]) != typeClientHello {
|
||||
err = fmt.Errorf("readHandshake: type[%d] is not clientHello", uint16(data[5]))
|
||||
return
|
||||
}
|
||||
|
||||
// session
|
||||
sessionIDLen := int(data[43])
|
||||
if sessionIDLen > 32 || len(data) < 44+sessionIDLen {
|
||||
err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIDLen)
|
||||
return
|
||||
}
|
||||
data = data[44+sessionIDLen:]
|
||||
if len(data) < 2 {
|
||||
err = fmt.Errorf("readHandshake: dataLen[%d] after session is short", len(data))
|
||||
return
|
||||
}
|
||||
|
||||
// cipher suite numbers
|
||||
cipherSuiteLen := int(data[0])<<8 | int(data[1])
|
||||
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
|
||||
err = fmt.Errorf("readHandshake: dataLen[%d] after cipher suite is short", len(data))
|
||||
return
|
||||
}
|
||||
data = data[2+cipherSuiteLen:]
|
||||
if len(data) < 1 {
|
||||
err = fmt.Errorf("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen)
|
||||
return
|
||||
}
|
||||
|
||||
// compression method
|
||||
compressionMethodsLen := int(data[0])
|
||||
if len(data) < 1+compressionMethodsLen {
|
||||
err = fmt.Errorf("readHandshake: compressionMethodsLen[%d] is long", compressionMethodsLen)
|
||||
return
|
||||
}
|
||||
|
||||
data = data[1+compressionMethodsLen:]
|
||||
if len(data) == 0 {
|
||||
// ClientHello is optionally followed by extension data
|
||||
err = fmt.Errorf("readHandshake: there is no extension data to get servername")
|
||||
return
|
||||
}
|
||||
if len(data) < 2 {
|
||||
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short", len(data))
|
||||
return
|
||||
}
|
||||
|
||||
extensionsLength := int(data[0])<<8 | int(data[1])
|
||||
data = data[2:]
|
||||
if extensionsLength != len(data) {
|
||||
err = fmt.Errorf("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data))
|
||||
return
|
||||
}
|
||||
for len(data) != 0 {
|
||||
if len(data) < 4 {
|
||||
err = fmt.Errorf("readHandshake: extensionsDataLen[%d] is too short", len(data))
|
||||
return
|
||||
}
|
||||
extension := uint16(data[0])<<8 | uint16(data[1])
|
||||
length := int(data[2])<<8 | int(data[3])
|
||||
data = data[4:]
|
||||
if len(data) < length {
|
||||
err = fmt.Errorf("readHandshake: extensionLen[%d] is long", length)
|
||||
return
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionRenegotiationInfo:
|
||||
if length != 1 || data[0] != 0 {
|
||||
err = fmt.Errorf("readHandshake: extension reNegotiationInfoLen[%d] is short", length)
|
||||
return
|
||||
}
|
||||
case extensionNextProtoNeg:
|
||||
case extensionStatusRequest:
|
||||
case extensionServerName:
|
||||
d := data[:length]
|
||||
if len(d) < 2 {
|
||||
err = fmt.Errorf("readHandshake: remiaining dataLen[%d] is short", len(d))
|
||||
return
|
||||
}
|
||||
namesLen := int(d[0])<<8 | int(d[1])
|
||||
d = d[2:]
|
||||
if len(d) != namesLen {
|
||||
err = fmt.Errorf("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d))
|
||||
return
|
||||
}
|
||||
for len(d) > 0 {
|
||||
if len(d) < 3 {
|
||||
err = fmt.Errorf("readHandshake: extension serverNameLen[%d] is short", len(d))
|
||||
return
|
||||
}
|
||||
nameType := d[0]
|
||||
nameLen := int(d[1])<<8 | int(d[2])
|
||||
d = d[3:]
|
||||
if len(d) < nameLen {
|
||||
err = fmt.Errorf("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d))
|
||||
return
|
||||
}
|
||||
if nameType == 0 {
|
||||
serverName := string(d[:nameLen])
|
||||
host = strings.TrimSpace(serverName)
|
||||
return host, nil
|
||||
}
|
||||
d = d[nameLen:]
|
||||
}
|
||||
}
|
||||
data = data[length:]
|
||||
}
|
||||
err = fmt.Errorf("Unknown error")
|
||||
return
|
||||
}
|
||||
|
||||
func GetHTTPSHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
|
||||
reqInfoMap := make(map[string]string, 0)
|
||||
sc, rd := gnet.NewSharedConn(c)
|
||||
host, err := readHandshake(rd)
|
||||
|
||||
clientHello, err := readClientHello(rd)
|
||||
if err != nil {
|
||||
return nil, reqInfoMap, err
|
||||
}
|
||||
reqInfoMap["Host"] = host
|
||||
|
||||
reqInfoMap["Host"] = clientHello.ServerName
|
||||
reqInfoMap["Scheme"] = "https"
|
||||
return sc, reqInfoMap, nil
|
||||
}
|
||||
|
||||
func readClientHello(reader io.Reader) (*tls.ClientHelloInfo, error) {
|
||||
var hello *tls.ClientHelloInfo
|
||||
|
||||
// Note that Handshake always fails because the readOnlyConn is not a real connection.
|
||||
// As long as the Client Hello is successfully read, the failure should only happen after GetConfigForClient is called,
|
||||
// so we only care about the error if hello was never set.
|
||||
err := tls.Server(readOnlyConn{reader: reader}, &tls.Config{
|
||||
GetConfigForClient: func(argHello *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
hello = &tls.ClientHelloInfo{}
|
||||
*hello = *argHello
|
||||
return nil, nil
|
||||
},
|
||||
}).Handshake()
|
||||
|
||||
if hello == nil {
|
||||
return nil, err
|
||||
}
|
||||
return hello, nil
|
||||
}
|
||||
|
||||
type readOnlyConn struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (conn readOnlyConn) Read(p []byte) (int, error) { return conn.reader.Read(p) }
|
||||
func (conn readOnlyConn) Write(p []byte) (int, error) { return 0, io.ErrClosedPipe }
|
||||
func (conn readOnlyConn) Close() error { return nil }
|
||||
func (conn readOnlyConn) LocalAddr() net.Addr { return nil }
|
||||
func (conn readOnlyConn) RemoteAddr() net.Addr { return nil }
|
||||
func (conn readOnlyConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (conn readOnlyConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (conn readOnlyConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
38
pkg/util/vhost/https_test.go
Normal file
38
pkg/util/vhost/https_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package vhost
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetHTTPSHostname(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
l, err := net.Listen("tcp", ":")
|
||||
require.NoError(err)
|
||||
defer l.Close()
|
||||
|
||||
var conn net.Conn
|
||||
go func() {
|
||||
conn, _ = l.Accept()
|
||||
require.NotNil(conn)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
tls.Dial("tcp", l.Addr().String(), &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "example.com",
|
||||
})
|
||||
}()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
_, infos, err := GetHTTPSHostname(conn)
|
||||
require.NoError(err)
|
||||
require.Equal("example.com", infos["Host"])
|
||||
require.Equal("https", infos["Scheme"])
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/pkg/config"
|
||||
"github.com/fatedier/frp/pkg/msg"
|
||||
@ -151,12 +152,28 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn,
|
||||
xl := xlog.FromContextSafe(pxy.ctx)
|
||||
for _, listener := range pxy.listeners {
|
||||
go func(l net.Listener) {
|
||||
var tempDelay time.Duration // how long to sleep on accept failure
|
||||
|
||||
for {
|
||||
// block
|
||||
// if listener is closed, err returned
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
xl.Info("listener is closed")
|
||||
if err, ok := err.(interface{ Temporary() bool }); ok && err.Temporary() {
|
||||
if tempDelay == 0 {
|
||||
tempDelay = 5 * time.Millisecond
|
||||
} else {
|
||||
tempDelay *= 2
|
||||
}
|
||||
if max := 1 * time.Second; tempDelay > max {
|
||||
tempDelay = max
|
||||
}
|
||||
xl.Info("met temporary error: %s, sleep for %s ...", err, tempDelay)
|
||||
time.Sleep(tempDelay)
|
||||
continue
|
||||
}
|
||||
|
||||
xl.Warn("listener is closed: %s", err)
|
||||
return
|
||||
}
|
||||
xl.Info("get a user connection [%s]", c.RemoteAddr().String())
|
||||
|
@ -1,12 +1,15 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/pkg/transport"
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
|
||||
@ -81,7 +84,7 @@ var _ = Describe("[Feature: Basic]", func() {
|
||||
|
||||
for _, test := range tests {
|
||||
framework.NewRequestExpect(f).
|
||||
RequestModify(framework.SetRequestProtocol(protocol)).
|
||||
Protocol(protocol).
|
||||
PortName(test.portName).
|
||||
Explain(test.proxyName).
|
||||
Ensure()
|
||||
@ -90,6 +93,188 @@ var _ = Describe("[Feature: Basic]", func() {
|
||||
}
|
||||
})
|
||||
|
||||
Describe("HTTP", func() {
|
||||
It("proxy to HTTP server", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf += fmt.Sprintf(`
|
||||
vhost_http_port = %d
|
||||
`, vhostHTTPPort)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
getProxyConf := func(proxyName string, customDomains string, extra string) string {
|
||||
return fmt.Sprintf(`
|
||||
[%s]
|
||||
type = http
|
||||
local_port = {{ .%s }}
|
||||
custom_domains = %s
|
||||
`+extra, proxyName, framework.HTTPSimpleServerPort, customDomains)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
proxyName string
|
||||
customDomains string
|
||||
extraConfig string
|
||||
}{
|
||||
{
|
||||
proxyName: "normal",
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption",
|
||||
extraConfig: "use_encryption = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-compression",
|
||||
extraConfig: "use_compression = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption-and-compression",
|
||||
extraConfig: `
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
`,
|
||||
},
|
||||
{
|
||||
proxyName: "multiple-custom-domains",
|
||||
customDomains: "a.example.com, b.example.com",
|
||||
},
|
||||
}
|
||||
|
||||
// build all client config
|
||||
for i, test := range tests {
|
||||
if tests[i].customDomains == "" {
|
||||
tests[i].customDomains = test.proxyName + ".example.com"
|
||||
}
|
||||
clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
|
||||
}
|
||||
// run frps and frpc
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
for _, test := range tests {
|
||||
for _, domain := range strings.Split(test.customDomains, ",") {
|
||||
domain = strings.TrimSpace(domain)
|
||||
framework.NewRequestExpect(f).
|
||||
Explain(test.proxyName + "-" + domain).
|
||||
Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost(domain)
|
||||
}).
|
||||
Ensure()
|
||||
}
|
||||
}
|
||||
|
||||
// not exist host
|
||||
framework.NewRequestExpect(f).
|
||||
Explain("not exist host").
|
||||
Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("not-exist.example.com")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(404))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("HTTPS", func() {
|
||||
It("proxy to HTTPS server", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
vhostHTTPSPort := f.AllocPort()
|
||||
serverConf += fmt.Sprintf(`
|
||||
vhost_https_port = %d
|
||||
`, vhostHTTPSPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig
|
||||
getProxyConf := func(proxyName string, customDomains string, extra string) string {
|
||||
return fmt.Sprintf(`
|
||||
[%s]
|
||||
type = https
|
||||
local_port = %d
|
||||
custom_domains = %s
|
||||
`+extra, proxyName, localPort, customDomains)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
proxyName string
|
||||
customDomains string
|
||||
extraConfig string
|
||||
}{
|
||||
{
|
||||
proxyName: "normal",
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption",
|
||||
extraConfig: "use_encryption = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-compression",
|
||||
extraConfig: "use_compression = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption-and-compression",
|
||||
extraConfig: `
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
`,
|
||||
},
|
||||
{
|
||||
proxyName: "multiple-custom-domains",
|
||||
customDomains: "a.example.com, b.example.com",
|
||||
},
|
||||
}
|
||||
|
||||
// build all client config
|
||||
for i, test := range tests {
|
||||
if tests[i].customDomains == "" {
|
||||
tests[i].customDomains = test.proxyName + ".example.com"
|
||||
}
|
||||
clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
|
||||
}
|
||||
// run frps and frpc
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
|
||||
framework.ExpectNoError(err)
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithTlsConfig(tlsConfig),
|
||||
httpserver.WithResponse([]byte("test")),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
for _, test := range tests {
|
||||
for _, domain := range strings.Split(test.customDomains, ",") {
|
||||
domain = strings.TrimSpace(domain)
|
||||
framework.NewRequestExpect(f).
|
||||
Explain(test.proxyName + "-" + domain).
|
||||
Port(vhostHTTPSPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTPS().HTTPHost(domain).TLSConfig(&tls.Config{
|
||||
ServerName: domain,
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
}).
|
||||
ExpectResp([]byte("test")).
|
||||
Ensure()
|
||||
}
|
||||
}
|
||||
|
||||
// not exist host
|
||||
notExistDomain := "not-exist.example.com"
|
||||
framework.NewRequestExpect(f).
|
||||
Explain("not exist host").
|
||||
Port(vhostHTTPSPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTPS().HTTPHost(notExistDomain).TLSConfig(&tls.Config{
|
||||
ServerName: notExistDomain,
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
}).
|
||||
ExpectError(true).
|
||||
Ensure()
|
||||
})
|
||||
})
|
||||
|
||||
Describe("STCP && SUDP", func() {
|
||||
types := []string{"stcp", "sudp"}
|
||||
for _, t := range types {
|
||||
@ -186,12 +371,11 @@ var _ = Describe("[Feature: Basic]", func() {
|
||||
|
||||
for _, test := range tests {
|
||||
framework.NewRequestExpect(f).
|
||||
RequestModify(framework.SetRequestProtocol(protocol)).
|
||||
Protocol(protocol).
|
||||
PortName(test.bindPortName).
|
||||
Explain(test.proxyName).
|
||||
ExpectError(test.expectError).
|
||||
Ensure()
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -245,7 +429,7 @@ var _ = Describe("[Feature: Basic]", func() {
|
||||
for _, test := range tests {
|
||||
clientConf += getProxyConf(test.proxyName, test.extraConfig) + "\n"
|
||||
|
||||
localServer := server.New(server.TCP, server.WithBindPort(f.AllocPort()), server.WithRespContent([]byte(test.proxyName)))
|
||||
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName)))
|
||||
f.RunServer(port.GenName(test.proxyName), localServer)
|
||||
}
|
||||
|
||||
@ -262,13 +446,13 @@ var _ = Describe("[Feature: Basic]", func() {
|
||||
proxyURL := fmt.Sprintf("http://127.0.0.1:%d", f.PortByName(tcpmuxHTTPConnectPortName))
|
||||
// Request with incorrect connect hostname
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.Proxy(proxyURL, "invalid")
|
||||
r.Addr("invalid").Proxy(proxyURL)
|
||||
}).ExpectError(true).Explain("request without HTTP connect expect error").Ensure()
|
||||
|
||||
// Request with correct connect hostname
|
||||
for _, test := range tests {
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.Proxy(proxyURL, test.proxyName)
|
||||
r.Addr(test.proxyName).Proxy(proxyURL)
|
||||
}).ExpectResp([]byte(test.proxyName)).Explain(test.proxyName).Ensure()
|
||||
}
|
||||
})
|
||||
|
78
test/e2e/basic/client.go
Normal file
78
test/e2e/basic/client.go
Normal file
@ -0,0 +1,78 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
clientsdk "github.com/fatedier/frp/test/e2e/pkg/sdk/client"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: ClientManage]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
It("Update && Reload API", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
|
||||
adminPort := f.AllocPort()
|
||||
|
||||
p1Port := f.AllocPort()
|
||||
p2Port := f.AllocPort()
|
||||
p3Port := f.AllocPort()
|
||||
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
admin_port = %d
|
||||
|
||||
[p1]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
|
||||
[p2]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
|
||||
[p3]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, adminPort,
|
||||
framework.TCPEchoServerPort, p1Port,
|
||||
framework.TCPEchoServerPort, p2Port,
|
||||
framework.TCPEchoServerPort, p3Port)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(p1Port).Ensure()
|
||||
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
||||
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
||||
|
||||
client := clientsdk.New("127.0.0.1", adminPort)
|
||||
conf, err := client.GetConfig()
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
newP2Port := f.AllocPort()
|
||||
// change p2 port and remove p3 proxy
|
||||
newClientConf := strings.ReplaceAll(conf, strconv.Itoa(p2Port), strconv.Itoa(newP2Port))
|
||||
p3Index := strings.Index(newClientConf, "[p3]")
|
||||
newClientConf = newClientConf[:p3Index]
|
||||
|
||||
err = client.UpdateConfig(newClientConf)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
err = client.Reload()
|
||||
framework.ExpectNoError(err)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
framework.NewRequestExpect(f).Port(p1Port).Explain("p1 port").Ensure()
|
||||
framework.NewRequestExpect(f).Port(p2Port).Explain("original p2 port").ExpectError(true).Ensure()
|
||||
framework.NewRequestExpect(f).Port(newP2Port).Explain("new p2 port").Ensure()
|
||||
framework.NewRequestExpect(f).Port(p3Port).Explain("p3 port").ExpectError(true).Ensure()
|
||||
})
|
||||
})
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/cert"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@ -17,19 +18,17 @@ type generalTestConfigures struct {
|
||||
expectError bool
|
||||
}
|
||||
|
||||
// defineClientServerTest test a normal tcp and udp proxy with specified TestConfigures.
|
||||
func defineClientServerTest(desc string, f *framework.Framework, configures *generalTestConfigures) {
|
||||
It(desc, func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
serverConf += fmt.Sprintf(`
|
||||
serverConf += fmt.Sprintf(`
|
||||
%s
|
||||
`, configures.server)
|
||||
|
||||
tcpPortName := port.GenName("TCP")
|
||||
udpPortName := port.GenName("UDP")
|
||||
clientConf += fmt.Sprintf(`
|
||||
tcpPortName := port.GenName("TCP")
|
||||
udpPortName := port.GenName("UDP")
|
||||
clientConf += fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
[tcp]
|
||||
@ -42,15 +41,21 @@ func defineClientServerTest(desc string, f *framework.Framework, configures *gen
|
||||
local_port = {{ .%s }}
|
||||
remote_port = {{ .%s }}
|
||||
`, configures.client,
|
||||
framework.TCPEchoServerPort, tcpPortName,
|
||||
framework.UDPEchoServerPort, udpPortName,
|
||||
)
|
||||
framework.TCPEchoServerPort, tcpPortName,
|
||||
framework.UDPEchoServerPort, udpPortName,
|
||||
)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).ExpectError(configures.expectError).Explain("tcp proxy").Ensure()
|
||||
framework.NewRequestExpect(f).RequestModify(framework.SetRequestProtocol("udp")).
|
||||
PortName(udpPortName).ExpectError(configures.expectError).Explain("udp proxy").Ensure()
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).ExpectError(configures.expectError).Explain("tcp proxy").Ensure()
|
||||
framework.NewRequestExpect(f).Protocol("udp").
|
||||
PortName(udpPortName).ExpectError(configures.expectError).Explain("udp proxy").Ensure()
|
||||
}
|
||||
|
||||
// defineClientServerTest test a normal tcp and udp proxy with specified TestConfigures.
|
||||
func defineClientServerTest(desc string, f *framework.Framework, configures *generalTestConfigures) {
|
||||
It(desc, func() {
|
||||
runClientServerTest(f, configures)
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,4 +113,122 @@ var _ = Describe("[Feature: Client-Server]", func() {
|
||||
expectError: true,
|
||||
})
|
||||
})
|
||||
|
||||
Describe("TLS with custom certificate", func() {
|
||||
supportProtocols := []string{"tcp", "kcp", "websocket"}
|
||||
|
||||
var (
|
||||
caCrtPath string
|
||||
serverCrtPath, serverKeyPath string
|
||||
clientCrtPath, clientKeyPath string
|
||||
)
|
||||
JustBeforeEach(func() {
|
||||
generator := &cert.SelfSignedCertGenerator{}
|
||||
artifacts, err := generator.Generate("0.0.0.0")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
caCrtPath = f.WriteTempFile("ca.crt", string(artifacts.CACert))
|
||||
serverCrtPath = f.WriteTempFile("server.crt", string(artifacts.Cert))
|
||||
serverKeyPath = f.WriteTempFile("server.key", string(artifacts.Key))
|
||||
generator.SetCA(artifacts.CACert, artifacts.CAKey)
|
||||
generator.Generate("0.0.0.0")
|
||||
clientCrtPath = f.WriteTempFile("client.crt", string(artifacts.Cert))
|
||||
clientKeyPath = f.WriteTempFile("client.key", string(artifacts.Key))
|
||||
})
|
||||
|
||||
for _, protocol := range supportProtocols {
|
||||
tmp := protocol
|
||||
|
||||
It("one-way authentication: "+tmp, func() {
|
||||
runClientServerTest(f, &generalTestConfigures{
|
||||
server: fmt.Sprintf(`
|
||||
protocol = %s
|
||||
kcp_bind_port = {{ .%s }}
|
||||
tls_trusted_ca_file = %s
|
||||
`, tmp, consts.PortServerName, caCrtPath),
|
||||
client: fmt.Sprintf(`
|
||||
protocol = %s
|
||||
tls_enable = true
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
`, tmp, clientCrtPath, clientKeyPath),
|
||||
})
|
||||
})
|
||||
|
||||
It("mutual authentication: "+tmp, func() {
|
||||
runClientServerTest(f, &generalTestConfigures{
|
||||
server: fmt.Sprintf(`
|
||||
protocol = %s
|
||||
kcp_bind_port = {{ .%s }}
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
tls_trusted_ca_file = %s
|
||||
`, tmp, consts.PortServerName, serverCrtPath, serverKeyPath, caCrtPath),
|
||||
client: fmt.Sprintf(`
|
||||
protocol = %s
|
||||
tls_enable = true
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
tls_trusted_ca_file = %s
|
||||
`, tmp, clientCrtPath, clientKeyPath, caCrtPath),
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Describe("TLS with custom certificate and specified server name", func() {
|
||||
var (
|
||||
caCrtPath string
|
||||
serverCrtPath, serverKeyPath string
|
||||
clientCrtPath, clientKeyPath string
|
||||
)
|
||||
JustBeforeEach(func() {
|
||||
generator := &cert.SelfSignedCertGenerator{}
|
||||
artifacts, err := generator.Generate("example.com")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
caCrtPath = f.WriteTempFile("ca.crt", string(artifacts.CACert))
|
||||
serverCrtPath = f.WriteTempFile("server.crt", string(artifacts.Cert))
|
||||
serverKeyPath = f.WriteTempFile("server.key", string(artifacts.Key))
|
||||
generator.SetCA(artifacts.CACert, artifacts.CAKey)
|
||||
generator.Generate("example.com")
|
||||
clientCrtPath = f.WriteTempFile("client.crt", string(artifacts.Cert))
|
||||
clientKeyPath = f.WriteTempFile("client.key", string(artifacts.Key))
|
||||
})
|
||||
|
||||
It("mutual authentication", func() {
|
||||
runClientServerTest(f, &generalTestConfigures{
|
||||
server: fmt.Sprintf(`
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
tls_trusted_ca_file = %s
|
||||
`, serverCrtPath, serverKeyPath, caCrtPath),
|
||||
client: fmt.Sprintf(`
|
||||
tls_enable = true
|
||||
tls_server_name = example.com
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
tls_trusted_ca_file = %s
|
||||
`, clientCrtPath, clientKeyPath, caCrtPath),
|
||||
})
|
||||
})
|
||||
|
||||
It("mutual authentication with incorrect server name", func() {
|
||||
runClientServerTest(f, &generalTestConfigures{
|
||||
server: fmt.Sprintf(`
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
tls_trusted_ca_file = %s
|
||||
`, serverCrtPath, serverKeyPath, caCrtPath),
|
||||
client: fmt.Sprintf(`
|
||||
tls_enable = true
|
||||
tls_server_name = invalid.com
|
||||
tls_cert_file = %s
|
||||
tls_key_file = %s
|
||||
tls_trusted_ca_file = %s
|
||||
`, clientCrtPath, clientKeyPath, caCrtPath),
|
||||
expectError: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
113
test/e2e/basic/cmd.go
Normal file
113
test/e2e/basic/cmd.go
Normal file
@ -0,0 +1,113 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigValidStr = "syntax is ok"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Cmd]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
Describe("Verify", func() {
|
||||
It("frps valid", func() {
|
||||
path := f.GenerateConfigFile(`
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 7000
|
||||
`)
|
||||
_, output, err := f.RunFrps("verify", "-c", path)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectTrue(strings.Contains(output, ConfigValidStr), "output: %s", output)
|
||||
})
|
||||
It("frps invalid", func() {
|
||||
path := f.GenerateConfigFile(`
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 70000
|
||||
`)
|
||||
_, output, err := f.RunFrps("verify", "-c", path)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectTrue(!strings.Contains(output, ConfigValidStr), "output: %s", output)
|
||||
})
|
||||
It("frpc valid", func() {
|
||||
path := f.GenerateConfigFile(`
|
||||
[common]
|
||||
server_addr = 0.0.0.0
|
||||
server_port = 7000
|
||||
`)
|
||||
_, output, err := f.RunFrpc("verify", "-c", path)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectTrue(strings.Contains(output, ConfigValidStr), "output: %s", output)
|
||||
})
|
||||
It("frpc invalid", func() {
|
||||
path := f.GenerateConfigFile(`
|
||||
[common]
|
||||
server_addr = 0.0.0.0
|
||||
server_port = 7000
|
||||
protocol = invalid
|
||||
`)
|
||||
_, output, err := f.RunFrpc("verify", "-c", path)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectTrue(!strings.Contains(output, ConfigValidStr), "output: %s", output)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Single proxy", func() {
|
||||
It("TCP", func() {
|
||||
serverPort := f.AllocPort()
|
||||
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
localPort := f.PortByName(framework.TCPEchoServerPort)
|
||||
remotePort := f.AllocPort()
|
||||
_, _, err = f.RunFrpc("tcp", "-s", fmt.Sprintf("127.0.0.1:%d", serverPort), "-t", "123", "-u", "test",
|
||||
"-l", strconv.Itoa(localPort), "-r", strconv.Itoa(remotePort), "-n", "tcp_test")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
})
|
||||
|
||||
It("UDP", func() {
|
||||
serverPort := f.AllocPort()
|
||||
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
localPort := f.PortByName(framework.UDPEchoServerPort)
|
||||
remotePort := f.AllocPort()
|
||||
_, _, err = f.RunFrpc("udp", "-s", fmt.Sprintf("127.0.0.1:%d", serverPort), "-t", "123", "-u", "test",
|
||||
"-l", strconv.Itoa(localPort), "-r", strconv.Itoa(remotePort), "-n", "udp_test")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Protocol("udp").
|
||||
Port(remotePort).Ensure()
|
||||
})
|
||||
|
||||
It("HTTP", func() {
|
||||
serverPort := f.AllocPort()
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort), "--vhost_http_port", strconv.Itoa(vhostHTTPPort))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
_, _, err = f.RunFrpc("http", "-s", "127.0.0.1:"+strconv.Itoa(serverPort), "-t", "123", "-u", "test",
|
||||
"-n", "udp_test", "-l", strconv.Itoa(f.PortByName(framework.HTTPSimpleServerPort)),
|
||||
"--custom_domain", "test.example.com")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("test.example.com")
|
||||
}).
|
||||
Ensure()
|
||||
})
|
||||
})
|
||||
})
|
83
test/e2e/basic/config.go
Normal file
83
test/e2e/basic/config.go
Normal file
@ -0,0 +1,83 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Config]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
Describe("Template", func() {
|
||||
It("render by env", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
portName := port.GenName("TCP")
|
||||
serverConf += fmt.Sprintf(`
|
||||
token = {{ %s{{ .Envs.FRP_TOKEN }}%s }}
|
||||
`, "`", "`")
|
||||
|
||||
clientConf += fmt.Sprintf(`
|
||||
token = {{ %s{{ .Envs.FRP_TOKEN }}%s }}
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = {{ .%s }}
|
||||
`, "`", "`", framework.TCPEchoServerPort, portName)
|
||||
|
||||
f.SetEnvs([]string{"FRP_TOKEN=123"})
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Includes", func() {
|
||||
It("split tcp proxies into different files", func() {
|
||||
serverPort := f.AllocPort()
|
||||
serverConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = %d
|
||||
`, serverPort))
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
proxyConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
`, f.PortByName(framework.TCPEchoServerPort), remotePort))
|
||||
|
||||
remotePort2 := f.AllocPort()
|
||||
proxyConfigPath2 := f.GenerateConfigFile(fmt.Sprintf(`
|
||||
[tcp2]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
`, f.PortByName(framework.TCPEchoServerPort), remotePort2))
|
||||
|
||||
clientConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
|
||||
[common]
|
||||
server_port = %d
|
||||
includes = %s,%s
|
||||
`, serverPort, proxyConfigPath, proxyConfigPath2))
|
||||
|
||||
_, _, err := f.RunFrps("-c", serverConfigPath)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
_, _, err = f.RunFrpc("-c", clientConfigPath)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
framework.NewRequestExpect(f).Port(remotePort2).Ensure()
|
||||
})
|
||||
})
|
||||
})
|
326
test/e2e/basic/http.go
Normal file
326
test/e2e/basic/http.go
Normal file
@ -0,0 +1,326 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/utils"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: HTTP]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
getDefaultServerConf := func(vhostHTTPPort int) string {
|
||||
conf := consts.DefaultServerConfig + `
|
||||
vhost_http_port = %d
|
||||
`
|
||||
return fmt.Sprintf(conf, vhostHTTPPort)
|
||||
}
|
||||
newHTTPServer := func(port int, respContent string) *httpserver.Server {
|
||||
return httpserver.New(
|
||||
httpserver.WithBindPort(port),
|
||||
httpserver.WithHandler(framework.SpecifiedHTTPBodyHandler([]byte(respContent))),
|
||||
)
|
||||
}
|
||||
|
||||
It("HTTP route by locations", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
fooPort := f.AllocPort()
|
||||
f.RunServer("", newHTTPServer(fooPort, "foo"))
|
||||
|
||||
barPort := f.AllocPort()
|
||||
f.RunServer("", newHTTPServer(barPort, "bar"))
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[foo]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = normal.example.com
|
||||
locations = /,/foo
|
||||
|
||||
[bar]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = normal.example.com
|
||||
locations = /bar
|
||||
`, fooPort, barPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// foo path
|
||||
framework.NewRequestExpect(f).Explain("foo path").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPPath("/foo")
|
||||
}).
|
||||
ExpectResp([]byte("foo")).
|
||||
Ensure()
|
||||
|
||||
// bar path
|
||||
framework.NewRequestExpect(f).Explain("bar path").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPPath("/bar")
|
||||
}).
|
||||
ExpectResp([]byte("bar")).
|
||||
Ensure()
|
||||
|
||||
// other path
|
||||
framework.NewRequestExpect(f).Explain("other path").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPPath("/other")
|
||||
}).
|
||||
ExpectResp([]byte("foo")).
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("HTTP Basic Auth", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = {{ .%s }}
|
||||
custom_domains = normal.example.com
|
||||
http_user = test
|
||||
http_pwd = test
|
||||
`, framework.HTTPSimpleServerPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// not set auth header
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(401))
|
||||
|
||||
// set incorrect auth header
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPHeaders(map[string]string{
|
||||
"Authorization": utils.BasicAuth("test", "invalid"),
|
||||
})
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(401))
|
||||
|
||||
// set correct auth header
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPHeaders(map[string]string{
|
||||
"Authorization": utils.BasicAuth("test", "test"),
|
||||
})
|
||||
}).
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("Wildcard domain", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = {{ .%s }}
|
||||
custom_domains = *.example.com
|
||||
`, framework.HTTPSimpleServerPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// not match host
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("not-match.test.com")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(404))
|
||||
|
||||
// test.example.com match *.example.com
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("test.example.com")
|
||||
}).
|
||||
Ensure()
|
||||
|
||||
// sub.test.example.com match *.example.com
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("sub.test.example.com")
|
||||
}).
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("Subdomain", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
serverConf += `
|
||||
subdomain_host = example.com
|
||||
`
|
||||
|
||||
fooPort := f.AllocPort()
|
||||
f.RunServer("", newHTTPServer(fooPort, "foo"))
|
||||
|
||||
barPort := f.AllocPort()
|
||||
f.RunServer("", newHTTPServer(barPort, "bar"))
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[foo]
|
||||
type = http
|
||||
local_port = %d
|
||||
subdomain = foo
|
||||
|
||||
[bar]
|
||||
type = http
|
||||
local_port = %d
|
||||
subdomain = bar
|
||||
`, fooPort, barPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// foo
|
||||
framework.NewRequestExpect(f).Explain("foo subdomain").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("foo.example.com")
|
||||
}).
|
||||
ExpectResp([]byte("foo")).
|
||||
Ensure()
|
||||
|
||||
// bar
|
||||
framework.NewRequestExpect(f).Explain("bar subdomain").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("bar.example.com")
|
||||
}).
|
||||
ExpectResp([]byte("bar")).
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("Modify headers", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(req.Header.Get("X-From-Where")))
|
||||
})),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = normal.example.com
|
||||
header_X-From-Where = frp
|
||||
`, localPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// not set auth header
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com")
|
||||
}).
|
||||
ExpectResp([]byte("frp")). // local http server will write this X-From-Where header to response body
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("Host Header Rewrite", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(req.Host))
|
||||
})),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = normal.example.com
|
||||
host_header_rewrite = rewrite.example.com
|
||||
`, localPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com")
|
||||
}).
|
||||
ExpectResp([]byte("rewrite.example.com")). // local http server will write host header to response body
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("Websocket protocol", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
upgrader := websocket.Upgrader{}
|
||||
|
||||
localPort := f.AllocPort()
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
c, err := upgrader.Upgrade(w, req, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
for {
|
||||
mt, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
err = c.WriteMessage(mt, message)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
})),
|
||||
)
|
||||
|
||||
f.RunServer("", localServer)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = 127.0.0.1
|
||||
`, localPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
u := url.URL{Scheme: "ws", Host: "127.0.0.1:" + strconv.Itoa(vhostHTTPPort)}
|
||||
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
err = c.WriteMessage(websocket.TextMessage, []byte(consts.TestString))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
_, msg, err := c.ReadMessage()
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqualValues(consts.TestString, string(msg))
|
||||
})
|
||||
})
|
@ -2,11 +2,14 @@ package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
clientsdk "github.com/fatedier/frp/test/e2e/pkg/sdk/client"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
@ -19,10 +22,10 @@ var _ = Describe("[Feature: Server Manager]", func() {
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
serverConf += `
|
||||
allow_ports = 10000-20000,20002,30000-50000
|
||||
allow_ports = 20000-25000,25002,30000-50000
|
||||
`
|
||||
|
||||
tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 20000))
|
||||
tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000))
|
||||
udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000))
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp-allowded-in-range]
|
||||
@ -62,18 +65,83 @@ var _ = Describe("[Feature: Server Manager]", func() {
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
|
||||
|
||||
// Not Allowed
|
||||
framework.NewRequestExpect(f).RequestModify(framework.SetRequestPort(20001)).ExpectError(true).Ensure()
|
||||
framework.NewRequestExpect(f).Port(25003).ExpectError(true).Ensure()
|
||||
|
||||
// Unavailable, already bind by frps
|
||||
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
|
||||
|
||||
// UDP
|
||||
// Allowed in range
|
||||
framework.NewRequestExpect(f).RequestModify(framework.SetRequestProtocol("udp")).PortName(udpPortName).Ensure()
|
||||
framework.NewRequestExpect(f).Protocol("udp").PortName(udpPortName).Ensure()
|
||||
|
||||
// Not Allowed
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.UDP().Port(20003)
|
||||
r.UDP().Port(25003)
|
||||
}).ExpectError(true).Ensure()
|
||||
})
|
||||
|
||||
It("Alloc Random Port", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
adminPort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
admin_port = %d
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
|
||||
[udp]
|
||||
type = udp
|
||||
local_port = {{ .%s }}
|
||||
`, adminPort, framework.TCPEchoServerPort, framework.UDPEchoServerPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
client := clientsdk.New("127.0.0.1", adminPort)
|
||||
|
||||
// tcp random port
|
||||
status, err := client.GetProxyStatus("tcp")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
_, portStr, err := net.SplitHostPort(status.RemoteAddr)
|
||||
framework.ExpectNoError(err)
|
||||
port, err := strconv.Atoi(portStr)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Port(port).Ensure()
|
||||
|
||||
// udp random port
|
||||
status, err = client.GetProxyStatus("udp")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
_, portStr, err = net.SplitHostPort(status.RemoteAddr)
|
||||
framework.ExpectNoError(err)
|
||||
port, err = strconv.Atoi(portStr)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
framework.NewRequestExpect(f).Protocol("udp").Port(port).Ensure()
|
||||
})
|
||||
|
||||
It("Port Reuse", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
// Use same port as PortServer
|
||||
serverConf += fmt.Sprintf(`
|
||||
vhost_http_port = {{ .%s }}
|
||||
`, consts.PortServerName)
|
||||
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
[http]
|
||||
type = http
|
||||
local_port = {{ .%s }}
|
||||
custom_domains = example.com
|
||||
`, framework.HTTPSimpleServerPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("example.com")
|
||||
}).PortName(consts.PortServerName).Ensure()
|
||||
})
|
||||
})
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
// test source
|
||||
_ "github.com/fatedier/frp/test/e2e/basic"
|
||||
_ "github.com/fatedier/frp/test/e2e/features"
|
||||
_ "github.com/fatedier/frp/test/e2e/plugin"
|
||||
|
||||
_ "github.com/onsi/ginkgo"
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
@ -18,17 +17,17 @@ var _ = Describe("[Feature: Example]", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
portName := port.GenName("TCP")
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = {{ .%s }}
|
||||
`, framework.TCPEchoServerPort, portName)
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
47
test/e2e/features/bandwidth_limit.go
Normal file
47
test/e2e/features/bandwidth_limit.go
Normal file
@ -0,0 +1,47 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Bandwidth Limit]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
It("Proxy Bandwidth Limit", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
localPort := f.AllocPort()
|
||||
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort))
|
||||
f.RunServer("", localServer)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
bandwidth_limit = 10KB
|
||||
`, localPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
content := strings.Repeat("a", 50*1024) // 5KB
|
||||
start := time.Now()
|
||||
framework.NewRequestExpect(f).Port(remotePort).RequestModify(func(r *request.Request) {
|
||||
r.Body([]byte(content)).Timeout(30 * time.Second)
|
||||
}).ExpectResp([]byte(content)).Ensure()
|
||||
duration := time.Now().Sub(start)
|
||||
|
||||
framework.ExpectTrue(duration.Seconds() > 7, "100Kb with 10KB limit, want > 7 seconds, but got %d seconds", duration.Seconds())
|
||||
})
|
||||
})
|
65
test/e2e/features/chaos.go
Normal file
65
test/e2e/features/chaos.go
Normal file
@ -0,0 +1,65 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Chaos]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
It("reconnect after frps restart", func() {
|
||||
serverPort := f.AllocPort()
|
||||
serverConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = %d
|
||||
`, serverPort))
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConfigPath := f.GenerateConfigFile(fmt.Sprintf(`
|
||||
[common]
|
||||
server_port = %d
|
||||
log_level = trace
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
`, serverPort, f.PortByName(framework.TCPEchoServerPort), remotePort))
|
||||
|
||||
// 1. start frps and frpc, expect request success
|
||||
ps, _, err := f.RunFrps("-c", serverConfigPath)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
pc, _, err := f.RunFrpc("-c", clientConfigPath)
|
||||
framework.ExpectNoError(err)
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
// 2. stop frps, expect request failed
|
||||
ps.Stop()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
||||
|
||||
// 3. restart frps, expect request success
|
||||
_, _, err = f.RunFrps("-c", serverConfigPath)
|
||||
framework.ExpectNoError(err)
|
||||
time.Sleep(2 * time.Second)
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
// 4. stop frpc, expect request failed
|
||||
pc.Stop()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
||||
|
||||
// 5. restart frpc, expect request success
|
||||
_, _, err = f.RunFrpc("-c", clientConfigPath)
|
||||
framework.ExpectNoError(err)
|
||||
time.Sleep(time.Second)
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
})
|
||||
})
|
238
test/e2e/features/group.go
Normal file
238
test/e2e/features/group.go
Normal file
@ -0,0 +1,238 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Group]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
newHTTPServer := func(port int, respContent string) *httpserver.Server {
|
||||
return httpserver.New(
|
||||
httpserver.WithBindPort(port),
|
||||
httpserver.WithHandler(framework.SpecifiedHTTPBodyHandler([]byte(respContent))),
|
||||
)
|
||||
}
|
||||
|
||||
validateFooBarResponse := func(resp *request.Response) bool {
|
||||
if string(resp.Content) == "foo" || string(resp.Content) == "bar" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
doFooBarHTTPRequest := func(vhostPort int, host string) []string {
|
||||
results := []string{}
|
||||
var wait sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
expectFn := func() {
|
||||
framework.NewRequestExpect(f).Port(vhostPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost(host)
|
||||
}).
|
||||
Ensure(validateFooBarResponse, func(resp *request.Response) bool {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
results = append(results, string(resp.Content))
|
||||
return true
|
||||
})
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
expectFn()
|
||||
}()
|
||||
}
|
||||
|
||||
wait.Wait()
|
||||
return results
|
||||
}
|
||||
|
||||
Describe("Load Balancing", func() {
|
||||
It("TCP", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
fooPort := f.AllocPort()
|
||||
fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo")))
|
||||
f.RunServer("", fooServer)
|
||||
|
||||
barPort := f.AllocPort()
|
||||
barServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(barPort), streamserver.WithRespContent([]byte("bar")))
|
||||
f.RunServer("", barServer)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[foo]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
group = test
|
||||
group_key = 123
|
||||
|
||||
[bar]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
group = test
|
||||
group_key = 123
|
||||
`, fooPort, remotePort, barPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
fooCount := 0
|
||||
barCount := 0
|
||||
for i := 0; i < 10; i++ {
|
||||
framework.NewRequestExpect(f).Explain("times " + strconv.Itoa(i)).Port(remotePort).Ensure(func(resp *request.Response) bool {
|
||||
switch string(resp.Content) {
|
||||
case "foo":
|
||||
fooCount++
|
||||
case "bar":
|
||||
barCount++
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
framework.ExpectTrue(fooCount > 1 && barCount > 1, "fooCount: %d, barCount: %d", fooCount, barCount)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Health Check", func() {
|
||||
It("TCP", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
fooPort := f.AllocPort()
|
||||
fooServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(fooPort), streamserver.WithRespContent([]byte("foo")))
|
||||
f.RunServer("", fooServer)
|
||||
|
||||
barPort := f.AllocPort()
|
||||
barServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(barPort), streamserver.WithRespContent([]byte("bar")))
|
||||
f.RunServer("", barServer)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[foo]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
group = test
|
||||
group_key = 123
|
||||
health_check_type = tcp
|
||||
health_check_interval_s = 1
|
||||
|
||||
[bar]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
group = test
|
||||
group_key = 123
|
||||
health_check_type = tcp
|
||||
health_check_interval_s = 1
|
||||
`, fooPort, remotePort, barPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// check foo and bar is ok
|
||||
results := []string{}
|
||||
for i := 0; i < 10; i++ {
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
|
||||
results = append(results, string(resp.Content))
|
||||
return true
|
||||
})
|
||||
}
|
||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||
|
||||
// close bar server, check foo is ok
|
||||
barServer.Close()
|
||||
time.Sleep(2 * time.Second)
|
||||
for i := 0; i < 10; i++ {
|
||||
framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure()
|
||||
}
|
||||
|
||||
// resume bar server, check foo and bar is ok
|
||||
f.RunServer("", barServer)
|
||||
time.Sleep(2 * time.Second)
|
||||
results = []string{}
|
||||
for i := 0; i < 10; i++ {
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
|
||||
results = append(results, string(resp.Content))
|
||||
return true
|
||||
})
|
||||
}
|
||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||
})
|
||||
|
||||
It("HTTP", func() {
|
||||
vhostPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
vhost_http_port = %d
|
||||
`, vhostPort)
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
fooPort := f.AllocPort()
|
||||
fooServer := newHTTPServer(fooPort, "foo")
|
||||
f.RunServer("", fooServer)
|
||||
|
||||
barPort := f.AllocPort()
|
||||
barServer := newHTTPServer(barPort, "bar")
|
||||
f.RunServer("", barServer)
|
||||
|
||||
clientConf += fmt.Sprintf(`
|
||||
[foo]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = example.com
|
||||
group = test
|
||||
group_key = 123
|
||||
health_check_type = http
|
||||
health_check_interval_s = 1
|
||||
health_check_url = /healthz
|
||||
|
||||
[bar]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = example.com
|
||||
group = test
|
||||
group_key = 123
|
||||
health_check_type = http
|
||||
health_check_interval_s = 1
|
||||
health_check_url = /healthz
|
||||
`, fooPort, barPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// check foo and bar is ok
|
||||
results := doFooBarHTTPRequest(vhostPort, "example.com")
|
||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||
|
||||
// close bar server, check foo is ok
|
||||
barServer.Close()
|
||||
time.Sleep(2 * time.Second)
|
||||
results = doFooBarHTTPRequest(vhostPort, "example.com")
|
||||
framework.ExpectContainElements(results, []string{"foo"})
|
||||
framework.ExpectNotContainElements(results, []string{"bar"})
|
||||
|
||||
// resume bar server, check foo and bar is ok
|
||||
f.RunServer("", barServer)
|
||||
time.Sleep(2 * time.Second)
|
||||
results = doFooBarHTTPRequest(vhostPort, "example.com")
|
||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||
})
|
||||
})
|
||||
})
|
52
test/e2e/features/monitor.go
Normal file
52
test/e2e/features/monitor.go
Normal file
@ -0,0 +1,52 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Monitor]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
It("Prometheus metrics", func() {
|
||||
dashboardPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
enable_prometheus = true
|
||||
dashboard_addr = 0.0.0.0
|
||||
dashboard_port = %d
|
||||
`, dashboardPort)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Port(dashboardPort).HTTPPath("/metrics")
|
||||
}).Ensure(func(resp *request.Response) bool {
|
||||
log.Trace("prometheus metrics response: \n%s", resp.Content)
|
||||
if resp.Code != 200 {
|
||||
return false
|
||||
}
|
||||
if !strings.Contains(string(resp.Content), "traffic_in") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
152
test/e2e/features/real_ip.go
Normal file
152
test/e2e/features/real_ip.go
Normal file
@ -0,0 +1,152 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/rpc"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
pp "github.com/pires/go-proxyproto"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Real IP]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
It("HTTP X-Forwarded-For", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
vhost_http_port = %d
|
||||
`, vhostHTTPPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(req.Header.Get("X-Forwarded-For")))
|
||||
})),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = normal.example.com
|
||||
`, localPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com")
|
||||
}).
|
||||
ExpectResp([]byte("127.0.0.1")).
|
||||
Ensure()
|
||||
|
||||
})
|
||||
|
||||
Describe("Proxy Protocol", func() {
|
||||
It("TCP", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
localPort := f.AllocPort()
|
||||
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort),
|
||||
streamserver.WithCustomHandler(func(c net.Conn) {
|
||||
defer c.Close()
|
||||
rd := bufio.NewReader(c)
|
||||
ppHeader, err := pp.Read(rd)
|
||||
if err != nil {
|
||||
log.Error("read proxy protocol error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if _, err := rpc.ReadBytes(rd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := []byte(ppHeader.SourceAddr.String())
|
||||
rpc.WriteBytes(c, buf)
|
||||
}
|
||||
}))
|
||||
f.RunServer("", localServer)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = %d
|
||||
remote_port = %d
|
||||
proxy_protocol_version = v2
|
||||
`, localPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure(func(resp *request.Response) bool {
|
||||
log.Trace("ProxyProtocol get SourceAddr: %s", string(resp.Content))
|
||||
addr, err := net.ResolveTCPAddr("tcp", string(resp.Content))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if addr.IP.String() != "127.0.0.1" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
It("HTTP", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
vhost_http_port = %d
|
||||
`, vhostHTTPPort)
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
localPort := f.AllocPort()
|
||||
var srcAddrRecord string
|
||||
localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort),
|
||||
streamserver.WithCustomHandler(func(c net.Conn) {
|
||||
defer c.Close()
|
||||
rd := bufio.NewReader(c)
|
||||
ppHeader, err := pp.Read(rd)
|
||||
if err != nil {
|
||||
log.Error("read proxy protocol error: %v", err)
|
||||
return
|
||||
}
|
||||
srcAddrRecord = ppHeader.SourceAddr.String()
|
||||
}))
|
||||
f.RunServer("", localServer)
|
||||
|
||||
clientConf += fmt.Sprintf(`
|
||||
[test]
|
||||
type = http
|
||||
local_port = %d
|
||||
custom_domains = normal.example.com
|
||||
proxy_protocol_version = v2
|
||||
`, localPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com")
|
||||
}).Ensure(framework.ExpectResponseCode(404))
|
||||
|
||||
log.Trace("ProxyProtocol get SourceAddr: %s", srcAddrRecord)
|
||||
addr, err := net.ResolveTCPAddr("tcp", srcAddrRecord)
|
||||
framework.ExpectNoError(err, srcAddrRecord)
|
||||
framework.ExpectEqualValues("127.0.0.1", addr.IP.String())
|
||||
})
|
||||
})
|
||||
})
|
@ -14,6 +14,10 @@ func ExpectEqualValues(actual interface{}, extra interface{}, explain ...interfa
|
||||
gomega.ExpectWithOffset(1, actual).To(gomega.BeEquivalentTo(extra), explain...)
|
||||
}
|
||||
|
||||
func ExpectEqualValuesWithOffset(offset int, actual interface{}, extra interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1+offset, actual).To(gomega.BeEquivalentTo(extra), explain...)
|
||||
}
|
||||
|
||||
// ExpectNotEqual expects the specified two are not the same, otherwise an exception raises
|
||||
func ExpectNotEqual(actual interface{}, extra interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1, actual).NotTo(gomega.Equal(extra), explain...)
|
||||
@ -24,6 +28,10 @@ func ExpectError(err error, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1, err).To(gomega.HaveOccurred(), explain...)
|
||||
}
|
||||
|
||||
func ExpectErrorWithOffset(offset int, err error, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1+offset, err).To(gomega.HaveOccurred(), explain...)
|
||||
}
|
||||
|
||||
// ExpectNoError checks if "err" is set, and if so, fails assertion while logging the error.
|
||||
func ExpectNoError(err error, explain ...interface{}) {
|
||||
ExpectNoErrorWithOffset(1, err, explain...)
|
||||
@ -40,6 +48,14 @@ func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface
|
||||
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
|
||||
}
|
||||
|
||||
func ExpectContainElements(actual interface{}, extra interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1, actual).To(gomega.ContainElements(extra), explain...)
|
||||
}
|
||||
|
||||
func ExpectNotContainElements(actual interface{}, extra interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1, actual).NotTo(gomega.ContainElements(extra), explain...)
|
||||
}
|
||||
|
||||
// ExpectHaveKey expects the actual map has the key in the keyset
|
||||
func ExpectHaveKey(actual interface{}, key interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1, actual).To(gomega.HaveKey(key), explain...)
|
||||
@ -53,3 +69,7 @@ func ExpectEmpty(actual interface{}, explain ...interface{}) {
|
||||
func ExpectTrue(actual interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1, actual).Should(gomega.BeTrue(), explain...)
|
||||
}
|
||||
|
||||
func ExpectTrueWithOffset(offset int, actual interface{}, explain ...interface{}) {
|
||||
gomega.ExpectWithOffset(1+offset, actual).Should(gomega.BeTrue(), explain...)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
@ -30,6 +31,9 @@ type Framework struct {
|
||||
// ports used in this framework indexed by port name.
|
||||
usedPorts map[string]int
|
||||
|
||||
// record ports alloced by this framework and release them after each test
|
||||
allocedPorts []int
|
||||
|
||||
// portAllocator to alloc port for this test case.
|
||||
portAllocator *port.Allocator
|
||||
|
||||
@ -50,7 +54,13 @@ type Framework struct {
|
||||
clientProcesses []*process.Process
|
||||
|
||||
// Manual registered mock servers.
|
||||
servers []*server.Server
|
||||
servers []server.Server
|
||||
|
||||
// used to generate unique config file name.
|
||||
configFileIndex int64
|
||||
|
||||
// envs used to start processes, the form is `key=value`.
|
||||
osEnvs []string
|
||||
}
|
||||
|
||||
func NewDefaultFramework() *Framework {
|
||||
@ -80,7 +90,7 @@ func (f *Framework) BeforeEach() {
|
||||
|
||||
f.cleanupHandle = AddCleanupAction(f.AfterEach)
|
||||
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "frpe2e-test-*")
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "frp-e2e-test-*")
|
||||
ExpectNoError(err)
|
||||
f.TempDirectory = dir
|
||||
|
||||
@ -88,6 +98,14 @@ func (f *Framework) BeforeEach() {
|
||||
if err := f.mockServers.Run(); err != nil {
|
||||
Failf("%v", err)
|
||||
}
|
||||
|
||||
params := f.mockServers.GetTemplateParams()
|
||||
for k, v := range params {
|
||||
switch t := v.(type) {
|
||||
case int:
|
||||
f.usedPorts[k] = int(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Framework) AfterEach() {
|
||||
@ -126,14 +144,23 @@ func (f *Framework) AfterEach() {
|
||||
// clean directory
|
||||
os.RemoveAll(f.TempDirectory)
|
||||
f.TempDirectory = ""
|
||||
f.serverConfPaths = nil
|
||||
f.clientConfPaths = nil
|
||||
f.serverConfPaths = []string{}
|
||||
f.clientConfPaths = []string{}
|
||||
|
||||
// release used ports
|
||||
for _, port := range f.usedPorts {
|
||||
f.portAllocator.Release(port)
|
||||
}
|
||||
f.usedPorts = make(map[string]int)
|
||||
|
||||
// release alloced ports
|
||||
for _, port := range f.allocedPorts {
|
||||
f.portAllocator.Release(port)
|
||||
}
|
||||
f.allocedPorts = make([]int, 0)
|
||||
|
||||
// clear os envs
|
||||
f.osEnvs = make([]string, 0)
|
||||
}
|
||||
|
||||
var portRegex = regexp.MustCompile(`{{ \.Port.*? }}`)
|
||||
@ -210,6 +237,7 @@ func (f *Framework) PortByName(name string) int {
|
||||
func (f *Framework) AllocPort() int {
|
||||
port := f.portAllocator.Get()
|
||||
ExpectTrue(port > 0, "alloc port failed")
|
||||
f.allocedPorts = append(f.allocedPorts, port)
|
||||
return port
|
||||
}
|
||||
|
||||
@ -217,11 +245,22 @@ func (f *Framework) ReleasePort(port int) {
|
||||
f.portAllocator.Release(port)
|
||||
}
|
||||
|
||||
func (f *Framework) RunServer(portName string, s *server.Server) {
|
||||
func (f *Framework) RunServer(portName string, s server.Server) {
|
||||
f.servers = append(f.servers, s)
|
||||
if s.BindPort() > 0 {
|
||||
if s.BindPort() > 0 && portName != "" {
|
||||
f.usedPorts[portName] = s.BindPort()
|
||||
}
|
||||
err := s.Run()
|
||||
ExpectNoError(err, portName)
|
||||
ExpectNoError(err, "RunServer: with PortName %s", portName)
|
||||
}
|
||||
|
||||
func (f *Framework) SetEnvs(envs []string) {
|
||||
f.osEnvs = envs
|
||||
}
|
||||
|
||||
func (f *Framework) WriteTempFile(name string, content string) string {
|
||||
filePath := filepath.Join(f.TempDirectory, name)
|
||||
err := ioutil.WriteFile(filePath, []byte(content), 0766)
|
||||
ExpectNoError(err)
|
||||
return filePath
|
||||
}
|
||||
|
@ -2,35 +2,45 @@ package framework
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/streamserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
)
|
||||
|
||||
const (
|
||||
TCPEchoServerPort = "TCPEchoServerPort"
|
||||
UDPEchoServerPort = "UDPEchoServerPort"
|
||||
UDSEchoServerAddr = "UDSEchoServerAddr"
|
||||
TCPEchoServerPort = "TCPEchoServerPort"
|
||||
UDPEchoServerPort = "UDPEchoServerPort"
|
||||
UDSEchoServerAddr = "UDSEchoServerAddr"
|
||||
HTTPSimpleServerPort = "HTTPSimpleServerPort"
|
||||
)
|
||||
|
||||
type MockServers struct {
|
||||
tcpEchoServer *server.Server
|
||||
udpEchoServer *server.Server
|
||||
udsEchoServer *server.Server
|
||||
tcpEchoServer server.Server
|
||||
udpEchoServer server.Server
|
||||
udsEchoServer server.Server
|
||||
httpSimpleServer server.Server
|
||||
}
|
||||
|
||||
func NewMockServers(portAllocator *port.Allocator) *MockServers {
|
||||
s := &MockServers{}
|
||||
tcpPort := portAllocator.Get()
|
||||
udpPort := portAllocator.Get()
|
||||
s.tcpEchoServer = server.New(server.TCP, server.WithBindPort(tcpPort), server.WithEchoMode(true))
|
||||
s.udpEchoServer = server.New(server.UDP, server.WithBindPort(udpPort), server.WithEchoMode(true))
|
||||
httpPort := portAllocator.Get()
|
||||
s.tcpEchoServer = streamserver.New(streamserver.TCP, streamserver.WithBindPort(tcpPort))
|
||||
s.udpEchoServer = streamserver.New(streamserver.UDP, streamserver.WithBindPort(udpPort))
|
||||
s.httpSimpleServer = httpserver.New(httpserver.WithBindPort(httpPort), httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(consts.TestString))
|
||||
})))
|
||||
|
||||
udsIndex := portAllocator.Get()
|
||||
udsAddr := fmt.Sprintf("%s/frp_echo_server_%d.sock", os.TempDir(), udsIndex)
|
||||
os.Remove(udsAddr)
|
||||
s.udsEchoServer = server.New(server.Unix, server.WithBindAddr(udsAddr), server.WithEchoMode(true))
|
||||
s.udsEchoServer = streamserver.New(streamserver.Unix, streamserver.WithBindAddr(udsAddr))
|
||||
return s
|
||||
}
|
||||
|
||||
@ -44,6 +54,9 @@ func (m *MockServers) Run() error {
|
||||
if err := m.udsEchoServer.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.httpSimpleServer.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -51,6 +64,7 @@ func (m *MockServers) Close() {
|
||||
m.tcpEchoServer.Close()
|
||||
m.udpEchoServer.Close()
|
||||
m.udsEchoServer.Close()
|
||||
m.httpSimpleServer.Close()
|
||||
os.Remove(m.udsEchoServer.BindAddr())
|
||||
}
|
||||
|
||||
@ -59,6 +73,7 @@ func (m *MockServers) GetTemplateParams() map[string]interface{} {
|
||||
ret[TCPEchoServerPort] = m.tcpEchoServer.BindPort()
|
||||
ret[UDPEchoServerPort] = m.udpEchoServer.BindPort()
|
||||
ret[UDSEchoServerAddr] = m.udsEchoServer.BindAddr()
|
||||
ret[HTTPSimpleServerPort] = m.httpSimpleServer.BindPort()
|
||||
return ret
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,6 @@ import (
|
||||
"github.com/fatedier/frp/test/e2e/pkg/process"
|
||||
)
|
||||
|
||||
func GenerateConfigFile(path string, content string) error {
|
||||
return ioutil.WriteFile(path, []byte(content), 0666)
|
||||
}
|
||||
|
||||
// RunProcesses run multiple processes from templates.
|
||||
// The first template should always be frps.
|
||||
func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []string) {
|
||||
@ -37,7 +33,8 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
||||
err = ioutil.WriteFile(path, []byte(outs[i]), 0666)
|
||||
ExpectNoError(err)
|
||||
flog.Trace("[%s] %s", path, outs[i])
|
||||
p := process.New(TestContext.FRPServerPath, []string{"-c", path})
|
||||
|
||||
p := process.NewWithEnvs(TestContext.FRPServerPath, []string{"-c", path}, f.osEnvs)
|
||||
f.serverConfPaths = append(f.serverConfPaths, path)
|
||||
f.serverProcesses = append(f.serverProcesses, p)
|
||||
err = p.Start()
|
||||
@ -51,7 +48,8 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
||||
err = ioutil.WriteFile(path, []byte(outs[index]), 0666)
|
||||
ExpectNoError(err)
|
||||
flog.Trace("[%s] %s", path, outs[index])
|
||||
p := process.New(TestContext.FRPClientPath, []string{"-c", path})
|
||||
|
||||
p := process.NewWithEnvs(TestContext.FRPClientPath, []string{"-c", path}, f.osEnvs)
|
||||
f.clientConfPaths = append(f.clientConfPaths, path)
|
||||
f.clientProcesses = append(f.clientProcesses, p)
|
||||
err = p.Start()
|
||||
@ -60,3 +58,34 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
func (f *Framework) RunFrps(args ...string) (*process.Process, string, error) {
|
||||
p := process.NewWithEnvs(TestContext.FRPServerPath, args, f.osEnvs)
|
||||
f.serverProcesses = append(f.serverProcesses, p)
|
||||
err := p.Start()
|
||||
if err != nil {
|
||||
return p, p.StdOutput(), err
|
||||
}
|
||||
// sleep for a while to get std output
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
return p, p.StdOutput(), nil
|
||||
}
|
||||
|
||||
func (f *Framework) RunFrpc(args ...string) (*process.Process, string, error) {
|
||||
p := process.NewWithEnvs(TestContext.FRPClientPath, args, f.osEnvs)
|
||||
f.clientProcesses = append(f.clientProcesses, p)
|
||||
err := p.Start()
|
||||
if err != nil {
|
||||
return p, p.StdOutput(), err
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
return p, p.StdOutput(), nil
|
||||
}
|
||||
|
||||
func (f *Framework) GenerateConfigFile(content string) string {
|
||||
f.configFileIndex++
|
||||
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-config-%d", f.configFileIndex))
|
||||
err := ioutil.WriteFile(path, []byte(content), 0666)
|
||||
ExpectNoError(err)
|
||||
return path
|
||||
}
|
||||
|
@ -1,38 +1,39 @@
|
||||
package framework
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
|
||||
flog "github.com/fatedier/frp/pkg/util/log"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
)
|
||||
|
||||
func SetRequestProtocol(protocol string) func(*request.Request) {
|
||||
return func(r *request.Request) {
|
||||
r.Protocol(protocol)
|
||||
func SpecifiedHTTPBodyHandler(body []byte) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write(body)
|
||||
}
|
||||
}
|
||||
|
||||
func SetRequestPort(port int) func(*request.Request) {
|
||||
return func(r *request.Request) {
|
||||
r.Port(port)
|
||||
func ExpectResponseCode(code int) EnsureFunc {
|
||||
return func(resp *request.Response) bool {
|
||||
if resp.Code == code {
|
||||
return true
|
||||
}
|
||||
flog.Warn("Expect code %d, but got %d", code, resp.Code)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// NewRequest return a default TCP request with default timeout and content.
|
||||
// NewRequest return a default request with default timeout and content.
|
||||
func NewRequest() *request.Request {
|
||||
return request.New().
|
||||
Timeout(consts.DefaultTimeout).
|
||||
Body([]byte(consts.TestString))
|
||||
}
|
||||
|
||||
func ExpectResponse(req *request.Request, expectResp []byte, explain ...interface{}) {
|
||||
ret, err := req.Do()
|
||||
ExpectNoError(err, explain...)
|
||||
ExpectEqualValues(expectResp, ret, explain...)
|
||||
}
|
||||
|
||||
func ExpectResponseError(req *request.Request, explain ...interface{}) {
|
||||
_, err := req.Do()
|
||||
ExpectError(err, explain...)
|
||||
func NewHTTPRequest() *request.Request {
|
||||
return request.New().HTTP().HTTPParams("GET", "", "/", nil)
|
||||
}
|
||||
|
||||
type RequestExpect struct {
|
||||
@ -54,11 +55,21 @@ func NewRequestExpect(f *Framework) *RequestExpect {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *RequestExpect) Request(req *request.Request) *RequestExpect {
|
||||
e.req = req
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RequestExpect) RequestModify(f func(r *request.Request)) *RequestExpect {
|
||||
f(e.req)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RequestExpect) Protocol(protocol string) *RequestExpect {
|
||||
e.req.Protocol(protocol)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RequestExpect) PortName(name string) *RequestExpect {
|
||||
if e.f != nil {
|
||||
e.req.Port(e.f.PortByName(name))
|
||||
@ -66,6 +77,13 @@ func (e *RequestExpect) PortName(name string) *RequestExpect {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RequestExpect) Port(port int) *RequestExpect {
|
||||
if e.f != nil {
|
||||
e.req.Port(port)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RequestExpect) ExpectResp(resp []byte) *RequestExpect {
|
||||
e.expectResp = resp
|
||||
return e
|
||||
@ -81,10 +99,32 @@ func (e *RequestExpect) Explain(explain ...interface{}) *RequestExpect {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RequestExpect) Ensure() {
|
||||
type EnsureFunc func(*request.Response) bool
|
||||
|
||||
func (e *RequestExpect) Ensure(fns ...EnsureFunc) {
|
||||
ret, err := e.req.Do()
|
||||
if e.expectError {
|
||||
ExpectResponseError(e.req, e.explain...)
|
||||
ExpectErrorWithOffset(1, err, e.explain...)
|
||||
return
|
||||
}
|
||||
ExpectNoErrorWithOffset(1, err, e.explain...)
|
||||
|
||||
if len(fns) == 0 {
|
||||
if !bytes.Equal(e.expectResp, ret.Content) {
|
||||
flog.Trace("Response info: %+v", ret)
|
||||
}
|
||||
ExpectEqualValuesWithOffset(1, e.expectResp, ret.Content, e.explain...)
|
||||
} else {
|
||||
ExpectResponse(e.req, e.expectResp, e.explain...)
|
||||
for _, fn := range fns {
|
||||
ok := fn(ret)
|
||||
if !ok {
|
||||
flog.Trace("Response info: %+v", ret)
|
||||
}
|
||||
ExpectTrueWithOffset(1, ok, e.explain...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *RequestExpect) Do() (*request.Response, error) {
|
||||
return e.req.Do()
|
||||
}
|
||||
|
@ -9,6 +9,5 @@ import (
|
||||
var RunID string
|
||||
|
||||
func init() {
|
||||
uuid, _ := uuid.NewUUID()
|
||||
RunID = uuid.String()
|
||||
RunID = uuid.NewString()
|
||||
}
|
||||
|
110
test/e2e/mock/server/httpserver/server.go
Normal file
110
test/e2e/mock/server/httpserver/server.go
Normal file
@ -0,0 +1,110 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
bindAddr string
|
||||
bindPort int
|
||||
handler http.Handler
|
||||
|
||||
l net.Listener
|
||||
tlsConfig *tls.Config
|
||||
hs *http.Server
|
||||
}
|
||||
|
||||
type Option func(*Server) *Server
|
||||
|
||||
func New(options ...Option) *Server {
|
||||
s := &Server{
|
||||
bindAddr: "127.0.0.1",
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
s = option(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func WithBindAddr(addr string) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.bindAddr = addr
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func WithBindPort(port int) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.bindPort = port
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func WithTlsConfig(tlsConfig *tls.Config) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.tlsConfig = tlsConfig
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func WithHandler(h http.Handler) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.handler = h
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func WithResponse(resp []byte) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(resp)
|
||||
})
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
if err := s.initListener(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr := net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort))
|
||||
hs := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: s.handler,
|
||||
TLSConfig: s.tlsConfig,
|
||||
}
|
||||
|
||||
s.hs = hs
|
||||
if s.tlsConfig == nil {
|
||||
go hs.Serve(s.l)
|
||||
} else {
|
||||
go hs.ServeTLS(s.l, "", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
if s.hs != nil {
|
||||
return s.hs.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) initListener() (err error) {
|
||||
s.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.bindAddr, s.bindPort))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) BindAddr() string {
|
||||
return s.bindAddr
|
||||
}
|
||||
|
||||
func (s *Server) BindPort() int {
|
||||
return s.bindPort
|
||||
}
|
8
test/e2e/mock/server/interface.go
Normal file
8
test/e2e/mock/server/interface.go
Normal file
@ -0,0 +1,8 @@
|
||||
package server
|
||||
|
||||
type Server interface {
|
||||
Run() error
|
||||
Close() error
|
||||
BindAddr() string
|
||||
BindPort() int
|
||||
}
|
@ -1,40 +1,42 @@
|
||||
package server
|
||||
package streamserver
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
libnet "github.com/fatedier/frp/pkg/util/net"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/rpc"
|
||||
)
|
||||
|
||||
type ServerType string
|
||||
type Type string
|
||||
|
||||
const (
|
||||
TCP ServerType = "tcp"
|
||||
UDP ServerType = "udp"
|
||||
Unix ServerType = "unix"
|
||||
TCP Type = "tcp"
|
||||
UDP Type = "udp"
|
||||
Unix Type = "unix"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
netType ServerType
|
||||
netType Type
|
||||
bindAddr string
|
||||
bindPort int
|
||||
respContent []byte
|
||||
bufSize int64
|
||||
|
||||
echoMode bool
|
||||
handler func(net.Conn)
|
||||
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
type Option func(*Server) *Server
|
||||
|
||||
func New(netType ServerType, options ...Option) *Server {
|
||||
func New(netType Type, options ...Option) *Server {
|
||||
s := &Server{
|
||||
netType: netType,
|
||||
bindAddr: "127.0.0.1",
|
||||
bufSize: 2048,
|
||||
}
|
||||
s.handler = s.handle
|
||||
|
||||
for _, option := range options {
|
||||
s = option(s)
|
||||
@ -63,16 +65,9 @@ func WithRespContent(content []byte) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithBufSize(bufSize int64) Option {
|
||||
func WithCustomHandler(handler func(net.Conn)) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.bufSize = bufSize
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func WithEchoMode(echoMode bool) Option {
|
||||
return func(s *Server) *Server {
|
||||
s.echoMode = echoMode
|
||||
s.handler = handler
|
||||
return s
|
||||
}
|
||||
}
|
||||
@ -88,7 +83,7 @@ func (s *Server) Run() error {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go s.handle(c)
|
||||
go s.handler(c)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
@ -118,18 +113,20 @@ func (s *Server) initListener() (err error) {
|
||||
func (s *Server) handle(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
buf := make([]byte, s.bufSize)
|
||||
var reader io.Reader = c
|
||||
if s.netType == UDP {
|
||||
reader = bufio.NewReader(c)
|
||||
}
|
||||
for {
|
||||
n, err := c.Read(buf)
|
||||
buf, err := rpc.ReadBytes(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.echoMode {
|
||||
c.Write(buf[:n])
|
||||
} else {
|
||||
c.Write(s.respContent)
|
||||
if len(s.respContent) > 0 {
|
||||
buf = s.respContent
|
||||
}
|
||||
rpc.WriteBytes(c, buf)
|
||||
}
|
||||
}
|
||||
|
68
test/e2e/pkg/cert/generator.go
Normal file
68
test/e2e/pkg/cert/generator.go
Normal file
@ -0,0 +1,68 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Artifacts hosts a private key, its corresponding serving certificate and
|
||||
// the CA certificate that signs the serving certificate.
|
||||
type Artifacts struct {
|
||||
// PEM encoded private key
|
||||
Key []byte
|
||||
// PEM encoded serving certificate
|
||||
Cert []byte
|
||||
// PEM encoded CA private key
|
||||
CAKey []byte
|
||||
// PEM encoded CA certificate
|
||||
CACert []byte
|
||||
// Resource version of the certs
|
||||
ResourceVersion string
|
||||
}
|
||||
|
||||
// CertGenerator is an interface to provision the serving certificate.
|
||||
type CertGenerator interface {
|
||||
// Generate returns a Artifacts struct.
|
||||
Generate(CommonName string) (*Artifacts, error)
|
||||
// SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert.
|
||||
SetCA(caKey, caCert []byte)
|
||||
}
|
||||
|
||||
// ValidCACert think cert and key are valid if they meet the following requirements:
|
||||
// - key and cert are valid pair
|
||||
// - caCert is the root ca of cert
|
||||
// - cert is for dnsName
|
||||
// - cert won't expire before time
|
||||
func ValidCACert(key, cert, caCert []byte, dnsName string, time time.Time) bool {
|
||||
if len(key) == 0 || len(cert) == 0 || len(caCert) == 0 {
|
||||
return false
|
||||
}
|
||||
// Verify key and cert are valid pair
|
||||
_, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Verify cert is valid for at least 1 year.
|
||||
pool := x509.NewCertPool()
|
||||
if !pool.AppendCertsFromPEM(caCert) {
|
||||
return false
|
||||
}
|
||||
block, _ := pem.Decode(cert)
|
||||
if block == nil {
|
||||
return false
|
||||
}
|
||||
c, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
ops := x509.VerifyOptions{
|
||||
DNSName: dnsName,
|
||||
Roots: pool,
|
||||
CurrentTime: time,
|
||||
}
|
||||
_, err = c.Verify(ops)
|
||||
return err == nil
|
||||
}
|
169
test/e2e/pkg/cert/selfsigned.go
Normal file
169
test/e2e/pkg/cert/selfsigned.go
Normal file
@ -0,0 +1,169 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
)
|
||||
|
||||
type SelfSignedCertGenerator struct {
|
||||
caKey []byte
|
||||
caCert []byte
|
||||
}
|
||||
|
||||
var _ CertGenerator = &SelfSignedCertGenerator{}
|
||||
|
||||
// SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert.
|
||||
func (cp *SelfSignedCertGenerator) SetCA(caKey, caCert []byte) {
|
||||
cp.caKey = caKey
|
||||
cp.caCert = caCert
|
||||
}
|
||||
|
||||
// Generate creates and returns a CA certificate, certificate and
|
||||
// key for the server or client. Key and Cert are used by the server or client
|
||||
// to establish trust for others, CA certificate is used by the
|
||||
// client or server to verify the other's authentication chain.
|
||||
// The cert will be valid for 365 days.
|
||||
func (cp *SelfSignedCertGenerator) Generate(commonName string) (*Artifacts, error) {
|
||||
var signingKey *rsa.PrivateKey
|
||||
var signingCert *x509.Certificate
|
||||
var valid bool
|
||||
var err error
|
||||
|
||||
valid, signingKey, signingCert = cp.validCACert()
|
||||
if !valid {
|
||||
signingKey, err = NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the CA private key: %v", err)
|
||||
}
|
||||
signingCert, err = cert.NewSelfSignedCACert(cert.Config{CommonName: commonName}, signingKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the CA cert: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
hostIP := net.ParseIP(commonName)
|
||||
var altIPs []net.IP
|
||||
DNSNames := []string{"localhost"}
|
||||
if hostIP.To4() != nil {
|
||||
altIPs = append(altIPs, hostIP.To4())
|
||||
} else {
|
||||
DNSNames = append(DNSNames, commonName)
|
||||
}
|
||||
|
||||
key, err := NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the private key: %v", err)
|
||||
}
|
||||
signedCert, err := NewSignedCert(
|
||||
cert.Config{
|
||||
CommonName: commonName,
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||
AltNames: cert.AltNames{IPs: altIPs, DNSNames: DNSNames},
|
||||
},
|
||||
key, signingCert, signingKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the cert: %v", err)
|
||||
}
|
||||
return &Artifacts{
|
||||
Key: EncodePrivateKeyPEM(key),
|
||||
Cert: EncodeCertPEM(signedCert),
|
||||
CAKey: EncodePrivateKeyPEM(signingKey),
|
||||
CACert: EncodeCertPEM(signingCert),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cp *SelfSignedCertGenerator) validCACert() (bool, *rsa.PrivateKey, *x509.Certificate) {
|
||||
if !ValidCACert(cp.caKey, cp.caCert, cp.caCert, "",
|
||||
time.Now().AddDate(1, 0, 0)) {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
var ok bool
|
||||
key, err := keyutil.ParsePrivateKeyPEM(cp.caKey)
|
||||
if err != nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
privateKey, ok := key.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
certs, err := cert.ParseCertsPEM(cp.caCert)
|
||||
if err != nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
if len(certs) != 1 {
|
||||
return false, nil, nil
|
||||
}
|
||||
return true, privateKey, certs[0]
|
||||
}
|
||||
|
||||
// NewPrivateKey creates an RSA private key
|
||||
func NewPrivateKey() (*rsa.PrivateKey, error) {
|
||||
return rsa.GenerateKey(rand.Reader, 2048)
|
||||
}
|
||||
|
||||
// NewSignedCert creates a signed certificate using the given CA certificate and key
|
||||
func NewSignedCert(cfg cert.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
|
||||
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cfg.CommonName) == 0 {
|
||||
return nil, errors.New("must specify a CommonName")
|
||||
}
|
||||
if len(cfg.Usages) == 0 {
|
||||
return nil, errors.New("must specify at least one ExtKeyUsage")
|
||||
}
|
||||
|
||||
certTmpl := x509.Certificate{
|
||||
Subject: pkix.Name{
|
||||
CommonName: cfg.CommonName,
|
||||
Organization: cfg.Organization,
|
||||
},
|
||||
DNSNames: cfg.AltNames.DNSNames,
|
||||
IPAddresses: cfg.AltNames.IPs,
|
||||
SerialNumber: serial,
|
||||
NotBefore: caCert.NotBefore,
|
||||
NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10).UTC(),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: cfg.Usages,
|
||||
}
|
||||
certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x509.ParseCertificate(certDERBytes)
|
||||
}
|
||||
|
||||
// EncodePrivateKeyPEM returns PEM-encoded private key data
|
||||
func EncodePrivateKeyPEM(key *rsa.PrivateKey) []byte {
|
||||
block := pem.Block{
|
||||
Type: keyutil.RSAPrivateKeyBlockType,
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}
|
||||
return pem.EncodeToMemory(&block)
|
||||
}
|
||||
|
||||
// EncodeCertPEM returns PEM-encoded certificate data
|
||||
func EncodeCertPEM(ct *x509.Certificate) []byte {
|
||||
block := pem.Block{
|
||||
Type: cert.CertificateBlockType,
|
||||
Bytes: ct.Raw,
|
||||
}
|
||||
return pem.EncodeToMemory(&block)
|
||||
}
|
@ -22,6 +22,7 @@ func NewAllocator(from int, to int, mod int, index int) *Allocator {
|
||||
reserved: sets.NewInt(),
|
||||
used: sets.NewInt(),
|
||||
}
|
||||
|
||||
for i := from; i <= to; i++ {
|
||||
if i%mod == index {
|
||||
pa.reserved.Insert(i)
|
||||
@ -50,7 +51,7 @@ func (pa *Allocator) GetByName(portName string) int {
|
||||
pa.mu.Lock()
|
||||
defer pa.mu.Unlock()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < 20; i++ {
|
||||
port := pa.getByRange(builder.rangePortFrom, builder.rangePortTo)
|
||||
if port == 0 {
|
||||
return 0
|
||||
|
@ -13,11 +13,17 @@ type Process struct {
|
||||
stdOutput *bytes.Buffer
|
||||
|
||||
beforeStopHandler func()
|
||||
stopped bool
|
||||
}
|
||||
|
||||
func New(path string, params []string) *Process {
|
||||
return NewWithEnvs(path, params, nil)
|
||||
}
|
||||
|
||||
func NewWithEnvs(path string, params []string, envs []string) *Process {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cmd := exec.CommandContext(ctx, path, params...)
|
||||
cmd.Env = envs
|
||||
p := &Process{
|
||||
cmd: cmd,
|
||||
cancel: cancel,
|
||||
@ -34,6 +40,12 @@ func (p *Process) Start() error {
|
||||
}
|
||||
|
||||
func (p *Process) Stop() error {
|
||||
if p.stopped {
|
||||
return nil
|
||||
}
|
||||
defer func() {
|
||||
p.stopped = true
|
||||
}()
|
||||
if p.beforeStopHandler != nil {
|
||||
p.beforeStopHandler()
|
||||
}
|
||||
|
@ -1,26 +1,48 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/pkg/rpc"
|
||||
libnet "github.com/fatedier/golib/net"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
protocol string
|
||||
addr string
|
||||
port int
|
||||
body []byte
|
||||
timeout time.Duration
|
||||
proxyURL string
|
||||
proxyHost string
|
||||
protocol string
|
||||
|
||||
// for all protocol
|
||||
addr string
|
||||
port int
|
||||
body []byte
|
||||
timeout time.Duration
|
||||
|
||||
// for http or https
|
||||
method string
|
||||
host string
|
||||
path string
|
||||
headers map[string]string
|
||||
tlsConfig *tls.Config
|
||||
|
||||
proxyURL string
|
||||
}
|
||||
|
||||
func New() *Request {
|
||||
return &Request{
|
||||
protocol: "tcp",
|
||||
addr: "127.0.0.1",
|
||||
|
||||
method: "GET",
|
||||
path: "/",
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,9 +61,18 @@ func (r *Request) UDP() *Request {
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) Proxy(url, host string) *Request {
|
||||
func (r *Request) HTTP() *Request {
|
||||
r.protocol = "http"
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) HTTPS() *Request {
|
||||
r.protocol = "https"
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) Proxy(url string) *Request {
|
||||
r.proxyURL = url
|
||||
r.proxyHost = host
|
||||
return r
|
||||
}
|
||||
|
||||
@ -55,6 +86,34 @@ func (r *Request) Port(port int) *Request {
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) HTTPParams(method, host, path string, headers map[string]string) *Request {
|
||||
r.method = method
|
||||
r.host = host
|
||||
r.path = path
|
||||
r.headers = headers
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) HTTPHost(host string) *Request {
|
||||
r.host = host
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) HTTPPath(path string) *Request {
|
||||
r.path = path
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) HTTPHeaders(headers map[string]string) *Request {
|
||||
r.headers = headers
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) TLSConfig(tlsConfig *tls.Config) *Request {
|
||||
r.tlsConfig = tlsConfig
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) Timeout(timeout time.Duration) *Request {
|
||||
r.timeout = timeout
|
||||
return r
|
||||
@ -65,28 +124,34 @@ func (r *Request) Body(content []byte) *Request {
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) Do() ([]byte, error) {
|
||||
func (r *Request) Do() (*Response, error) {
|
||||
var (
|
||||
conn net.Conn
|
||||
err error
|
||||
)
|
||||
|
||||
addr := net.JoinHostPort(r.addr, strconv.Itoa(r.port))
|
||||
// for protocol http and https
|
||||
if r.protocol == "http" || r.protocol == "https" {
|
||||
return r.sendHTTPRequest(r.method, fmt.Sprintf("%s://%s%s", r.protocol, addr, r.path),
|
||||
r.host, r.headers, r.proxyURL, r.body, r.tlsConfig)
|
||||
}
|
||||
|
||||
// for protocol tcp and udp
|
||||
if len(r.proxyURL) > 0 {
|
||||
if r.protocol != "tcp" {
|
||||
return nil, fmt.Errorf("only tcp protocol is allowed for proxy")
|
||||
}
|
||||
conn, err = libnet.DialTcpByProxy(r.proxyURL, r.proxyHost)
|
||||
conn, err = libnet.DialTcpByProxy(r.proxyURL, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if r.addr == "" {
|
||||
r.addr = fmt.Sprintf("127.0.0.1:%d", r.port)
|
||||
}
|
||||
switch r.protocol {
|
||||
case "tcp":
|
||||
conn, err = net.Dial("tcp", r.addr)
|
||||
conn, err = net.Dial("tcp", addr)
|
||||
case "udp":
|
||||
conn, err = net.Dial("udp", r.addr)
|
||||
conn, err = net.Dial("udp", addr)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid protocol")
|
||||
}
|
||||
@ -99,41 +164,83 @@ func (r *Request) Do() ([]byte, error) {
|
||||
if r.timeout > 0 {
|
||||
conn.SetDeadline(time.Now().Add(r.timeout))
|
||||
}
|
||||
return sendRequestByConn(conn, r.body)
|
||||
}
|
||||
|
||||
func SendTCPRequest(port int, content []byte, timeout time.Duration) ([]byte, error) {
|
||||
c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
||||
buf, err := r.sendRequestByConn(conn, r.body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to tcp server error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.SetDeadline(time.Now().Add(timeout))
|
||||
return sendRequestByConn(c, content)
|
||||
return &Response{Content: buf}, nil
|
||||
}
|
||||
|
||||
func SendUDPRequest(port int, content []byte, timeout time.Duration) ([]byte, error) {
|
||||
c, err := net.Dial("udp", fmt.Sprintf("127.0.0.1:%d", port))
|
||||
type Response struct {
|
||||
Code int
|
||||
Header http.Header
|
||||
Content []byte
|
||||
}
|
||||
|
||||
func (r *Request) sendHTTPRequest(method, urlstr string, host string, headers map[string]string,
|
||||
proxy string, body []byte, tlsConfig *tls.Config,
|
||||
) (*Response, error) {
|
||||
|
||||
var inBody io.Reader
|
||||
if len(body) != 0 {
|
||||
inBody = bytes.NewReader(body)
|
||||
}
|
||||
req, err := http.NewRequest(method, urlstr, inBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to udp server error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
tr := &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
if len(proxy) != 0 {
|
||||
tr.Proxy = func(req *http.Request) (*url.URL, error) {
|
||||
return url.Parse(proxy)
|
||||
}
|
||||
}
|
||||
client := http.Client{Transport: tr}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.SetDeadline(time.Now().Add(timeout))
|
||||
return sendRequestByConn(c, content)
|
||||
ret := &Response{Code: resp.StatusCode, Header: resp.Header}
|
||||
buf, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Content = buf
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func sendRequestByConn(c net.Conn, content []byte) ([]byte, error) {
|
||||
_, err := c.Write(content)
|
||||
func (r *Request) sendRequestByConn(c net.Conn, content []byte) ([]byte, error) {
|
||||
_, err := rpc.WriteBytes(c, content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("write error: %v", err)
|
||||
}
|
||||
|
||||
buf := make([]byte, 2048)
|
||||
n, err := c.Read(buf)
|
||||
var reader io.Reader = c
|
||||
if r.protocol == "udp" {
|
||||
reader = bufio.NewReader(c)
|
||||
}
|
||||
|
||||
buf, err := rpc.ReadBytes(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read error: %v", err)
|
||||
}
|
||||
return buf[:n], nil
|
||||
return buf, nil
|
||||
}
|
||||
|
31
test/e2e/pkg/rpc/rpc.go
Normal file
31
test/e2e/pkg/rpc/rpc.go
Normal file
@ -0,0 +1,31 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
func WriteBytes(w io.Writer, buf []byte) (int, error) {
|
||||
out := bytes.NewBuffer(nil)
|
||||
binary.Write(out, binary.BigEndian, int64(len(buf)))
|
||||
out.Write(buf)
|
||||
return w.Write(out.Bytes())
|
||||
}
|
||||
|
||||
func ReadBytes(r io.Reader) ([]byte, error) {
|
||||
var length int64
|
||||
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffer := make([]byte, length)
|
||||
n, err := io.ReadFull(r, buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int64(n) != length {
|
||||
return nil, errors.New("invalid length")
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
133
test/e2e/pkg/sdk/client/client.go
Normal file
133
test/e2e/pkg/sdk/client/client.go
Normal file
@ -0,0 +1,133 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/utils"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
address string
|
||||
authUser string
|
||||
authPwd string
|
||||
}
|
||||
|
||||
func New(host string, port int) *Client {
|
||||
return &Client{
|
||||
address: net.JoinHostPort(host, strconv.Itoa(port)),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) SetAuth(user, pwd string) {
|
||||
c.authUser = user
|
||||
c.authPwd = pwd
|
||||
}
|
||||
|
||||
func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) {
|
||||
req, err := http.NewRequest("GET", "http://"+c.address+"/api/status", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content, err := c.do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allStatus := &client.StatusResp{}
|
||||
if err = json.Unmarshal([]byte(content), &allStatus); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(content))
|
||||
}
|
||||
for _, s := range allStatus.TCP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.UDP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.HTTP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.HTTPS {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.STCP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.XTCP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.SUDP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no proxy status found")
|
||||
}
|
||||
|
||||
func (c *Client) Reload() error {
|
||||
req, err := http.NewRequest("GET", "http://"+c.address+"/api/reload", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.do(req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) GetConfig() (string, error) {
|
||||
req, err := http.NewRequest("GET", "http://"+c.address+"/api/config", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return c.do(req)
|
||||
}
|
||||
|
||||
func (c *Client) UpdateConfig(content string) error {
|
||||
req, err := http.NewRequest("PUT", "http://"+c.address+"/api/config", strings.NewReader(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.do(req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) setAuthHeader(req *http.Request) {
|
||||
if c.authUser != "" || c.authPwd != "" {
|
||||
req.Header.Set("Authorization", utils.BasicAuth(c.authUser, c.authPwd))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) do(req *http.Request) (string, error) {
|
||||
c.setAuthHeader(req)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
buf, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
10
test/e2e/pkg/utils/utils.go
Normal file
10
test/e2e/pkg/utils/utils.go
Normal file
@ -0,0 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func BasicAuth(username, passwd string) string {
|
||||
auth := username + ":" + passwd
|
||||
return "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
|
||||
}
|
316
test/e2e/plugin/client.go
Normal file
316
test/e2e/plugin/client.go
Normal file
@ -0,0 +1,316 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/fatedier/frp/pkg/transport"
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/cert"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/utils"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Client-Plugins]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
Describe("UnixDomainSocket", func() {
|
||||
It("Expose a unix domain socket echo server", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
getProxyConf := func(proxyName string, portName string, extra string) string {
|
||||
return fmt.Sprintf(`
|
||||
[%s]
|
||||
type = tcp
|
||||
remote_port = {{ .%s }}
|
||||
plugin = unix_domain_socket
|
||||
plugin_unix_path = {{ .%s }}
|
||||
`+extra, proxyName, portName, framework.UDSEchoServerAddr)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
proxyName string
|
||||
portName string
|
||||
extraConfig string
|
||||
}{
|
||||
{
|
||||
proxyName: "normal",
|
||||
portName: port.GenName("Normal"),
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption",
|
||||
portName: port.GenName("WithEncryption"),
|
||||
extraConfig: "use_encryption = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-compression",
|
||||
portName: port.GenName("WithCompression"),
|
||||
extraConfig: "use_compression = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption-and-compression",
|
||||
portName: port.GenName("WithEncryptionAndCompression"),
|
||||
extraConfig: `
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
// build all client config
|
||||
for _, test := range tests {
|
||||
clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
|
||||
}
|
||||
// run frps and frpc
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
for _, test := range tests {
|
||||
framework.NewRequestExpect(f).Port(f.PortByName(test.portName)).Ensure()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
It("http_proxy", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
remote_port = %d
|
||||
plugin = http_proxy
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = 123
|
||||
`, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// http proxy, no auth info
|
||||
framework.NewRequestExpect(f).PortName(framework.HTTPSimpleServerPort).RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Proxy("http://127.0.0.1:" + strconv.Itoa(remotePort))
|
||||
}).Ensure(framework.ExpectResponseCode(407))
|
||||
|
||||
// http proxy, correct auth
|
||||
framework.NewRequestExpect(f).PortName(framework.HTTPSimpleServerPort).RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Proxy("http://abc:123@127.0.0.1:" + strconv.Itoa(remotePort))
|
||||
}).Ensure()
|
||||
|
||||
// connect TCP server by CONNECT method
|
||||
framework.NewRequestExpect(f).PortName(framework.TCPEchoServerPort).RequestModify(func(r *request.Request) {
|
||||
r.TCP().Proxy("http://abc:123@127.0.0.1:" + strconv.Itoa(remotePort))
|
||||
})
|
||||
})
|
||||
|
||||
It("socks5 proxy", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
remote_port = %d
|
||||
plugin = socks5
|
||||
plugin_user = abc
|
||||
plugin_passwd = 123
|
||||
`, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// http proxy, no auth info
|
||||
framework.NewRequestExpect(f).PortName(framework.TCPEchoServerPort).RequestModify(func(r *request.Request) {
|
||||
r.TCP().Proxy("socks5://127.0.0.1:" + strconv.Itoa(remotePort))
|
||||
}).ExpectError(true).Ensure()
|
||||
|
||||
// http proxy, correct auth
|
||||
framework.NewRequestExpect(f).PortName(framework.TCPEchoServerPort).RequestModify(func(r *request.Request) {
|
||||
r.TCP().Proxy("socks5://abc:123@127.0.0.1:" + strconv.Itoa(remotePort))
|
||||
}).Ensure()
|
||||
})
|
||||
|
||||
It("static_file", func() {
|
||||
vhostPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
vhost_http_port = %d
|
||||
`, vhostPort)
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
f.WriteTempFile("test_static_file", "foo")
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
remote_port = %d
|
||||
plugin = static_file
|
||||
plugin_local_path = %s
|
||||
|
||||
[http]
|
||||
type = http
|
||||
custom_domains = example.com
|
||||
plugin = static_file
|
||||
plugin_local_path = %s
|
||||
|
||||
[http-with-auth]
|
||||
type = http
|
||||
custom_domains = other.example.com
|
||||
plugin = static_file
|
||||
plugin_local_path = %s
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = 123
|
||||
`, remotePort, f.TempDirectory, f.TempDirectory, f.TempDirectory)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
// from tcp proxy
|
||||
framework.NewRequestExpect(f).Request(
|
||||
framework.NewHTTPRequest().HTTPPath("/test_static_file").Port(remotePort),
|
||||
).ExpectResp([]byte("foo")).Ensure()
|
||||
|
||||
// from http proxy without auth
|
||||
framework.NewRequestExpect(f).Request(
|
||||
framework.NewHTTPRequest().HTTPHost("example.com").HTTPPath("/test_static_file").Port(vhostPort),
|
||||
).ExpectResp([]byte("foo")).Ensure()
|
||||
|
||||
// from http proxy with auth
|
||||
framework.NewRequestExpect(f).Request(
|
||||
framework.NewHTTPRequest().HTTPHost("other.example.com").HTTPPath("/test_static_file").Port(vhostPort).HTTPHeaders(map[string]string{
|
||||
"Authorization": utils.BasicAuth("abc", "123"),
|
||||
}),
|
||||
).ExpectResp([]byte("foo")).Ensure()
|
||||
})
|
||||
|
||||
It("http2https", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf += fmt.Sprintf(`
|
||||
vhost_http_port = %d
|
||||
`, vhostHTTPPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
[http2https]
|
||||
type = http
|
||||
custom_domains = example.com
|
||||
plugin = http2https
|
||||
plugin_local_addr = 127.0.0.1:%d
|
||||
`, localPort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
|
||||
framework.ExpectNoError(err)
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithTlsConfig(tlsConfig),
|
||||
httpserver.WithResponse([]byte("test")),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
framework.NewRequestExpect(f).
|
||||
Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("example.com")
|
||||
}).
|
||||
ExpectResp([]byte("test")).
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("https2http", func() {
|
||||
generator := &cert.SelfSignedCertGenerator{}
|
||||
artifacts, err := generator.Generate("example.com")
|
||||
framework.ExpectNoError(err)
|
||||
crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert))
|
||||
keyPath := f.WriteTempFile("server.key", string(artifacts.Key))
|
||||
|
||||
serverConf := consts.DefaultServerConfig
|
||||
vhostHTTPSPort := f.AllocPort()
|
||||
serverConf += fmt.Sprintf(`
|
||||
vhost_https_port = %d
|
||||
`, vhostHTTPSPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
[https2http]
|
||||
type = https
|
||||
custom_domains = example.com
|
||||
plugin = https2http
|
||||
plugin_local_addr = 127.0.0.1:%d
|
||||
plugin_crt_path = %s
|
||||
plugin_key_path = %s
|
||||
`, localPort, crtPath, keyPath)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithResponse([]byte("test")),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
framework.NewRequestExpect(f).
|
||||
Port(vhostHTTPSPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTPS().HTTPHost("example.com").TLSConfig(&tls.Config{
|
||||
ServerName: "example.com",
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
}).
|
||||
ExpectResp([]byte("test")).
|
||||
Ensure()
|
||||
})
|
||||
|
||||
It("https2https", func() {
|
||||
generator := &cert.SelfSignedCertGenerator{}
|
||||
artifacts, err := generator.Generate("example.com")
|
||||
framework.ExpectNoError(err)
|
||||
crtPath := f.WriteTempFile("server.crt", string(artifacts.Cert))
|
||||
keyPath := f.WriteTempFile("server.key", string(artifacts.Key))
|
||||
|
||||
serverConf := consts.DefaultServerConfig
|
||||
vhostHTTPSPort := f.AllocPort()
|
||||
serverConf += fmt.Sprintf(`
|
||||
vhost_https_port = %d
|
||||
`, vhostHTTPSPort)
|
||||
|
||||
localPort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
[https2https]
|
||||
type = https
|
||||
custom_domains = example.com
|
||||
plugin = https2https
|
||||
plugin_local_addr = 127.0.0.1:%d
|
||||
plugin_crt_path = %s
|
||||
plugin_key_path = %s
|
||||
`, localPort, crtPath, keyPath)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
|
||||
framework.ExpectNoError(err)
|
||||
localServer := httpserver.New(
|
||||
httpserver.WithBindPort(localPort),
|
||||
httpserver.WithResponse([]byte("test")),
|
||||
httpserver.WithTlsConfig(tlsConfig),
|
||||
)
|
||||
f.RunServer("", localServer)
|
||||
|
||||
framework.NewRequestExpect(f).
|
||||
Port(vhostHTTPSPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTPS().HTTPHost("example.com").TLSConfig(&tls.Config{
|
||||
ServerName: "example.com",
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
}).
|
||||
ExpectResp([]byte("test")).
|
||||
Ensure()
|
||||
})
|
||||
})
|
@ -1,76 +0,0 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Client-Plugins]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
Describe("UnixDomainSocket", func() {
|
||||
It("Expose a unix domain socket echo server", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
getProxyConf := func(proxyName string, portName string, extra string) string {
|
||||
return fmt.Sprintf(`
|
||||
[%s]
|
||||
type = tcp
|
||||
remote_port = {{ .%s }}
|
||||
plugin = unix_domain_socket
|
||||
plugin_unix_path = {{ .%s }}
|
||||
`+extra, proxyName, portName, framework.UDSEchoServerAddr)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
proxyName string
|
||||
portName string
|
||||
extraConfig string
|
||||
}{
|
||||
{
|
||||
proxyName: "normal",
|
||||
portName: port.GenName("Normal"),
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption",
|
||||
portName: port.GenName("WithEncryption"),
|
||||
extraConfig: "use_encryption = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-compression",
|
||||
portName: port.GenName("WithCompression"),
|
||||
extraConfig: "use_compression = true",
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption-and-compression",
|
||||
portName: port.GenName("WithEncryptionAndCompression"),
|
||||
extraConfig: `
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
// build all client config
|
||||
for _, test := range tests {
|
||||
clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
|
||||
}
|
||||
// run frps and frpc
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
for _, test := range tests {
|
||||
framework.ExpectResponse(
|
||||
framework.NewRequest().Port(f.PortByName(test.portName)),
|
||||
[]byte(consts.TestString),
|
||||
test.proxyName,
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
341
test/e2e/plugin/server.go
Normal file
341
test/e2e/plugin/server.go
Normal file
@ -0,0 +1,341 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||
"github.com/fatedier/frp/pkg/transport"
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("[Feature: Server-Plugins]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
Describe("Login", func() {
|
||||
newFunc := func() *plugin.Request {
|
||||
var r plugin.Request
|
||||
r.Content = &plugin.LoginContent{}
|
||||
return &r
|
||||
}
|
||||
|
||||
It("Auth for custom meta token", func() {
|
||||
localPort := f.AllocPort()
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.LoginContent)
|
||||
if content.Metas["token"] == "123" {
|
||||
ret.Unchange = true
|
||||
} else {
|
||||
ret.Reject = true
|
||||
ret.RejectReason = "invalid token"
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.user-manager]
|
||||
addr = 127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = Login
|
||||
`, localPort)
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
meta_token = 123
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
remotePort2 := f.AllocPort()
|
||||
invalidTokenClientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
[tcp2]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort2)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf, invalidTokenClientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
framework.NewRequestExpect(f).Port(remotePort2).ExpectError(true).Ensure()
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NewProxy", func() {
|
||||
newFunc := func() *plugin.Request {
|
||||
var r plugin.Request
|
||||
r.Content = &plugin.NewProxyContent{}
|
||||
return &r
|
||||
}
|
||||
|
||||
It("Validate Info", func() {
|
||||
localPort := f.AllocPort()
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.NewProxyContent)
|
||||
if content.ProxyName == "tcp" {
|
||||
ret.Unchange = true
|
||||
} else {
|
||||
ret.Reject = true
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.test]
|
||||
addr = 127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = NewProxy
|
||||
`, localPort)
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
})
|
||||
|
||||
It("Mofify RemotePort", func() {
|
||||
localPort := f.AllocPort()
|
||||
remotePort := f.AllocPort()
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.NewProxyContent)
|
||||
content.RemotePort = remotePort
|
||||
ret.Content = content
|
||||
return &ret
|
||||
}
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.test]
|
||||
addr = 127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = NewProxy
|
||||
`, localPort)
|
||||
clientConf := consts.DefaultClientConfig
|
||||
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = 0
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Ping", func() {
|
||||
newFunc := func() *plugin.Request {
|
||||
var r plugin.Request
|
||||
r.Content = &plugin.PingContent{}
|
||||
return &r
|
||||
}
|
||||
|
||||
It("Validate Info", func() {
|
||||
localPort := f.AllocPort()
|
||||
|
||||
var record string
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.PingContent)
|
||||
record = content.Ping.PrivilegeKey
|
||||
ret.Unchange = true
|
||||
return &ret
|
||||
}
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.test]
|
||||
addr = 127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = Ping
|
||||
`, localPort)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
heartbeat_interval = 1
|
||||
authenticate_heartbeats = true
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
framework.ExpectNotEqual("", record)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NewWorkConn", func() {
|
||||
newFunc := func() *plugin.Request {
|
||||
var r plugin.Request
|
||||
r.Content = &plugin.NewWorkConnContent{}
|
||||
return &r
|
||||
}
|
||||
|
||||
It("Validate Info", func() {
|
||||
localPort := f.AllocPort()
|
||||
|
||||
var record string
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.NewWorkConnContent)
|
||||
record = content.NewWorkConn.RunID
|
||||
ret.Unchange = true
|
||||
return &ret
|
||||
}
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.test]
|
||||
addr = 127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = NewWorkConn
|
||||
`, localPort)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
framework.ExpectNotEqual("", record)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NewUserConn", func() {
|
||||
newFunc := func() *plugin.Request {
|
||||
var r plugin.Request
|
||||
r.Content = &plugin.NewUserConnContent{}
|
||||
return &r
|
||||
}
|
||||
It("Validate Info", func() {
|
||||
localPort := f.AllocPort()
|
||||
|
||||
var record string
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.NewUserConnContent)
|
||||
record = content.RemoteAddr
|
||||
ret.Unchange = true
|
||||
return &ret
|
||||
}
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, nil)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.test]
|
||||
addr = 127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = NewUserConn
|
||||
`, localPort)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
framework.ExpectNotEqual("", record)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("HTTPS Protocol", func() {
|
||||
newFunc := func() *plugin.Request {
|
||||
var r plugin.Request
|
||||
r.Content = &plugin.NewUserConnContent{}
|
||||
return &r
|
||||
}
|
||||
It("Validate Login Info, disable tls verify", func() {
|
||||
localPort := f.AllocPort()
|
||||
|
||||
var record string
|
||||
handler := func(req *plugin.Request) *plugin.Response {
|
||||
var ret plugin.Response
|
||||
content := req.Content.(*plugin.NewUserConnContent)
|
||||
record = content.RemoteAddr
|
||||
ret.Unchange = true
|
||||
return &ret
|
||||
}
|
||||
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
|
||||
framework.ExpectNoError(err)
|
||||
pluginServer := NewHTTPPluginServer(localPort, newFunc, handler, tlsConfig)
|
||||
|
||||
f.RunServer("", pluginServer)
|
||||
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
[plugin.test]
|
||||
addr = https://127.0.0.1:%d
|
||||
path = /handler
|
||||
ops = NewUserConn
|
||||
`, localPort)
|
||||
|
||||
remotePort := f.AllocPort()
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = {{ .%s }}
|
||||
remote_port = %d
|
||||
`, framework.TCPEchoServerPort, remotePort)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||
|
||||
framework.ExpectNotEqual("", record)
|
||||
})
|
||||
})
|
||||
})
|
41
test/e2e/plugin/utils.go
Normal file
41
test/e2e/plugin/utils.go
Normal file
@ -0,0 +1,41 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
"github.com/fatedier/frp/test/e2e/mock/server/httpserver"
|
||||
)
|
||||
|
||||
type PluginHandler func(req *plugin.Request) *plugin.Response
|
||||
|
||||
type NewPluginRequest func() *plugin.Request
|
||||
|
||||
func NewHTTPPluginServer(port int, newFunc NewPluginRequest, handler PluginHandler, tlsConfig *tls.Config) *httpserver.Server {
|
||||
return httpserver.New(
|
||||
httpserver.WithBindPort(port),
|
||||
httpserver.WithTlsConfig(tlsConfig),
|
||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
r := newFunc()
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
log.Trace("plugin request: %s", string(buf))
|
||||
err = json.Unmarshal(buf, &r)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
resp := handler(r)
|
||||
buf, _ = json.Marshal(resp)
|
||||
log.Trace("plugin response: %s", string(buf))
|
||||
w.Write(buf)
|
||||
})),
|
||||
)
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = 10700
|
||||
log_file = console
|
||||
log_level = trace
|
||||
token = 123456
|
||||
admin_port = 10600
|
||||
admin_user = abc
|
||||
admin_pwd = abc
|
||||
|
||||
[tcp_normal]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 10801
|
||||
|
||||
[tcp_ec]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 10901
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
|
||||
[tcp_group1]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 10802
|
||||
group = test1
|
||||
group_key = 123
|
||||
|
||||
[tcp_group2]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
remote_port = 10802
|
||||
group = test1
|
||||
group_key = 123
|
||||
|
||||
[udp_normal]
|
||||
type = udp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
remote_port = 10802
|
||||
|
||||
[udp_ec]
|
||||
type = udp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
remote_port = 10902
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
|
||||
[unix_domain]
|
||||
type = tcp
|
||||
remote_port = 10803
|
||||
plugin = unix_domain_socket
|
||||
plugin_unix_path = /tmp/frp_echo_server.sock
|
||||
|
||||
[stcp]
|
||||
type = stcp
|
||||
sk = abcdefg
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
|
||||
[stcp_ec]
|
||||
type = stcp
|
||||
sk = abc
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
|
||||
[sudp]
|
||||
type = sudp
|
||||
sk = abcdefg
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
|
||||
[web01]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = 127.0.0.1
|
||||
|
||||
[web02]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = test2.frp.com
|
||||
host_header_rewrite = test2.frp.com
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
|
||||
[web03]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = test3.frp.com
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
host_header_rewrite = test3.frp.com
|
||||
locations = /,/foo
|
||||
|
||||
[web04]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = test3.frp.com
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
host_header_rewrite = test3.frp.com
|
||||
locations = /bar
|
||||
|
||||
[web05]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = test5.frp.com
|
||||
host_header_rewrite = test5.frp.com
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
http_user = test
|
||||
http_user = test
|
||||
|
||||
[web06]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = test6.frp.com
|
||||
host_header_rewrite = test6.frp.com
|
||||
header_X-From-Where = frp
|
||||
|
||||
[wildcard_http]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
custom_domains = *.frp1.com
|
||||
|
||||
[subhost01]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
subdomain = test01
|
||||
|
||||
[subhost02]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10704
|
||||
subdomain = test02
|
||||
|
||||
[tcp_port_not_allowed]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 20001
|
||||
|
||||
[tcp_port_unavailable]
|
||||
type =tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 10700
|
||||
|
||||
[tcp_port_normal]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 20002
|
||||
|
||||
[tcp_random_port]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
remote_port = 0
|
||||
|
||||
[udp_port_not_allowed]
|
||||
type = udp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
remote_port = 20001
|
||||
|
||||
[udp_port_normal]
|
||||
type = udp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
remote_port = 20002
|
||||
|
||||
[udp_random_port]
|
||||
type = udp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10702
|
||||
remote_port = 0
|
||||
|
||||
[http_proxy]
|
||||
type = tcp
|
||||
plugin = http_proxy
|
||||
remote_port = 0
|
||||
|
||||
[range:range_tcp]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 30000-30001,30003
|
||||
remote_port = 30000-30001,30003
|
@ -1,34 +0,0 @@
|
||||
[common]
|
||||
server_addr = 0.0.0.0
|
||||
server_port = 10700
|
||||
log_file = console
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
token = 123456
|
||||
|
||||
[stcp_visitor]
|
||||
type = stcp
|
||||
role = visitor
|
||||
server_name = stcp
|
||||
sk = abcdefg
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 10805
|
||||
|
||||
[stcp_ec_visitor]
|
||||
type = stcp
|
||||
role = visitor
|
||||
server_name = stcp_ec
|
||||
sk = abc
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 10905
|
||||
use_encryption = true
|
||||
use_compression = true
|
||||
|
||||
|
||||
[sudp_visitor]
|
||||
type = sudp
|
||||
role = visitor
|
||||
server_name = sudp
|
||||
sk = abcdefg
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 10816
|
@ -1,9 +0,0 @@
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 10700
|
||||
vhost_http_port = 10804
|
||||
tcpmux_httpconnect_port = 10806
|
||||
log_level = trace
|
||||
token = 123456
|
||||
allow_ports = 10000-20000,20002,30000-50000
|
||||
subdomain_host = sub.com
|
@ -1,85 +0,0 @@
|
||||
package ci
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
"github.com/fatedier/frp/tests/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdTCP(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var err error
|
||||
s := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-t", "123", "-p", "20000"})
|
||||
err = s.Start()
|
||||
if assert.NoError(err) {
|
||||
defer s.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"tcp", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test",
|
||||
"-l", "10701", "-r", "20801", "-n", "tcp_test"})
|
||||
err = c.Start()
|
||||
if assert.NoError(err) {
|
||||
defer c.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
res, err := util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
}
|
||||
|
||||
func TestCmdUDP(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var err error
|
||||
s := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-t", "123", "-p", "20000"})
|
||||
err = s.Start()
|
||||
if assert.NoError(err) {
|
||||
defer s.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"udp", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test",
|
||||
"-l", "10702", "-r", "20802", "-n", "udp_test"})
|
||||
err = c.Start()
|
||||
if assert.NoError(err) {
|
||||
defer c.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
res, err := util.SendUDPMsg("127.0.0.1:20802", consts.TEST_UDP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_UDP_ECHO_STR, res)
|
||||
}
|
||||
|
||||
func TestCmdHTTP(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var err error
|
||||
s := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-t", "123", "-p", "20000", "--vhost_http_port", "20001"})
|
||||
err = s.Start()
|
||||
if assert.NoError(err) {
|
||||
defer s.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"http", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test",
|
||||
"-n", "udp_test", "-l", "10704", "--custom_domain", "127.0.0.1"})
|
||||
err = c.Start()
|
||||
if assert.NoError(err) {
|
||||
defer c.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
code, body, _, err := util.SendHTTPMsg("GET", "http://127.0.0.1:20001", "", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
}
|
@ -1,309 +0,0 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/tests/config"
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
"github.com/fatedier/frp/tests/mock"
|
||||
"github.com/fatedier/frp/tests/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const FRPS_CONF = `
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 14000
|
||||
vhost_http_port = 14000
|
||||
log_file = console
|
||||
log_level = debug
|
||||
token = 123456
|
||||
`
|
||||
|
||||
const FRPC_CONF = `
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = 14000
|
||||
log_file = console
|
||||
log_level = debug
|
||||
token = 123456
|
||||
|
||||
[tcp1]
|
||||
type = tcp
|
||||
local_port = 15001
|
||||
remote_port = 15000
|
||||
group = test
|
||||
group_key = 123
|
||||
health_check_type = tcp
|
||||
health_check_interval_s = 1
|
||||
|
||||
[tcp2]
|
||||
type = tcp
|
||||
local_port = 15002
|
||||
remote_port = 15000
|
||||
group = test
|
||||
group_key = 123
|
||||
health_check_type = tcp
|
||||
health_check_interval_s = 1
|
||||
|
||||
[http1]
|
||||
type = http
|
||||
local_port = 15003
|
||||
custom_domains = test1.com
|
||||
health_check_type = http
|
||||
health_check_interval_s = 1
|
||||
health_check_url = /health
|
||||
|
||||
[http2]
|
||||
type = http
|
||||
local_port = 15004
|
||||
custom_domains = test2.com
|
||||
health_check_type = http
|
||||
health_check_interval_s = 1
|
||||
health_check_url = /health
|
||||
|
||||
[http3]
|
||||
type = http
|
||||
local_port = 15005
|
||||
custom_domains = test.balancing.com
|
||||
group = test-balancing
|
||||
group_key = 123
|
||||
|
||||
[http4]
|
||||
type = http
|
||||
local_port = 15006
|
||||
custom_domains = test.balancing.com
|
||||
group = test-balancing
|
||||
group_key = 123
|
||||
`
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// ****** start background services ******
|
||||
echoSvc1 := mock.NewEchoServer(15001, 1, "echo1")
|
||||
err := echoSvc1.Start()
|
||||
if assert.NoError(err) {
|
||||
defer echoSvc1.Stop()
|
||||
}
|
||||
|
||||
echoSvc2 := mock.NewEchoServer(15002, 1, "echo2")
|
||||
err = echoSvc2.Start()
|
||||
if assert.NoError(err) {
|
||||
defer echoSvc2.Stop()
|
||||
}
|
||||
|
||||
var healthMu sync.RWMutex
|
||||
svc1Health := true
|
||||
svc2Health := true
|
||||
httpSvc1 := mock.NewHTTPServer(15003, func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.Contains(r.URL.Path, "health") {
|
||||
healthMu.RLock()
|
||||
defer healthMu.RUnlock()
|
||||
if svc1Health {
|
||||
w.WriteHeader(200)
|
||||
} else {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
} else {
|
||||
w.Write([]byte("http1"))
|
||||
}
|
||||
})
|
||||
err = httpSvc1.Start()
|
||||
if assert.NoError(err) {
|
||||
defer httpSvc1.Stop()
|
||||
}
|
||||
|
||||
httpSvc2 := mock.NewHTTPServer(15004, func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.Contains(r.URL.Path, "health") {
|
||||
healthMu.RLock()
|
||||
defer healthMu.RUnlock()
|
||||
if svc2Health {
|
||||
w.WriteHeader(200)
|
||||
} else {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
} else {
|
||||
w.Write([]byte("http2"))
|
||||
}
|
||||
})
|
||||
err = httpSvc2.Start()
|
||||
if assert.NoError(err) {
|
||||
defer httpSvc2.Stop()
|
||||
}
|
||||
|
||||
httpSvc3 := mock.NewHTTPServer(15005, func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(time.Second)
|
||||
w.Write([]byte("http3"))
|
||||
})
|
||||
err = httpSvc3.Start()
|
||||
if assert.NoError(err) {
|
||||
defer httpSvc3.Stop()
|
||||
}
|
||||
|
||||
httpSvc4 := mock.NewHTTPServer(15006, func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(time.Second)
|
||||
w.Write([]byte("http4"))
|
||||
})
|
||||
err = httpSvc4.Start()
|
||||
if assert.NoError(err) {
|
||||
defer httpSvc4.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// ****** start frps and frpc ******
|
||||
frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpsCfgPath)
|
||||
}
|
||||
|
||||
frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpcCfgPath)
|
||||
}
|
||||
|
||||
frpsProcess := util.NewProcess(consts.FRPS_SUB_BIN_PATH, []string{"-c", frpsCfgPath})
|
||||
err = frpsProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpsProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
frpcProcess := util.NewProcess(consts.FRPC_SUB_BIN_PATH, []string{"-c", frpcCfgPath})
|
||||
err = frpcProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpcProcess.Stop()
|
||||
}
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
// ****** healcheck type tcp ******
|
||||
// echo1 and echo2 is ok
|
||||
result := make([]string, 0)
|
||||
res, err := util.SendTCPMsg("127.0.0.1:15000", "echo")
|
||||
assert.NoError(err)
|
||||
result = append(result, res)
|
||||
|
||||
res, err = util.SendTCPMsg("127.0.0.1:15000", "echo")
|
||||
assert.NoError(err)
|
||||
result = append(result, res)
|
||||
|
||||
assert.Contains(result, "echo1")
|
||||
assert.Contains(result, "echo2")
|
||||
|
||||
// close echo2 server, echo1 is work
|
||||
echoSvc2.Stop()
|
||||
time.Sleep(1200 * time.Millisecond)
|
||||
|
||||
result = make([]string, 0)
|
||||
res, err = util.SendTCPMsg("127.0.0.1:15000", "echo")
|
||||
assert.NoError(err)
|
||||
result = append(result, res)
|
||||
|
||||
res, err = util.SendTCPMsg("127.0.0.1:15000", "echo")
|
||||
assert.NoError(err)
|
||||
result = append(result, res)
|
||||
|
||||
assert.NotContains(result, "echo2")
|
||||
|
||||
// resume echo2 server, all services are ok
|
||||
echoSvc2 = mock.NewEchoServer(15002, 1, "echo2")
|
||||
err = echoSvc2.Start()
|
||||
if assert.NoError(err) {
|
||||
defer echoSvc2.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(1200 * time.Millisecond)
|
||||
|
||||
result = make([]string, 0)
|
||||
res, err = util.SendTCPMsg("127.0.0.1:15000", "echo")
|
||||
assert.NoError(err)
|
||||
result = append(result, res)
|
||||
|
||||
res, err = util.SendTCPMsg("127.0.0.1:15000", "echo")
|
||||
assert.NoError(err)
|
||||
result = append(result, res)
|
||||
|
||||
assert.Contains(result, "echo1")
|
||||
assert.Contains(result, "echo2")
|
||||
|
||||
// ****** healcheck type http ******
|
||||
// http1 and http2 is ok
|
||||
code, body, _, err := util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("http1", body)
|
||||
|
||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("http2", body)
|
||||
|
||||
// http2 health check error
|
||||
healthMu.Lock()
|
||||
svc2Health = false
|
||||
healthMu.Unlock()
|
||||
time.Sleep(1200 * time.Millisecond)
|
||||
|
||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("http1", body)
|
||||
|
||||
code, _, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(404, code)
|
||||
|
||||
// resume http2 service, http1 and http2 are ok
|
||||
healthMu.Lock()
|
||||
svc2Health = true
|
||||
healthMu.Unlock()
|
||||
time.Sleep(1200 * time.Millisecond)
|
||||
|
||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("http1", body)
|
||||
|
||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("http2", body)
|
||||
|
||||
// ****** load balancing type http ******
|
||||
result = make([]string, 0)
|
||||
var wait sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
wait.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
code, body, _, err := util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
mu.Lock()
|
||||
result = append(result, body)
|
||||
mu.Unlock()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
|
||||
assert.NoError(err)
|
||||
assert.Equal(200, code)
|
||||
mu.Lock()
|
||||
result = append(result, body)
|
||||
mu.Unlock()
|
||||
}()
|
||||
wait.Wait()
|
||||
|
||||
assert.Contains(result, "http3")
|
||||
assert.Contains(result, "http4")
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
package ci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/fatedier/frp/client/proxy"
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
"github.com/fatedier/frp/tests/mock"
|
||||
"github.com/fatedier/frp/tests/util"
|
||||
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
tcpEcho1 := mock.NewEchoServer(consts.TEST_TCP_PORT, 1, "")
|
||||
tcpEcho2 := mock.NewEchoServer(consts.TEST_TCP2_PORT, 2, "")
|
||||
|
||||
if err = tcpEcho1.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tcpEcho2.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go mock.StartUDPEchoServer(consts.TEST_UDP_PORT)
|
||||
go mock.StartUnixDomainServer(consts.TEST_UNIX_DOMAIN_ADDR)
|
||||
go mock.StartHTTPServer(consts.TEST_HTTP_PORT)
|
||||
|
||||
p1 := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./auto_test_frps.ini"})
|
||||
if err = p1.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
p2 := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc.ini"})
|
||||
if err = p2.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
p3 := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", "./auto_test_frpc_visitor.ini"})
|
||||
if err = p3.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
exitCode := m.Run()
|
||||
p1.Stop()
|
||||
p2.Stop()
|
||||
p3.Stop()
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func TestHTTP(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// web01
|
||||
code, body, _, err := util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
|
||||
// web02
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test2.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
|
||||
// error host header
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "errorhost.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(404, code)
|
||||
}
|
||||
|
||||
// web03
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/foo", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_FOO_STR, body)
|
||||
}
|
||||
|
||||
// web04
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/bar", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_BAR_STR, body)
|
||||
}
|
||||
|
||||
// web05
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(401, code)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Authorization"] = util.BasicAuth("test", "test")
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", headers, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(401, code)
|
||||
}
|
||||
|
||||
// web06
|
||||
var header http.Header
|
||||
code, body, header, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test6.frp.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
assert.Equal("true", header.Get("X-Header-Set"))
|
||||
}
|
||||
|
||||
// wildcard_http
|
||||
// test.frp1.com match *.frp1.com
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test.frp1.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
|
||||
// new.test.frp1.com also match *.frp1.com
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "new.test.frp1.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
|
||||
// subhost01
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test01.sub.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("test01.sub.com", body)
|
||||
}
|
||||
|
||||
// subhost02
|
||||
code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test02.sub.com", nil, "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal("test02.sub.com", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebSocket(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
u := url.URL{Scheme: "ws", Host: fmt.Sprintf("%s:%d", "127.0.0.1", consts.TEST_HTTP_FRP_PORT), Path: "/ws"}
|
||||
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
||||
assert.NoError(err)
|
||||
defer c.Close()
|
||||
|
||||
err = c.WriteMessage(websocket.TextMessage, []byte(consts.TEST_HTTP_NORMAL_STR))
|
||||
assert.NoError(err)
|
||||
|
||||
_, msg, err := c.ReadMessage()
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, string(msg))
|
||||
}
|
||||
|
||||
func TestRandomPort(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// tcp
|
||||
status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTCPRandomPort)
|
||||
if assert.NoError(err) {
|
||||
addr := status.RemoteAddr
|
||||
res, err := util.SendTCPMsg(addr, consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
}
|
||||
|
||||
// udp
|
||||
status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUDPRandomPort)
|
||||
if assert.NoError(err) {
|
||||
addr := status.RemoteAddr
|
||||
res, err := util.SendUDPMsg(addr, consts.TEST_UDP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_UDP_ECHO_STR, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginHTTPProxy(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyHTTPProxy)
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(proxy.ProxyPhaseRunning, status.Status)
|
||||
|
||||
// http proxy
|
||||
addr := status.RemoteAddr
|
||||
code, body, _, err := util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT),
|
||||
"", nil, "http://"+addr)
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(200, code)
|
||||
assert.Equal(consts.TEST_HTTP_NORMAL_STR, body)
|
||||
}
|
||||
|
||||
// connect method
|
||||
conn, err := gnet.DialTcpByProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_FRP_PORT))
|
||||
if assert.NoError(err) {
|
||||
res, err := util.SendTCPMsgByConn(conn, consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangePortsMapping(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
name := fmt.Sprintf("%s_%d", consts.ProxyRangeTCPPrefix, i)
|
||||
status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, name)
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(proxy.ProxyPhaseRunning, status.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var (
|
||||
p1 int
|
||||
p2 int
|
||||
)
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP2_FRP_PORT)
|
||||
|
||||
for i := 0; i < 6; i++ {
|
||||
res, err := util.SendTCPMsg(addr, consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
switch res {
|
||||
case consts.TEST_TCP_ECHO_STR:
|
||||
p1++
|
||||
case consts.TEST_TCP_ECHO_STR + consts.TEST_TCP_ECHO_STR:
|
||||
p2++
|
||||
}
|
||||
}
|
||||
assert.True(p1 > 0 && p2 > 0, "group proxies load balancing")
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
package ci
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/tests/config"
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
"github.com/fatedier/frp/tests/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const FRPS_RECONNECT_CONF = `
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 20000
|
||||
log_file = console
|
||||
log_level = debug
|
||||
token = 123456
|
||||
`
|
||||
|
||||
const FRPC_RECONNECT_CONF = `
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = 20000
|
||||
log_file = console
|
||||
log_level = debug
|
||||
token = 123456
|
||||
admin_port = 21000
|
||||
admin_user = abc
|
||||
admin_pwd = abc
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = 20801
|
||||
`
|
||||
|
||||
func TestReconnect(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_RECONNECT_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpsCfgPath)
|
||||
}
|
||||
|
||||
frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RECONNECT_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpcCfgPath)
|
||||
}
|
||||
|
||||
frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
|
||||
err = frpsProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpsProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
|
||||
err = frpcProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpcProcess.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// test tcp
|
||||
res, err := util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// stop frpc
|
||||
frpcProcess.Stop()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// test tcp, expect failed
|
||||
_, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.Error(err)
|
||||
|
||||
// restart frpc
|
||||
newFrpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
|
||||
err = newFrpcProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer newFrpcProcess.Stop()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// test tcp
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// stop frps
|
||||
frpsProcess.Stop()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// test tcp, expect failed
|
||||
_, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.Error(err)
|
||||
|
||||
// restart frps
|
||||
newFrpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
|
||||
err = newFrpsProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer newFrpsProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// test tcp
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package ci
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/fatedier/frp/tests/config"
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
"github.com/fatedier/frp/tests/util"
|
||||
)
|
||||
|
||||
const FRPS_RELOAD_CONF = `
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 20000
|
||||
log_file = console
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
token = 123456
|
||||
`
|
||||
|
||||
const FRPC_RELOAD_CONF_1 = `
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = 20000
|
||||
log_file = console
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
token = 123456
|
||||
admin_port = 21000
|
||||
admin_user = abc
|
||||
admin_pwd = abc
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = 20801
|
||||
|
||||
# change remote port
|
||||
[tcp2]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = 20802
|
||||
|
||||
# delete
|
||||
[tcp3]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = 20803
|
||||
`
|
||||
|
||||
const FRPC_RELOAD_CONF_2 = `
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = 20000
|
||||
log_file = console
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
token = 123456
|
||||
admin_port = 21000
|
||||
admin_user = abc
|
||||
admin_pwd = abc
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = 20801
|
||||
|
||||
[tcp2]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = 20902
|
||||
`
|
||||
|
||||
func TestReload(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_RELOAD_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpsCfgPath)
|
||||
}
|
||||
|
||||
frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RELOAD_CONF_1)
|
||||
if assert.NoError(err) {
|
||||
rmFile1 := frpcCfgPath
|
||||
defer os.Remove(rmFile1)
|
||||
}
|
||||
|
||||
frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
|
||||
err = frpsProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpsProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
|
||||
err = frpcProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpcProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// test tcp1
|
||||
res, err := util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// test tcp2
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// test tcp3
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// reload frpc config
|
||||
frpcCfgPath, err = config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_RELOAD_CONF_2)
|
||||
if assert.NoError(err) {
|
||||
rmFile2 := frpcCfgPath
|
||||
defer os.Remove(rmFile2)
|
||||
}
|
||||
err = util.ReloadConf("127.0.0.1:21000", "abc", "abc")
|
||||
assert.NoError(err)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// test tcp1
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// test origin tcp2, expect failed
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR)
|
||||
assert.Error(err)
|
||||
|
||||
// test new origin tcp2 with different port
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20902", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
|
||||
// test tcp3, expect failed
|
||||
res, err = util.SendTCPMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR)
|
||||
assert.Error(err)
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package ci
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/tests/config"
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
"github.com/fatedier/frp/tests/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const FRPS_TEMPLATE_CONF = `
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = {{ .Envs.SERVER_PORT }}
|
||||
log_file = console
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
token = 123456
|
||||
`
|
||||
|
||||
const FRPC_TEMPLATE_CONF = `
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = 20000
|
||||
log_file = console
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
token = {{ .Envs.FRP_TOKEN }}
|
||||
|
||||
[tcp]
|
||||
type = tcp
|
||||
local_port = 10701
|
||||
remote_port = {{ .Envs.TCP_REMOTE_PORT }}
|
||||
`
|
||||
|
||||
func TestConfTemplate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TEMPLATE_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpsCfgPath)
|
||||
}
|
||||
|
||||
frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TEMPLATE_CONF)
|
||||
if assert.NoError(err) {
|
||||
defer os.Remove(frpcCfgPath)
|
||||
}
|
||||
|
||||
frpsProcess := util.NewProcess("env", []string{"SERVER_PORT=20000", consts.FRPS_BIN_PATH, "-c", frpsCfgPath})
|
||||
err = frpsProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpsProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
frpcProcess := util.NewProcess("env", []string{"FRP_TOKEN=123456", "TCP_REMOTE_PORT=20801", consts.FRPC_BIN_PATH, "-c", frpcCfgPath})
|
||||
err = frpcProcess.Start()
|
||||
if assert.NoError(err) {
|
||||
defer frpcProcess.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// test tcp1
|
||||
res, err := util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func GenerateConfigFile(path string, content string) (realPath string, err error) {
|
||||
realPath = filepath.Join(os.TempDir(), path)
|
||||
err = ioutil.WriteFile(realPath, []byte(content), 0666)
|
||||
return realPath, err
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package consts
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
var (
|
||||
FRPS_BIN_PATH = "../../bin/frps"
|
||||
FRPC_BIN_PATH = "../../bin/frpc"
|
||||
|
||||
FRPS_SUB_BIN_PATH = "../../../bin/frps"
|
||||
FRPC_SUB_BIN_PATH = "../../../bin/frpc"
|
||||
|
||||
FRPS_NORMAL_CONFIG = "./auto_test_frps.ini"
|
||||
FRPC_NORMAL_CONFIG = "./auto_test_frpc.ini"
|
||||
|
||||
SERVER_ADDR = "127.0.0.1"
|
||||
ADMIN_ADDR = "127.0.0.1:10600"
|
||||
ADMIN_USER = "abc"
|
||||
ADMIN_PWD = "abc"
|
||||
|
||||
TEST_STR = "frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet."
|
||||
TEST_TCP_PORT int = 10701
|
||||
TEST_TCP2_PORT int = 10702
|
||||
TEST_TCP_FRP_PORT int = 10801
|
||||
TEST_TCP2_FRP_PORT int = 10802
|
||||
TEST_TCP_EC_FRP_PORT int = 10901
|
||||
TEST_TCP_ECHO_STR string = "tcp type:" + TEST_STR
|
||||
|
||||
TEST_UDP_PORT int = 10702
|
||||
TEST_UDP_FRP_PORT int = 10802
|
||||
TEST_UDP_EC_FRP_PORT int = 10902
|
||||
TEST_UDP_ECHO_STR string = "udp type:" + TEST_STR
|
||||
|
||||
TEST_UNIX_DOMAIN_ADDR string = "/tmp/frp_echo_server.sock"
|
||||
TEST_UNIX_DOMAIN_FRP_PORT int = 10803
|
||||
TEST_UNIX_DOMAIN_STR string = "unix domain type:" + TEST_STR
|
||||
|
||||
TEST_HTTP_PORT int = 10704
|
||||
TEST_HTTP_FRP_PORT int = 10804
|
||||
TEST_HTTP_NORMAL_STR string = "http normal string: " + TEST_STR
|
||||
TEST_HTTP_FOO_STR string = "http foo string: " + TEST_STR
|
||||
TEST_HTTP_BAR_STR string = "http bar string: " + TEST_STR
|
||||
|
||||
TEST_TCP_MUX_FRP_PORT int = 10806
|
||||
|
||||
TEST_STCP_FRP_PORT int = 10805
|
||||
TEST_STCP_EC_FRP_PORT int = 10905
|
||||
TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR
|
||||
|
||||
TEST_SUDP_FRP_PORT int = 10816
|
||||
TEST_SUDP_ECHO_STR string = "sudp type:" + TEST_STR
|
||||
|
||||
ProxyTCPPortNotAllowed string = "tcp_port_not_allowed"
|
||||
ProxyTCPPortUnavailable string = "tcp_port_unavailable"
|
||||
ProxyTCPPortNormal string = "tcp_port_normal"
|
||||
ProxyTCPRandomPort string = "tcp_random_port"
|
||||
ProxyUDPPortNotAllowed string = "udp_port_not_allowed"
|
||||
ProxyUDPPortNormal string = "udp_port_normal"
|
||||
ProxyUDPRandomPort string = "udp_random_port"
|
||||
ProxyHTTPProxy string = "http_proxy"
|
||||
|
||||
ProxyRangeTCPPrefix string = "range_tcp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if path, err := filepath.Abs(FRPS_BIN_PATH); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
FRPS_BIN_PATH = path
|
||||
}
|
||||
|
||||
if path, err := filepath.Abs(FRPC_BIN_PATH); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
FRPC_BIN_PATH = path
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||
)
|
||||
|
||||
type EchoServer struct {
|
||||
l net.Listener
|
||||
|
||||
port int
|
||||
repeatedNum int
|
||||
specifyStr string
|
||||
}
|
||||
|
||||
func NewEchoServer(port int, repeatedNum int, specifyStr string) *EchoServer {
|
||||
if repeatedNum <= 0 {
|
||||
repeatedNum = 1
|
||||
}
|
||||
return &EchoServer{
|
||||
port: port,
|
||||
repeatedNum: repeatedNum,
|
||||
specifyStr: specifyStr,
|
||||
}
|
||||
}
|
||||
|
||||
func (es *EchoServer) Start() error {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", es.port))
|
||||
if err != nil {
|
||||
fmt.Printf("echo server listen error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
es.l = l
|
||||
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go echoWorker(c, es.repeatedNum, es.specifyStr)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *EchoServer) Stop() {
|
||||
es.l.Close()
|
||||
}
|
||||
|
||||
func StartUDPEchoServer(port int) {
|
||||
l, err := frpNet.ListenUDP("127.0.0.1", port)
|
||||
if err != nil {
|
||||
fmt.Printf("udp echo server listen error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Printf("udp echo server accept error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
go echoWorker(c, 1, "")
|
||||
}
|
||||
}
|
||||
|
||||
func StartUnixDomainServer(unixPath string) {
|
||||
os.Remove(unixPath)
|
||||
syscall.Umask(0)
|
||||
l, err := net.Listen("unix", unixPath)
|
||||
if err != nil {
|
||||
fmt.Printf("unix domain server listen error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Printf("unix domain server accept error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
go echoWorker(c, 1, "")
|
||||
}
|
||||
}
|
||||
|
||||
func echoWorker(c net.Conn, repeatedNum int, specifyStr string) {
|
||||
buf := make([]byte, 2048)
|
||||
|
||||
for {
|
||||
n, err := c.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
c.Close()
|
||||
break
|
||||
} else {
|
||||
fmt.Printf("echo server read error: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if specifyStr != "" {
|
||||
c.Write([]byte(specifyStr))
|
||||
} else {
|
||||
var w []byte
|
||||
for i := 0; i < repeatedNum; i++ {
|
||||
w = append(w, buf[:n]...)
|
||||
}
|
||||
c.Write(w)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/tests/consts"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type HTTPServer struct {
|
||||
l net.Listener
|
||||
|
||||
port int
|
||||
handler http.HandlerFunc
|
||||
}
|
||||
|
||||
func NewHTTPServer(port int, handler http.HandlerFunc) *HTTPServer {
|
||||
return &HTTPServer{
|
||||
port: port,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Start() error {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", hs.port))
|
||||
if err != nil {
|
||||
fmt.Printf("http server listen error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
hs.l = l
|
||||
|
||||
go http.Serve(l, http.HandlerFunc(hs.handler))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Stop() {
|
||||
hs.l.Close()
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{}
|
||||
|
||||
func StartHTTPServer(port int) {
|
||||
http.HandleFunc("/", handleHTTP)
|
||||
http.HandleFunc("/ws", handleWebSocket)
|
||||
http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil)
|
||||
}
|
||||
|
||||
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Print("upgrade:", err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
for {
|
||||
mt, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
err = c.WriteMessage(mt, message)
|
||||
if err != nil {
|
||||
log.Println("write:", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-From-Where") == "frp" {
|
||||
w.Header().Set("X-Header-Set", "true")
|
||||
}
|
||||
|
||||
match, err := regexp.Match(`.*\.sub\.com`, []byte(r.Host))
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
if match {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(r.Host))
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.Host, "127.0.0.1") || strings.HasPrefix(r.Host, "test2.frp.com") ||
|
||||
strings.HasPrefix(r.Host, "test5.frp.com") || strings.HasPrefix(r.Host, "test6.frp.com") ||
|
||||
strings.HasPrefix(r.Host, "test.frp1.com") || strings.HasPrefix(r.Host, "new.test.frp1.com") {
|
||||
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(consts.TEST_HTTP_NORMAL_STR))
|
||||
} else if strings.Contains(r.Host, "test3.frp.com") {
|
||||
w.WriteHeader(200)
|
||||
if strings.Contains(r.URL.Path, "foo") {
|
||||
w.Write([]byte(consts.TEST_HTTP_FOO_STR))
|
||||
} else if strings.Contains(r.URL.Path, "bar") {
|
||||
w.Write([]byte(consts.TEST_HTTP_BAR_STR))
|
||||
} else {
|
||||
w.Write([]byte(consts.TEST_HTTP_NORMAL_STR))
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(404)
|
||||
}
|
||||
return
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
cmd *exec.Cmd
|
||||
cancel context.CancelFunc
|
||||
errorOutput *bytes.Buffer
|
||||
|
||||
beforeStopHandler func()
|
||||
}
|
||||
|
||||
func NewProcess(path string, params []string) *Process {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cmd := exec.CommandContext(ctx, path, params...)
|
||||
p := &Process{
|
||||
cmd: cmd,
|
||||
cancel: cancel,
|
||||
}
|
||||
p.errorOutput = bytes.NewBufferString("")
|
||||
cmd.Stderr = p.errorOutput
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Process) Start() error {
|
||||
return p.cmd.Start()
|
||||
}
|
||||
|
||||
func (p *Process) Stop() error {
|
||||
if p.beforeStopHandler != nil {
|
||||
p.beforeStopHandler()
|
||||
}
|
||||
p.cancel()
|
||||
return p.cmd.Wait()
|
||||
}
|
||||
|
||||
func (p *Process) ErrorOutput() string {
|
||||
return p.errorOutput.String()
|
||||
}
|
||||
|
||||
func (p *Process) SetBeforeStopHandler(fn func()) {
|
||||
p.beforeStopHandler = fn
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
)
|
||||
|
||||
func GetProxyStatus(statusAddr string, user string, passwd string, name string) (status *client.ProxyStatusResp, err error) {
|
||||
req, err := http.NewRequest("GET", "http://"+statusAddr+"/api/status", nil)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+passwd))
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return status, fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
allStatus := &client.StatusResp{}
|
||||
err = json.Unmarshal(body, &allStatus)
|
||||
if err != nil {
|
||||
return status, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
}
|
||||
for _, s := range allStatus.TCP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.UDP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.HTTP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.HTTPS {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.STCP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.XTCP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
for _, s := range allStatus.SUDP {
|
||||
if s.Name == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
|
||||
return status, errors.New("no proxy status found")
|
||||
}
|
||||
|
||||
func ReloadConf(reloadAddr string, user string, passwd string) error {
|
||||
req, err := http.NewRequest("GET", "http://"+reloadAddr+"/api/reload", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+passwd))
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendTCPMsg(addr string, msg string) (res string, err error) {
|
||||
c, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("connect to tcp server error: %v", err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
return SendTCPMsgByConn(c, msg)
|
||||
}
|
||||
|
||||
func SendTCPMsgByConn(c net.Conn, msg string) (res string, err error) {
|
||||
timer := time.Now().Add(5 * time.Second)
|
||||
c.SetDeadline(timer)
|
||||
c.Write([]byte(msg))
|
||||
|
||||
buf := make([]byte, 2048)
|
||||
n, errRet := c.Read(buf)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("read from tcp server error: %v", errRet)
|
||||
return
|
||||
}
|
||||
return string(buf[:n]), nil
|
||||
}
|
||||
|
||||
func SendUDPMsg(addr string, msg string) (res string, err error) {
|
||||
udpAddr, errRet := net.ResolveUDPAddr("udp", addr)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("resolve udp addr error: %v", err)
|
||||
return
|
||||
}
|
||||
conn, errRet := net.DialUDP("udp", nil, udpAddr)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("dial udp server error: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
_, err = conn.Write([]byte(msg))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("write to udp server error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
buf := make([]byte, 2048)
|
||||
n, errRet := conn.Read(buf)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("read from udp server error: %v", err)
|
||||
return
|
||||
}
|
||||
return string(buf[:n]), nil
|
||||
}
|
||||
|
||||
func SendHTTPMsg(method, urlStr string, host string, headers map[string]string, proxy string) (code int, body string, header http.Header, err error) {
|
||||
req, errRet := http.NewRequest(method, urlStr, nil)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
}
|
||||
|
||||
if host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
if len(proxy) != 0 {
|
||||
tr.Proxy = func(req *http.Request) (*url.URL, error) {
|
||||
return url.Parse(proxy)
|
||||
}
|
||||
}
|
||||
client := http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
|
||||
resp, errRet := client.Do(req)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
}
|
||||
code = resp.StatusCode
|
||||
header = resp.Header
|
||||
buf, errRet := ioutil.ReadAll(resp.Body)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
}
|
||||
body = string(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func BasicAuth(username, passwd string) string {
|
||||
auth := username + ":" + passwd
|
||||
return "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
</el-row>
|
||||
</header>
|
||||
<section>
|
||||
<el-row :gutter="20">
|
||||
<el-row>
|
||||
<el-col id="side-nav" :xs="24" :md="4">
|
||||
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
|
||||
<el-menu-item index="/">Overview</el-menu-item>
|
||||
|
@ -22,7 +22,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
status: null
|
||||
status: new Array(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -97,7 +97,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: null,
|
||||
proxies: new Array(),
|
||||
vhost_http_port: "",
|
||||
subdomain_host: ""
|
||||
}
|
||||
|
@ -92,7 +92,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: null,
|
||||
proxies: new Array(),
|
||||
vhost_https_port: '',
|
||||
subdomain_host: ''
|
||||
}
|
||||
|
@ -78,7 +78,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: null
|
||||
proxies: new Array(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -78,7 +78,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: null
|
||||
proxies: new Array(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -86,7 +86,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: null
|
||||
proxies: new Array(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -88,7 +88,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: null
|
||||
proxies: new Array(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user