mirror of
https://github.com/fatedier/frp.git
synced 2026-04-15 05:29:10 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10f2620131 | ||
|
|
4acae540c8 | ||
|
|
11b13533a0 | ||
|
|
100d556336 | ||
|
|
452fe25cc6 | ||
|
|
63efa6b776 | ||
|
|
37c27169ac | ||
|
|
ce677820c6 | ||
|
|
1f88a7a0b8 | ||
|
|
eeea7602d9 | ||
|
|
bf635c0e90 | ||
|
|
cd31359a27 | ||
|
|
19739ed31a | ||
|
|
10100c28d9 | ||
|
|
88fcc079e8 | ||
|
|
ddc1e163c4 | ||
|
|
d20a6d3d75 | ||
|
|
6194273615 | ||
|
|
b2311e55e7 | ||
|
|
07873d471f |
@@ -2,14 +2,14 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
go-version-latest:
|
go-version-latest:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/go:1.17-node
|
- image: cimg/go:1.18-node
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: make
|
- run: make
|
||||||
- run: make alltest
|
- run: make alltest
|
||||||
go-version-last:
|
go-version-last:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/go:1.16-node
|
- image: cimg/go:1.17-node
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: make
|
- run: make
|
||||||
|
|||||||
2
.github/workflows/build-and-push-image.yml
vendored
2
.github/workflows/build-and-push-image.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.17
|
go-version: 1.18
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
# https://github.com/actions/setup-go/issues/107
|
# https://github.com/actions/setup-go/issues/107
|
||||||
|
|||||||
2
.github/workflows/goreleaser.yml
vendored
2
.github/workflows/goreleaser.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.17
|
go-version: 1.18
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
# https://github.com/actions/setup-go/issues/107
|
# https://github.com/actions/setup-go/issues/107
|
||||||
|
|||||||
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
@@ -12,11 +12,11 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v3
|
- uses: actions/stale@v5
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
|
stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
|
||||||
stale-pr-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
|
stale-pr-message: "PRs go stale after 30d of inactivity. Stale PRs rot after an additional 7d of inactivity and eventually close."
|
||||||
stale-issue-label: 'lifecycle/stale'
|
stale-issue-label: 'lifecycle/stale'
|
||||||
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
|
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
|
||||||
stale-pr-label: 'lifecycle/stale'
|
stale-pr-label: 'lifecycle/stale'
|
||||||
@@ -24,3 +24,5 @@ jobs:
|
|||||||
days-before-stale: 30
|
days-before-stale: 30
|
||||||
days-before-close: 7
|
days-before-close: 7
|
||||||
debug-only: ${{ github.event.inputs.debug-only }}
|
debug-only: ${{ github.event.inputs.debug-only }}
|
||||||
|
exempt-all-pr-milestones: true
|
||||||
|
exempt-all-pr-assignees: true
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
builds:
|
builds:
|
||||||
- skip: true
|
- skip: true
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: '{{ .ProjectName }}_{{ .Version }}_sha256_checksums.txt'
|
||||||
|
algorithm: sha256
|
||||||
|
extra_files:
|
||||||
|
- glob: ./release/packages/*
|
||||||
release:
|
release:
|
||||||
# Same as for github
|
# Same as for github
|
||||||
# Note: it can only be one: either github, gitlab or gitea
|
# Note: it can only be one: either github, gitlab or gitea
|
||||||
|
|||||||
36
README.md
36
README.md
@@ -6,12 +6,6 @@
|
|||||||
|
|
||||||
[README](README.md) | [中文文档](README_zh.md)
|
[README](README.md) | [中文文档](README_zh.md)
|
||||||
|
|
||||||
## What is frp?
|
|
||||||
|
|
||||||
frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
|
|
||||||
|
|
||||||
frp also has a P2P connect mode.
|
|
||||||
|
|
||||||
<h3 align="center">Platinum Sponsors</h3>
|
<h3 align="center">Platinum Sponsors</h3>
|
||||||
<!--platinum sponsors start-->
|
<!--platinum sponsors start-->
|
||||||
|
|
||||||
@@ -23,10 +17,27 @@ frp also has a P2P connect mode.
|
|||||||
|
|
||||||
<!--platinum sponsors end-->
|
<!--platinum sponsors end-->
|
||||||
|
|
||||||
|
<h3 align="center">Gold Sponsors</h3>
|
||||||
|
<!--gold sponsors start-->
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
|
||||||
|
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!--gold sponsors end-->
|
||||||
|
|
||||||
<h3 align="center">Silver Sponsors</h3>
|
<h3 align="center">Silver Sponsors</h3>
|
||||||
|
|
||||||
* Sakura Frp - 欢迎点击 "加入我们"
|
* Sakura Frp - 欢迎点击 "加入我们"
|
||||||
|
|
||||||
|
## What is frp?
|
||||||
|
|
||||||
|
frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
|
||||||
|
|
||||||
|
frp also has a P2P connect mode.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
<!-- vim-markdown-toc GFM -->
|
<!-- vim-markdown-toc GFM -->
|
||||||
@@ -82,8 +93,7 @@ frp also has a P2P connect mode.
|
|||||||
* [Development Plan](#development-plan)
|
* [Development Plan](#development-plan)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
* [Donation](#donation)
|
* [Donation](#donation)
|
||||||
* [AliPay](#alipay)
|
* [GitHub Sponsors](#github-sponsors)
|
||||||
* [Wechat Pay](#wechat-pay)
|
|
||||||
* [PayPal](#paypal)
|
* [PayPal](#paypal)
|
||||||
|
|
||||||
<!-- vim-markdown-toc -->
|
<!-- vim-markdown-toc -->
|
||||||
@@ -1092,15 +1102,11 @@ Interested in getting involved? We would like to help you!
|
|||||||
|
|
||||||
If frp helps you a lot, you can support us by:
|
If frp helps you a lot, you can support us by:
|
||||||
|
|
||||||
frp QQ group: 606194980
|
### GitHub Sponsors
|
||||||
|
|
||||||
### AliPay
|
Support us by [Github Sponsors](https://github.com/sponsors/fatedier).
|
||||||
|
|
||||||

|
You can have your company's logo placed on README file of this project.
|
||||||
|
|
||||||
### Wechat Pay
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### PayPal
|
### PayPal
|
||||||
|
|
||||||
|
|||||||
21
README_zh.md
21
README_zh.md
@@ -18,6 +18,17 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
|
|||||||
|
|
||||||
<!--platinum sponsors end-->
|
<!--platinum sponsors end-->
|
||||||
|
|
||||||
|
<h3 align="center">Gold Sponsors</h3>
|
||||||
|
<!--gold sponsors start-->
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
|
||||||
|
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!--gold sponsors end-->
|
||||||
|
|
||||||
<h3 align="center">Silver Sponsors</h3>
|
<h3 align="center">Silver Sponsors</h3>
|
||||||
|
|
||||||
* Sakura Frp - 欢迎点击 "加入我们"
|
* Sakura Frp - 欢迎点击 "加入我们"
|
||||||
@@ -65,6 +76,12 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
|
|||||||
|
|
||||||
如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。
|
如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。
|
||||||
|
|
||||||
|
### GitHub Sponsors
|
||||||
|
|
||||||
|
您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。
|
||||||
|
|
||||||
|
企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。
|
||||||
|
|
||||||
### 知识星球
|
### 知识星球
|
||||||
|
|
||||||
如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:
|
如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:
|
||||||
@@ -78,7 +95,3 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
|
|||||||
### 微信支付捐赠
|
### 微信支付捐赠
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Paypal 捐赠
|
|
||||||
|
|
||||||
海外用户推荐通过 [Paypal](https://www.paypal.me/fatedier) 向我的账户 **fatedier@gmail.com** 进行捐赠。
|
|
||||||
|
|||||||
10
Release.md
10
Release.md
@@ -1,12 +1,8 @@
|
|||||||
### New
|
### New
|
||||||
|
|
||||||
* Added `connect_server_local_ip` in frpc to specify local IP connected to frps.
|
* Support go http pprof.
|
||||||
* Added `tcp_mux_keepalive_interval` both in frpc and frps to set `tcp_mux` keepalive interval seconds if `tcp_mux` is enabled. After using this params, you can set `heartbeat_interval` to `-1` to disable application layer heartbeat to reduce traffic usage(Make sure frps is in the latest version).
|
|
||||||
|
|
||||||
### Improve
|
### Improve
|
||||||
|
|
||||||
* Server Plugin: Added `client_address` in Login Operation.
|
* Change underlying TCP connection keepalive interval to 2 hours.
|
||||||
|
* Create new connection to server for `sudp` visitor when needed, to avoid frequent reconnections.
|
||||||
### Fix
|
|
||||||
|
|
||||||
* Remove authentication for healthz api.
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"github.com/fatedier/frp/assets"
|
||||||
@@ -26,8 +27,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
httpServerReadTimeout = 10 * time.Second
|
httpServerReadTimeout = 60 * time.Second
|
||||||
httpServerWriteTimeout = 10 * time.Second
|
httpServerWriteTimeout = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func (svr *Service) RunAdminServer(address string) (err error) {
|
func (svr *Service) RunAdminServer(address string) (err error) {
|
||||||
@@ -36,6 +37,15 @@ func (svr *Service) RunAdminServer(address string) (err error) {
|
|||||||
|
|
||||||
router.HandleFunc("/healthz", svr.healthz)
|
router.HandleFunc("/healthz", svr.healthz)
|
||||||
|
|
||||||
|
// debug
|
||||||
|
if svr.cfg.PprofEnable {
|
||||||
|
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||||
|
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
|
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
|
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||||
|
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
||||||
|
}
|
||||||
|
|
||||||
subRouter := router.NewRoute().Subrouter()
|
subRouter := router.NewRoute().Subrouter()
|
||||||
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
|
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
|
||||||
subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
|
subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
|
|||||||
}
|
}
|
||||||
dialOptions = append(dialOptions,
|
dialOptions = append(dialOptions,
|
||||||
libdial.WithProtocol(protocol),
|
libdial.WithProtocol(protocol),
|
||||||
|
libdial.WithTimeout(time.Duration(ctl.clientCfg.DialServerTimeout)*time.Second),
|
||||||
|
libdial.WithKeepAlive(time.Duration(ctl.clientCfg.DialServerKeepAlive)*time.Second),
|
||||||
libdial.WithProxy(proxyType, addr),
|
libdial.WithProxy(proxyType, addr),
|
||||||
libdial.WithProxyAuth(auth),
|
libdial.WithProxyAuth(auth),
|
||||||
libdial.WithTLSConfig(tlsConfig),
|
libdial.WithTLSConfig(tlsConfig),
|
||||||
|
|||||||
@@ -347,22 +347,18 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
|||||||
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
|
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
|
||||||
|
|
||||||
// Send detect message
|
// Send detect message
|
||||||
array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
|
host, portStr, err := net.SplitHostPort(natHoleRespMsg.VisitorAddr)
|
||||||
if len(array) <= 1 {
|
if err != nil {
|
||||||
xl.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
|
xl.Error("get NatHoleResp visitor address [%s] error: %v", natHoleRespMsg.VisitorAddr, err)
|
||||||
}
|
}
|
||||||
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
|
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
|
||||||
/*
|
|
||||||
for i := 1000; i < 65000; i++ {
|
port, err := strconv.ParseInt(portStr, 10, 64)
|
||||||
pxy.sendDetectMsg(array[0], int64(i), laddr, "a")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
port, err := strconv.ParseInt(array[1], 10, 64)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
|
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
|
pxy.sendDetectMsg(host, int(port), laddr, []byte(natHoleRespMsg.Sid))
|
||||||
xl.Trace("send all detect msg done")
|
xl.Trace("send all detect msg done")
|
||||||
|
|
||||||
msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
|
msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
|
||||||
@@ -791,7 +787,10 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
localConn, err := libdial.Dial(net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort)))
|
localConn, err := libdial.Dial(
|
||||||
|
net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort)),
|
||||||
|
libdial.WithTimeout(10*time.Second),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err)
|
xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package client
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -34,6 +33,7 @@ import (
|
|||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
"github.com/fatedier/frp/pkg/util/version"
|
"github.com/fatedier/frp/pkg/util/version"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
libdial "github.com/fatedier/golib/net/dial"
|
libdial "github.com/fatedier/golib/net/dial"
|
||||||
@@ -109,7 +109,7 @@ func (svr *Service) Run() error {
|
|||||||
if svr.cfg.LoginFailExit {
|
if svr.cfg.LoginFailExit {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Second)
|
util.RandomSleep(10*time.Second, 0.9, 1.1)
|
||||||
} else {
|
} else {
|
||||||
// login success
|
// login success
|
||||||
ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
|
ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
|
||||||
@@ -158,8 +158,11 @@ func (svr *Service) keepControllerWorking() {
|
|||||||
|
|
||||||
// the first three retry with no delay
|
// the first three retry with no delay
|
||||||
if reconnectCounts > 3 {
|
if reconnectCounts > 3 {
|
||||||
time.Sleep(reconnectDelay)
|
util.RandomSleep(reconnectDelay, 0.9, 1.1)
|
||||||
|
xl.Info("wait %v to reconnect", reconnectDelay)
|
||||||
reconnectDelay *= 2
|
reconnectDelay *= 2
|
||||||
|
} else {
|
||||||
|
util.RandomSleep(time.Second, 0, 0.5)
|
||||||
}
|
}
|
||||||
reconnectCounts++
|
reconnectCounts++
|
||||||
|
|
||||||
@@ -175,18 +178,12 @@ func (svr *Service) keepControllerWorking() {
|
|||||||
xl.Info("try to reconnect to server...")
|
xl.Info("try to reconnect to server...")
|
||||||
conn, session, err := svr.login()
|
conn, session, err := svr.login()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Warn("reconnect to server error: %v", err)
|
xl.Warn("reconnect to server error: %v, wait %v for another retry", err, delayTime)
|
||||||
time.Sleep(delayTime)
|
util.RandomSleep(delayTime, 0.9, 1.1)
|
||||||
|
|
||||||
opErr := &net.OpError{}
|
delayTime = delayTime * 2
|
||||||
// quick retry for dial error
|
if delayTime > maxDelayTime {
|
||||||
if errors.As(err, &opErr) && opErr.Op == "dial" {
|
delayTime = maxDelayTime
|
||||||
delayTime = 2 * time.Second
|
|
||||||
} else {
|
|
||||||
delayTime = delayTime * 2
|
|
||||||
if delayTime > maxDelayTime {
|
|
||||||
delayTime = maxDelayTime
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -245,6 +242,8 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
|
|||||||
}
|
}
|
||||||
dialOptions = append(dialOptions,
|
dialOptions = append(dialOptions,
|
||||||
libdial.WithProtocol(protocol),
|
libdial.WithProtocol(protocol),
|
||||||
|
libdial.WithTimeout(time.Duration(svr.cfg.DialServerTimeout)*time.Second),
|
||||||
|
libdial.WithKeepAlive(time.Duration(svr.cfg.DialServerKeepAlive)*time.Second),
|
||||||
libdial.WithProxy(proxyType, addr),
|
libdial.WithProxy(proxyType, addr),
|
||||||
libdial.WithProxyAuth(auth),
|
libdial.WithProxyAuth(auth),
|
||||||
libdial.WithTLSConfig(tlsConfig),
|
libdial.WithTLSConfig(tlsConfig),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ type STCPVisitor struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sv *STCPVisitor) Run() (err error) {
|
func (sv *STCPVisitor) Run() (err error) {
|
||||||
sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
|
sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -174,7 +175,7 @@ type XTCPVisitor struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sv *XTCPVisitor) Run() (err error) {
|
func (sv *XTCPVisitor) Run() (err error) {
|
||||||
sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
|
sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -352,7 +353,7 @@ type SUDPVisitor struct {
|
|||||||
func (sv *SUDPVisitor) Run() (err error) {
|
func (sv *SUDPVisitor) Run() (err error) {
|
||||||
xl := xlog.FromContextSafe(sv.ctx)
|
xl := xlog.FromContextSafe(sv.ctx)
|
||||||
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
|
addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("sudp ResolveUDPAddr error: %v", err)
|
return fmt.Errorf("sudp ResolveUDPAddr error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -376,29 +377,33 @@ func (sv *SUDPVisitor) Run() (err error) {
|
|||||||
func (sv *SUDPVisitor) dispatcher() {
|
func (sv *SUDPVisitor) dispatcher() {
|
||||||
xl := xlog.FromContextSafe(sv.ctx)
|
xl := xlog.FromContextSafe(sv.ctx)
|
||||||
|
|
||||||
|
var (
|
||||||
|
visitorConn net.Conn
|
||||||
|
err error
|
||||||
|
|
||||||
|
firstPacket *msg.UDPPacket
|
||||||
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// loop for get frpc to frps tcp conn
|
select {
|
||||||
// setup worker
|
case firstPacket = <-sv.sendCh:
|
||||||
// wait worker to finished
|
if firstPacket == nil {
|
||||||
// retry or exit
|
|
||||||
visitorConn, err := sv.getNewVisitorConn()
|
|
||||||
if err != nil {
|
|
||||||
// check if proxy is closed
|
|
||||||
// if checkCloseCh is close, we will return, other case we will continue to reconnect
|
|
||||||
select {
|
|
||||||
case <-sv.checkCloseCh:
|
|
||||||
xl.Info("frpc sudp visitor proxy is closed")
|
xl.Info("frpc sudp visitor proxy is closed")
|
||||||
return
|
return
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
case <-sv.checkCloseCh:
|
||||||
|
xl.Info("frpc sudp visitor proxy is closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
time.Sleep(3 * time.Second)
|
visitorConn, err = sv.getNewVisitorConn()
|
||||||
|
if err != nil {
|
||||||
xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
|
xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sv.worker(visitorConn)
|
// visitorConn always be closed when worker done.
|
||||||
|
sv.worker(visitorConn, firstPacket)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sv.checkCloseCh:
|
case <-sv.checkCloseCh:
|
||||||
@@ -406,9 +411,10 @@ func (sv *SUDPVisitor) dispatcher() {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *SUDPVisitor) worker(workConn net.Conn) {
|
func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
|
||||||
xl := xlog.FromContextSafe(sv.ctx)
|
xl := xlog.FromContextSafe(sv.ctx)
|
||||||
xl.Debug("starting sudp proxy worker")
|
xl.Debug("starting sudp proxy worker")
|
||||||
|
|
||||||
@@ -462,6 +468,14 @@ func (sv *SUDPVisitor) worker(workConn net.Conn) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var errRet error
|
var errRet error
|
||||||
|
if firstPacket != nil {
|
||||||
|
if errRet = msg.WriteMsg(conn, firstPacket); errRet != nil {
|
||||||
|
xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xl.Trace("send udp package to workConn: %s", firstPacket.Content)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case udpMsg, ok := <-sv.sendCh:
|
case udpMsg, ok := <-sv.sendCh:
|
||||||
|
|||||||
@@ -6,6 +6,13 @@
|
|||||||
server_addr = 0.0.0.0
|
server_addr = 0.0.0.0
|
||||||
server_port = 7000
|
server_port = 7000
|
||||||
|
|
||||||
|
# The maximum amount of time a dial to server will wait for a connect to complete. Default value is 10 seconds.
|
||||||
|
# dial_server_timeout = 10
|
||||||
|
|
||||||
|
# dial_server_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
|
||||||
|
# If negative, keep-alive probes are disabled.
|
||||||
|
# dial_server_keepalive = 7200
|
||||||
|
|
||||||
# if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables
|
# if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables
|
||||||
# it only works when protocol is tcp
|
# it only works when protocol is tcp
|
||||||
# http_proxy = http://user:passwd@192.168.1.128:8080
|
# http_proxy = http://user:passwd@192.168.1.128:8080
|
||||||
@@ -48,6 +55,12 @@ oidc_audience =
|
|||||||
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
|
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
|
||||||
oidc_token_endpoint_url =
|
oidc_token_endpoint_url =
|
||||||
|
|
||||||
|
# oidc_additional_xxx specifies additional parameters to be sent to the OIDC Token Endpoint.
|
||||||
|
# For example, if you want to specify the "audience" parameter, you can set as follow.
|
||||||
|
# frp will add "audience=<value>" "var1=<value>" to the additional parameters.
|
||||||
|
# oidc_additional_audience = https://dev.auth.com/api/v2/
|
||||||
|
# oidc_additional_var1 = foobar
|
||||||
|
|
||||||
# set admin address for control frpc's action by http api such as reload
|
# set admin address for control frpc's action by http api such as reload
|
||||||
admin_addr = 127.0.0.1
|
admin_addr = 127.0.0.1
|
||||||
admin_port = 7400
|
admin_port = 7400
|
||||||
@@ -60,7 +73,8 @@ admin_pwd = admin
|
|||||||
pool_count = 5
|
pool_count = 5
|
||||||
|
|
||||||
# if tcp stream multiplexing is used, default is true, it must be same with frps
|
# if tcp stream multiplexing is used, default is true, it must be same with frps
|
||||||
tcp_mux = true
|
# tcp_mux = true
|
||||||
|
|
||||||
# specify keep alive interval for tcp mux.
|
# specify keep alive interval for tcp mux.
|
||||||
# only valid if tcp_mux is true.
|
# only valid if tcp_mux is true.
|
||||||
# tcp_mux_keepalive_interval = 60
|
# tcp_mux_keepalive_interval = 60
|
||||||
@@ -117,6 +131,10 @@ udp_packet_size = 1500
|
|||||||
# If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
|
# If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
|
||||||
disable_custom_tls_first_byte = false
|
disable_custom_tls_first_byte = false
|
||||||
|
|
||||||
|
# Enable golang pprof handlers in admin listener.
|
||||||
|
# Admin port must be set first.
|
||||||
|
pprof_enable = false
|
||||||
|
|
||||||
# 'ssh' is the unique proxy name
|
# 'ssh' is the unique proxy name
|
||||||
# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
|
# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
|
||||||
[ssh]
|
[ssh]
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ oidc_audience =
|
|||||||
# By default, this value is false.
|
# By default, this value is false.
|
||||||
oidc_skip_expiry_check = false
|
oidc_skip_expiry_check = false
|
||||||
|
|
||||||
|
|
||||||
# oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer.
|
# oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer.
|
||||||
# By default, this value is false.
|
# By default, this value is false.
|
||||||
oidc_skip_issuer_check = false
|
oidc_skip_issuer_check = false
|
||||||
@@ -120,11 +119,16 @@ tls_only = false
|
|||||||
subdomain_host = frps.com
|
subdomain_host = frps.com
|
||||||
|
|
||||||
# if tcp stream multiplexing is used, default is true
|
# if tcp stream multiplexing is used, default is true
|
||||||
tcp_mux = true
|
# tcp_mux = true
|
||||||
|
|
||||||
# specify keep alive interval for tcp mux.
|
# specify keep alive interval for tcp mux.
|
||||||
# only valid if tcp_mux is true.
|
# only valid if tcp_mux is true.
|
||||||
# tcp_mux_keepalive_interval = 60
|
# tcp_mux_keepalive_interval = 60
|
||||||
|
|
||||||
|
# tcp_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
|
||||||
|
# If negative, keep-alive probes are disabled.
|
||||||
|
# tcp_keepalive = 7200
|
||||||
|
|
||||||
# custom 404 page for HTTP requests
|
# custom 404 page for HTTP requests
|
||||||
# custom_404_page = /path/to/404.html
|
# custom_404_page = /path/to/404.html
|
||||||
|
|
||||||
@@ -133,6 +137,10 @@ tcp_mux = true
|
|||||||
# It affects the udp and sudp proxy.
|
# It affects the udp and sudp proxy.
|
||||||
udp_packet_size = 1500
|
udp_packet_size = 1500
|
||||||
|
|
||||||
|
# Enable golang pprof handlers in dashboard listener.
|
||||||
|
# Dashboard port must be set first
|
||||||
|
pprof_enable = false
|
||||||
|
|
||||||
[plugin.user-manager]
|
[plugin.user-manager]
|
||||||
addr = 127.0.0.1:9000
|
addr = 127.0.0.1:9000
|
||||||
path = /handler
|
path = /handler
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 41 KiB |
BIN
doc/pic/sponsor_workos.png
Normal file
BIN
doc/pic/sponsor_workos.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -70,7 +70,7 @@ The response can look like any of the following:
|
|||||||
|
|
||||||
### Operation
|
### Operation
|
||||||
|
|
||||||
Currently `Login`, `NewProxy`, `Ping`, `NewWorkConn` and `NewUserConn` operations are supported.
|
Currently `Login`, `NewProxy`, `CloseProxy`, `Ping`, `NewWorkConn` and `NewUserConn` operations are supported.
|
||||||
|
|
||||||
#### Login
|
#### Login
|
||||||
|
|
||||||
@@ -136,6 +136,26 @@ Create new proxy
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### CloseProxy
|
||||||
|
|
||||||
|
A previously created proxy is closed.
|
||||||
|
|
||||||
|
Please note that one request will be sent for every proxy that is closed, do **NOT** use this
|
||||||
|
if you have too many proxies bound to a single client, as this may exhaust the server's resources.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"user": {
|
||||||
|
"user": <string>,
|
||||||
|
"metas": map<string>string
|
||||||
|
"run_id": <string>
|
||||||
|
},
|
||||||
|
"proxy_name": <string>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Ping
|
#### Ping
|
||||||
|
|
||||||
Heartbeat from frpc
|
Heartbeat from frpc
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -6,7 +6,7 @@ require (
|
|||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
|
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
|
||||||
github.com/fatedier/golib v0.1.1-0.20220119075718-78e5cf8c00ee
|
github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac
|
||||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
|
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
|
||||||
github.com/go-playground/validator/v10 v10.6.1
|
github.com/go-playground/validator/v10 v10.6.1
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -88,8 +88,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
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 h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
|
||||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
|
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
|
||||||
github.com/fatedier/golib v0.1.1-0.20220119075718-78e5cf8c00ee h1:iS0wlj2uZPxh3pciAf/HTzi88Kqu7DPh1jNKgJaFhtI=
|
github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac h1:td1FJwN/oz8+9GldeEm3YdBX0Husc0FSPywLesZxi4w=
|
||||||
github.com/fatedier/golib v0.1.1-0.20220119075718-78e5cf8c00ee/go.mod h1:fLV0TLwHqrnB/L3jbNl67Gn6PCLggDGHniX1wLrA2Qo=
|
github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac/go.mod h1:fLV0TLwHqrnB/L3jbNl67Gn6PCLggDGHniX1wLrA2Qo=
|
||||||
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 h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
|
||||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
|
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
|||||||
@@ -40,14 +40,20 @@ type OidcClientConfig struct {
|
|||||||
// It will be used to get an OIDC token if AuthenticationMethod == "oidc".
|
// It will be used to get an OIDC token if AuthenticationMethod == "oidc".
|
||||||
// By default, this value is "".
|
// By default, this value is "".
|
||||||
OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"`
|
OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"`
|
||||||
|
|
||||||
|
// OidcAdditionalEndpointParams specifies additional parameters to be sent
|
||||||
|
// this field will be transfer to map[string][]string in OIDC token generator
|
||||||
|
// The field will be set by prefix "oidc_additional_"
|
||||||
|
OidcAdditionalEndpointParams map[string]string `ini:"-" json:"oidc_additional_endpoint_params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultOidcClientConf() OidcClientConfig {
|
func getDefaultOidcClientConf() OidcClientConfig {
|
||||||
return OidcClientConfig{
|
return OidcClientConfig{
|
||||||
OidcClientID: "",
|
OidcClientID: "",
|
||||||
OidcClientSecret: "",
|
OidcClientSecret: "",
|
||||||
OidcAudience: "",
|
OidcAudience: "",
|
||||||
OidcTokenEndpointURL: "",
|
OidcTokenEndpointURL: "",
|
||||||
|
OidcAdditionalEndpointParams: make(map[string]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,11 +94,17 @@ type OidcAuthProvider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewOidcAuthSetter(baseCfg BaseConfig, cfg OidcClientConfig) *OidcAuthProvider {
|
func NewOidcAuthSetter(baseCfg BaseConfig, cfg OidcClientConfig) *OidcAuthProvider {
|
||||||
|
eps := make(map[string][]string)
|
||||||
|
for k, v := range cfg.OidcAdditionalEndpointParams {
|
||||||
|
eps[k] = []string{v}
|
||||||
|
}
|
||||||
|
|
||||||
tokenGenerator := &clientcredentials.Config{
|
tokenGenerator := &clientcredentials.Config{
|
||||||
ClientID: cfg.OidcClientID,
|
ClientID: cfg.OidcClientID,
|
||||||
ClientSecret: cfg.OidcClientSecret,
|
ClientSecret: cfg.OidcClientSecret,
|
||||||
Scopes: []string{cfg.OidcAudience},
|
Scopes: []string{cfg.OidcAudience},
|
||||||
TokenURL: cfg.OidcTokenEndpointURL,
|
TokenURL: cfg.OidcTokenEndpointURL,
|
||||||
|
EndpointParams: eps,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &OidcAuthProvider{
|
return &OidcAuthProvider{
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ type ClientCommonConf struct {
|
|||||||
// ServerPort specifies the port to connect to the server on. By default,
|
// ServerPort specifies the port to connect to the server on. By default,
|
||||||
// this value is 7000.
|
// this value is 7000.
|
||||||
ServerPort int `ini:"server_port" json:"server_port"`
|
ServerPort int `ini:"server_port" json:"server_port"`
|
||||||
|
// The maximum amount of time a dial to server will wait for a connect to complete.
|
||||||
|
DialServerTimeout int64 `ini:"dial_server_timeout" json:"dial_server_timeout"`
|
||||||
|
// DialServerKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
|
||||||
|
// If negative, keep-alive probes are disabled.
|
||||||
|
DialServerKeepAlive int64 `ini:"dial_server_keepalive" json:"dial_server_keepalive"`
|
||||||
// ConnectServerLocalIP specifies the address of the client bind when it connect to server.
|
// ConnectServerLocalIP specifies the address of the client bind when it connect to server.
|
||||||
// By default, this value is empty.
|
// By default, this value is empty.
|
||||||
// this value only use in TCP/Websocket protocol. Not support in KCP protocol.
|
// this value only use in TCP/Websocket protocol. Not support in KCP protocol.
|
||||||
@@ -149,6 +154,9 @@ type ClientCommonConf struct {
|
|||||||
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
|
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
|
||||||
// Include other config files for proxies.
|
// Include other config files for proxies.
|
||||||
IncludeConfigFiles []string `ini:"includes" json:"includes"`
|
IncludeConfigFiles []string `ini:"includes" json:"includes"`
|
||||||
|
// Enable golang pprof handlers in admin listener.
|
||||||
|
// Admin port must be set first.
|
||||||
|
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultClientConf returns a client configuration with default values.
|
// GetDefaultClientConf returns a client configuration with default values.
|
||||||
@@ -157,6 +165,8 @@ func GetDefaultClientConf() ClientCommonConf {
|
|||||||
ClientConfig: auth.GetDefaultClientConf(),
|
ClientConfig: auth.GetDefaultClientConf(),
|
||||||
ServerAddr: "0.0.0.0",
|
ServerAddr: "0.0.0.0",
|
||||||
ServerPort: 7000,
|
ServerPort: 7000,
|
||||||
|
DialServerTimeout: 10,
|
||||||
|
DialServerKeepAlive: 7200,
|
||||||
HTTPProxy: os.Getenv("http_proxy"),
|
HTTPProxy: os.Getenv("http_proxy"),
|
||||||
LogFile: "console",
|
LogFile: "console",
|
||||||
LogWay: "console",
|
LogWay: "console",
|
||||||
@@ -185,6 +195,7 @@ func GetDefaultClientConf() ClientCommonConf {
|
|||||||
Metas: make(map[string]string),
|
Metas: make(map[string]string),
|
||||||
UDPPacketSize: 1500,
|
UDPPacketSize: 1500,
|
||||||
IncludeConfigFiles: make([]string, 0),
|
IncludeConfigFiles: make([]string, 0),
|
||||||
|
PprofEnable: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +269,8 @@ func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
|
common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
|
||||||
|
common.ClientConfig.OidcAdditionalEndpointParams = GetMapWithoutPrefix(s.KeysHash(), "oidc_additional_")
|
||||||
|
|
||||||
return common, nil
|
return common, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -261,6 +261,8 @@ func Test_LoadClientCommonConf(t *testing.T) {
|
|||||||
},
|
},
|
||||||
ServerAddr: "0.0.0.9",
|
ServerAddr: "0.0.0.9",
|
||||||
ServerPort: 7009,
|
ServerPort: 7009,
|
||||||
|
DialServerTimeout: 10,
|
||||||
|
DialServerKeepAlive: 7200,
|
||||||
HTTPProxy: "http://user:passwd@192.168.1.128:8080",
|
HTTPProxy: "http://user:passwd@192.168.1.128:8080",
|
||||||
LogFile: "./frpc.log9",
|
LogFile: "./frpc.log9",
|
||||||
LogWay: "file",
|
LogWay: "file",
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ type ServerCommonConf struct {
|
|||||||
// TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler.
|
// TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler.
|
||||||
// If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux.
|
// If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux.
|
||||||
TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
|
TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
|
||||||
|
// TCPKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
|
||||||
|
// If negative, keep-alive probes are disabled.
|
||||||
|
TCPKeepAlive int64 `ini:"tcp_keepalive" json:"tcp_keepalive"`
|
||||||
// Custom404Page specifies a path to a custom 404 page to display. If this
|
// Custom404Page specifies a path to a custom 404 page to display. If this
|
||||||
// value is "", a default page will be displayed. By default, this value is
|
// value is "", a default page will be displayed. By default, this value is
|
||||||
// "".
|
// "".
|
||||||
@@ -167,6 +170,9 @@ type ServerCommonConf struct {
|
|||||||
// UDPPacketSize specifies the UDP packet size
|
// UDPPacketSize specifies the UDP packet size
|
||||||
// By default, this value is 1500
|
// By default, this value is 1500
|
||||||
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
|
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
|
||||||
|
// Enable golang pprof handlers in dashboard listener.
|
||||||
|
// Dashboard port must be set first.
|
||||||
|
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultServerConf returns a server configuration with reasonable
|
// GetDefaultServerConf returns a server configuration with reasonable
|
||||||
@@ -198,6 +204,7 @@ func GetDefaultServerConf() ServerCommonConf {
|
|||||||
SubDomainHost: "",
|
SubDomainHost: "",
|
||||||
TCPMux: true,
|
TCPMux: true,
|
||||||
TCPMuxKeepaliveInterval: 60,
|
TCPMuxKeepaliveInterval: 60,
|
||||||
|
TCPKeepAlive: 7200,
|
||||||
AllowPorts: make(map[int]struct{}),
|
AllowPorts: make(map[int]struct{}),
|
||||||
MaxPoolCount: 5,
|
MaxPoolCount: 5,
|
||||||
MaxPortsPerClient: 0,
|
MaxPortsPerClient: 0,
|
||||||
@@ -210,6 +217,7 @@ func GetDefaultServerConf() ServerCommonConf {
|
|||||||
Custom404Page: "",
|
Custom404Page: "",
|
||||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
||||||
UDPPacketSize: 1500,
|
UDPPacketSize: 1500,
|
||||||
|
PprofEnable: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ func Test_LoadServerCommonConf(t *testing.T) {
|
|||||||
SubDomainHost: "frps.com",
|
SubDomainHost: "frps.com",
|
||||||
TCPMux: true,
|
TCPMux: true,
|
||||||
TCPMuxKeepaliveInterval: 60,
|
TCPMuxKeepaliveInterval: 60,
|
||||||
|
TCPKeepAlive: 7200,
|
||||||
UDPPacketSize: 1509,
|
UDPPacketSize: 1509,
|
||||||
|
|
||||||
HTTPPlugins: map[string]plugin.HTTPPluginOptions{
|
HTTPPlugins: map[string]plugin.HTTPPluginOptions{
|
||||||
@@ -191,6 +192,7 @@ func Test_LoadServerCommonConf(t *testing.T) {
|
|||||||
DetailedErrorsToClient: true,
|
DetailedErrorsToClient: true,
|
||||||
TCPMux: true,
|
TCPMux: true,
|
||||||
TCPMuxKeepaliveInterval: 60,
|
TCPMuxKeepaliveInterval: 60,
|
||||||
|
TCPKeepAlive: 7200,
|
||||||
AllowPorts: make(map[int]struct{}),
|
AllowPorts: make(map[int]struct{}),
|
||||||
MaxPoolCount: 5,
|
MaxPoolCount: 5,
|
||||||
HeartbeatTimeout: 90,
|
HeartbeatTimeout: 90,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
@@ -26,6 +27,7 @@ import (
|
|||||||
type Manager struct {
|
type Manager struct {
|
||||||
loginPlugins []Plugin
|
loginPlugins []Plugin
|
||||||
newProxyPlugins []Plugin
|
newProxyPlugins []Plugin
|
||||||
|
closeProxyPlugins []Plugin
|
||||||
pingPlugins []Plugin
|
pingPlugins []Plugin
|
||||||
newWorkConnPlugins []Plugin
|
newWorkConnPlugins []Plugin
|
||||||
newUserConnPlugins []Plugin
|
newUserConnPlugins []Plugin
|
||||||
@@ -35,6 +37,7 @@ func NewManager() *Manager {
|
|||||||
return &Manager{
|
return &Manager{
|
||||||
loginPlugins: make([]Plugin, 0),
|
loginPlugins: make([]Plugin, 0),
|
||||||
newProxyPlugins: make([]Plugin, 0),
|
newProxyPlugins: make([]Plugin, 0),
|
||||||
|
closeProxyPlugins: make([]Plugin, 0),
|
||||||
pingPlugins: make([]Plugin, 0),
|
pingPlugins: make([]Plugin, 0),
|
||||||
newWorkConnPlugins: make([]Plugin, 0),
|
newWorkConnPlugins: make([]Plugin, 0),
|
||||||
newUserConnPlugins: make([]Plugin, 0),
|
newUserConnPlugins: make([]Plugin, 0),
|
||||||
@@ -48,6 +51,9 @@ func (m *Manager) Register(p Plugin) {
|
|||||||
if p.IsSupport(OpNewProxy) {
|
if p.IsSupport(OpNewProxy) {
|
||||||
m.newProxyPlugins = append(m.newProxyPlugins, p)
|
m.newProxyPlugins = append(m.newProxyPlugins, p)
|
||||||
}
|
}
|
||||||
|
if p.IsSupport(OpCloseProxy) {
|
||||||
|
m.closeProxyPlugins = append(m.closeProxyPlugins, p)
|
||||||
|
}
|
||||||
if p.IsSupport(OpPing) {
|
if p.IsSupport(OpPing) {
|
||||||
m.pingPlugins = append(m.pingPlugins, p)
|
m.pingPlugins = append(m.pingPlugins, p)
|
||||||
}
|
}
|
||||||
@@ -127,6 +133,32 @@ func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) {
|
|||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) CloseProxy(content *CloseProxyContent) error {
|
||||||
|
if len(m.closeProxyPlugins) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := make([]string, 0)
|
||||||
|
reqid, _ := util.RandID()
|
||||||
|
xl := xlog.New().AppendPrefix("reqid: " + reqid)
|
||||||
|
ctx := xlog.NewContext(context.Background(), xl)
|
||||||
|
ctx = NewReqidContext(ctx, reqid)
|
||||||
|
|
||||||
|
for _, p := range m.closeProxyPlugins {
|
||||||
|
_, _, err := p.Handle(ctx, OpCloseProxy, *content)
|
||||||
|
if err != nil {
|
||||||
|
xl.Warn("send CloseProxy request to plugin [%s] error: %v", p.Name(), err)
|
||||||
|
errs = append(errs, fmt.Sprintf("[%s]: %v", p.Name(), err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return fmt.Errorf("send CloseProxy request to plugin errors: %s", strings.Join(errs, "; "))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) Ping(content *PingContent) (*PingContent, error) {
|
func (m *Manager) Ping(content *PingContent) (*PingContent, error) {
|
||||||
if len(m.pingPlugins) == 0 {
|
if len(m.pingPlugins) == 0 {
|
||||||
return content, nil
|
return content, nil
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const (
|
|||||||
|
|
||||||
OpLogin = "Login"
|
OpLogin = "Login"
|
||||||
OpNewProxy = "NewProxy"
|
OpNewProxy = "NewProxy"
|
||||||
|
OpCloseProxy = "CloseProxy"
|
||||||
OpPing = "Ping"
|
OpPing = "Ping"
|
||||||
OpNewWorkConn = "NewWorkConn"
|
OpNewWorkConn = "NewWorkConn"
|
||||||
OpNewUserConn = "NewUserConn"
|
OpNewUserConn = "NewUserConn"
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ type NewProxyContent struct {
|
|||||||
msg.NewProxy
|
msg.NewProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CloseProxyContent struct {
|
||||||
|
User UserInfo `json:"user"`
|
||||||
|
msg.CloseProxy
|
||||||
|
}
|
||||||
|
|
||||||
type PingContent struct {
|
type PingContent struct {
|
||||||
User UserInfo `json:"user"`
|
User UserInfo `json:"user"`
|
||||||
msg.Ping
|
msg.Ping
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ func NewClientTLSConfig(certPath, keyPath, caPath, serverName string) (*tls.Conf
|
|||||||
base.Certificates = []tls.Certificate{*cert}
|
base.Certificates = []tls.Certificate{*cert}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base.ServerName = serverName
|
||||||
|
|
||||||
if caPath != "" {
|
if caPath != "" {
|
||||||
pool, err := newCertPool(caPath)
|
pool, err := newCertPool(caPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -107,7 +109,6 @@ func NewClientTLSConfig(certPath, keyPath, caPath, serverName string) (*tls.Conf
|
|||||||
}
|
}
|
||||||
|
|
||||||
base.RootCAs = pool
|
base.RootCAs = pool
|
||||||
base.ServerName = serverName
|
|
||||||
base.InsecureSkipVerify = false
|
base.InsecureSkipVerify = false
|
||||||
} else {
|
} else {
|
||||||
base.InsecureSkipVerify = true
|
base.InsecureSkipVerify = true
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -163,7 +164,7 @@ type UDPListener struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ListenUDP(bindAddr string, bindPort int) (l *UDPListener, err error) {
|
func ListenUDP(bindAddr string, bindPort int) (l *UDPListener, err error) {
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
|
udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(bindAddr, strconv.Itoa(bindPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return l, err
|
return l, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package net
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
@@ -52,7 +52,7 @@ func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error) {
|
func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error) {
|
||||||
tcpLn, err := net.Listen("tcp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
|
tcpLn, err := net.Listen("tcp", net.JoinHostPort(bindAddr, strconv.Itoa(bindPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func readHTTPConnectRequest(rd io.Reader) (host string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
host = util.GetHostFromAddr(req.Host)
|
host, _ = util.CanonicalHost(req.Host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,17 +34,6 @@ func OkResponse() *http.Response {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use "CanonicalHost" func to replace all "GetHostFromAddr" func.
|
|
||||||
func GetHostFromAddr(addr string) (host string) {
|
|
||||||
strs := strings.Split(addr, ":")
|
|
||||||
if len(strs) > 1 {
|
|
||||||
host = strs[0]
|
|
||||||
} else {
|
|
||||||
host = addr
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// canonicalHost strips port from host if present and returns the canonicalized
|
// canonicalHost strips port from host if present and returns the canonicalized
|
||||||
// host name.
|
// host name.
|
||||||
func CanonicalHost(host string) (string, error) {
|
func CanonicalHost(host string) (string, error) {
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandID return a rand string used in frp.
|
// RandID return a rand string used in frp.
|
||||||
@@ -52,7 +55,7 @@ func CanonicalAddr(host string, port int) (addr string) {
|
|||||||
if port == 80 || port == 443 {
|
if port == 80 || port == 443 {
|
||||||
addr = host
|
addr = host
|
||||||
} else {
|
} else {
|
||||||
addr = fmt.Sprintf("%s:%d", host, port)
|
addr = net.JoinHostPort(host, strconv.Itoa(port))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -108,3 +111,17 @@ func GenerateResponseErrorString(summary string, err error, detailed bool) strin
|
|||||||
}
|
}
|
||||||
return summary
|
return summary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Duration {
|
||||||
|
min := int64(minRatio * 1000.0)
|
||||||
|
max := int64(maxRatio * 1000.0)
|
||||||
|
var n int64
|
||||||
|
if max <= min {
|
||||||
|
n = min
|
||||||
|
} else {
|
||||||
|
n = mathrand.Int63n(max-min) + min
|
||||||
|
}
|
||||||
|
d := duration * time.Duration(n) / time.Duration(1000)
|
||||||
|
time.Sleep(d)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.39.0"
|
var version string = "0.41.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
|
|||||||
Director: func(req *http.Request) {
|
Director: func(req *http.Request) {
|
||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
url := req.Context().Value(RouteInfoURL).(string)
|
url := req.Context().Value(RouteInfoURL).(string)
|
||||||
oldHost := util.GetHostFromAddr(req.Context().Value(RouteInfoHost).(string))
|
oldHost, _ := util.CanonicalHost(req.Context().Value(RouteInfoHost).(string))
|
||||||
rc := rp.GetRouteConfig(oldHost, url)
|
rc := rp.GetRouteConfig(oldHost, url)
|
||||||
if rc != nil {
|
if rc != nil {
|
||||||
if rc.RewriteHost != "" {
|
if rc.RewriteHost != "" {
|
||||||
@@ -81,7 +81,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
|
|||||||
IdleConnTimeout: 60 * time.Second,
|
IdleConnTimeout: 60 * time.Second,
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
url := ctx.Value(RouteInfoURL).(string)
|
url := ctx.Value(RouteInfoURL).(string)
|
||||||
host := util.GetHostFromAddr(ctx.Value(RouteInfoHost).(string))
|
host, _ := util.CanonicalHost(ctx.Value(RouteInfoHost).(string))
|
||||||
remote := ctx.Value(RouteInfoRemote).(string)
|
remote := ctx.Value(RouteInfoRemote).(string)
|
||||||
return rp.CreateConnection(host, url, remote)
|
return rp.CreateConnection(host, url, remote)
|
||||||
},
|
},
|
||||||
@@ -191,7 +191,7 @@ func (rp *HTTPReverseProxy) getVhost(domain string, location string) (vr *Router
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
domain := util.GetHostFromAddr(req.Host)
|
domain, _ := util.CanonicalHost(req.Host)
|
||||||
location := req.URL.Path
|
location := req.URL.Path
|
||||||
user, passwd, _ := req.BasicAuth()
|
user, passwd, _ := req.BasicAuth()
|
||||||
if !rp.CheckAuth(domain, location, user, passwd) {
|
if !rp.CheckAuth(domain, location, user, passwd) {
|
||||||
|
|||||||
@@ -376,6 +376,20 @@ func (ctl *Control) stoper() {
|
|||||||
pxy.Close()
|
pxy.Close()
|
||||||
ctl.pxyManager.Del(pxy.GetName())
|
ctl.pxyManager.Del(pxy.GetName())
|
||||||
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
||||||
|
|
||||||
|
notifyContent := &plugin.CloseProxyContent{
|
||||||
|
User: plugin.UserInfo{
|
||||||
|
User: ctl.loginMsg.User,
|
||||||
|
Metas: ctl.loginMsg.Metas,
|
||||||
|
RunID: ctl.loginMsg.RunID,
|
||||||
|
},
|
||||||
|
CloseProxy: msg.CloseProxy{
|
||||||
|
ProxyName: pxy.GetName(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
ctl.pluginManager.CloseProxy(notifyContent)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctl.allShutdown.Done()
|
ctl.allShutdown.Done()
|
||||||
@@ -564,5 +578,20 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
|||||||
ctl.mu.Unlock()
|
ctl.mu.Unlock()
|
||||||
|
|
||||||
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
||||||
|
|
||||||
|
notifyContent := &plugin.CloseProxyContent{
|
||||||
|
User: plugin.UserInfo{
|
||||||
|
User: ctl.loginMsg.User,
|
||||||
|
Metas: ctl.loginMsg.Metas,
|
||||||
|
RunID: ctl.loginMsg.RunID,
|
||||||
|
},
|
||||||
|
CloseProxy: msg.CloseProxy{
|
||||||
|
ProxyName: pxy.GetName(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
ctl.pluginManager.CloseProxy(notifyContent)
|
||||||
|
}()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"github.com/fatedier/frp/assets"
|
||||||
@@ -27,8 +28,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
httpServerReadTimeout = 10 * time.Second
|
httpServerReadTimeout = 60 * time.Second
|
||||||
httpServerWriteTimeout = 10 * time.Second
|
httpServerWriteTimeout = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func (svr *Service) RunDashboardServer(address string) (err error) {
|
func (svr *Service) RunDashboardServer(address string) (err error) {
|
||||||
@@ -36,6 +37,15 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
|
|||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.HandleFunc("/healthz", svr.Healthz)
|
router.HandleFunc("/healthz", svr.Healthz)
|
||||||
|
|
||||||
|
// debug
|
||||||
|
if svr.cfg.PprofEnable {
|
||||||
|
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||||
|
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
|
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
|
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||||
|
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
||||||
|
}
|
||||||
|
|
||||||
subRouter := router.NewRoute().Subrouter()
|
subRouter := router.NewRoute().Subrouter()
|
||||||
|
|
||||||
user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd
|
user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
package group
|
package group
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/fatedier/frp/server/ports"
|
"github.com/fatedier/frp/server/ports"
|
||||||
@@ -101,7 +101,7 @@ func (tg *TCPGroup) Listen(proxyName string, group string, groupKey string, addr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tcpLn, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
|
tcpLn, errRet := net.Listen("tcp", net.JoinHostPort(addr, strconv.Itoa(port)))
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package ports
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -134,7 +134,7 @@ func (pm *Manager) Acquire(name string, port int) (realPort int, err error) {
|
|||||||
|
|
||||||
func (pm *Manager) isPortAvailable(port int) bool {
|
func (pm *Manager) isPortAvailable(port int) bool {
|
||||||
if pm.netType == "udp" {
|
if pm.netType == "udp" {
|
||||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pm.bindAddr, port))
|
addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(pm.bindAddr, strconv.Itoa(port)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ func (pm *Manager) isPortAvailable(port int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := net.Listen(pm.netType, fmt.Sprintf("%s:%d", pm.bindAddr, port))
|
l, err := net.Listen(pm.netType, net.JoinHostPort(pm.bindAddr, strconv.Itoa(port)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package proxy
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
)
|
)
|
||||||
@@ -54,7 +55,7 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
|
|||||||
pxy.rc.TCPPortManager.Release(pxy.realPort)
|
pxy.rc.TCPPortManager.Release(pxy.realPort)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
listener, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
|
listener, errRet := net.Listen("tcp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort)))
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
@@ -70,7 +71,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
|
|||||||
|
|
||||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||||
pxy.cfg.RemotePort = pxy.realPort
|
pxy.cfg.RemotePort = pxy.realPort
|
||||||
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
|
addr, errRet := net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort)))
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
// Create tcpmux httpconnect multiplexer.
|
// Create tcpmux httpconnect multiplexer.
|
||||||
if cfg.TCPMuxHTTPConnectPort > 0 {
|
if cfg.TCPMuxHTTPConnectPort > 0 {
|
||||||
var l net.Listener
|
var l net.Listener
|
||||||
l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.TCPMuxHTTPConnectPort))
|
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.TCPMuxHTTPConnectPort))
|
||||||
|
l, err = net.Listen("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Create server listener error, %v", err)
|
err = fmt.Errorf("Create server listener error, %v", err)
|
||||||
return
|
return
|
||||||
@@ -135,7 +136,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
err = fmt.Errorf("Create vhost tcpMuxer error, %v", err)
|
err = fmt.Errorf("Create vhost tcpMuxer error, %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("tcpmux httpconnect multiplexer listen on %s:%d", cfg.ProxyBindAddr, cfg.TCPMuxHTTPConnectPort)
|
log.Info("tcpmux httpconnect multiplexer listen on %s", address)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init all plugins
|
// Init all plugins
|
||||||
@@ -185,6 +186,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
svr.muxer = mux.NewMux(ln)
|
svr.muxer = mux.NewMux(ln)
|
||||||
|
svr.muxer.SetKeepAlive(time.Duration(cfg.TCPKeepAlive) * time.Second)
|
||||||
go svr.muxer.Serve()
|
go svr.muxer.Serve()
|
||||||
ln = svr.muxer.DefaultListener()
|
ln = svr.muxer.DefaultListener()
|
||||||
|
|
||||||
@@ -199,7 +201,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
err = fmt.Errorf("Listen on kcp address udp %s error: %v", address, err)
|
err = fmt.Errorf("Listen on kcp address udp %s error: %v", address, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("frps kcp listen on udp %s:%d", cfg.BindAddr, cfg.KCPBindPort)
|
log.Info("frps kcp listen on udp %s", address)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for accepting connections from client using websocket protocol.
|
// Listen for accepting connections from client using websocket protocol.
|
||||||
@@ -232,7 +234,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
go server.Serve(l)
|
go server.Serve(l)
|
||||||
log.Info("http service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHTTPPort)
|
log.Info("http service listen on %s", address)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create https vhost muxer.
|
// Create https vhost muxer.
|
||||||
@@ -288,7 +290,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
err = fmt.Errorf("Create dashboard web server error, %v", err)
|
err = fmt.Errorf("Create dashboard web server error, %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("Dashboard listen on %s:%d", cfg.DashboardAddr, cfg.DashboardPort)
|
log.Info("Dashboard listen on %s", address)
|
||||||
statsEnable = true
|
statsEnable = true
|
||||||
}
|
}
|
||||||
if statsEnable {
|
if statsEnable {
|
||||||
|
|||||||
@@ -249,4 +249,23 @@ var _ = Describe("[Feature: Client-Server]", func() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("IPv6 bind address", func() {
|
||||||
|
supportProtocols := []string{"tcp", "kcp", "websocket"}
|
||||||
|
for _, protocol := range supportProtocols {
|
||||||
|
tmp := protocol
|
||||||
|
defineClientServerTest("IPv6 bind address: "+strings.ToUpper(tmp), f, &generalTestConfigures{
|
||||||
|
server: fmt.Sprintf(`
|
||||||
|
bind_addr = ::
|
||||||
|
kcp_bind_port = {{ .%s }}
|
||||||
|
protocol = %s
|
||||||
|
`, consts.PortServerName, protocol),
|
||||||
|
client: fmt.Sprintf(`
|
||||||
|
tls_enable = true
|
||||||
|
protocol = %s
|
||||||
|
disable_custom_tls_first_byte = true
|
||||||
|
`, protocol),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// RunProcesses run multiple processes from templates.
|
// RunProcesses run multiple processes from templates.
|
||||||
// The first template should always be frps.
|
// The first template should always be frps.
|
||||||
func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []string) {
|
func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []string) ([]*process.Process, []*process.Process) {
|
||||||
templates := make([]string, 0, len(serverTemplates)+len(clientTemplates))
|
templates := make([]string, 0, len(serverTemplates)+len(clientTemplates))
|
||||||
for _, t := range serverTemplates {
|
for _, t := range serverTemplates {
|
||||||
templates = append(templates, t)
|
templates = append(templates, t)
|
||||||
@@ -28,6 +28,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
|||||||
f.usedPorts[name] = port
|
f.usedPorts[name] = port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentServerProcesses := make([]*process.Process, 0, len(serverTemplates))
|
||||||
for i := range serverTemplates {
|
for i := range serverTemplates {
|
||||||
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i))
|
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i))
|
||||||
err = os.WriteFile(path, []byte(outs[i]), 0666)
|
err = os.WriteFile(path, []byte(outs[i]), 0666)
|
||||||
@@ -37,11 +38,13 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
|||||||
p := process.NewWithEnvs(TestContext.FRPServerPath, []string{"-c", path}, f.osEnvs)
|
p := process.NewWithEnvs(TestContext.FRPServerPath, []string{"-c", path}, f.osEnvs)
|
||||||
f.serverConfPaths = append(f.serverConfPaths, path)
|
f.serverConfPaths = append(f.serverConfPaths, path)
|
||||||
f.serverProcesses = append(f.serverProcesses, p)
|
f.serverProcesses = append(f.serverProcesses, p)
|
||||||
|
currentServerProcesses = append(currentServerProcesses, p)
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
currentClientProcesses := make([]*process.Process, 0, len(clientTemplates))
|
||||||
for i := range clientTemplates {
|
for i := range clientTemplates {
|
||||||
index := i + len(serverTemplates)
|
index := i + len(serverTemplates)
|
||||||
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i))
|
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i))
|
||||||
@@ -52,11 +55,14 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
|||||||
p := process.NewWithEnvs(TestContext.FRPClientPath, []string{"-c", path}, f.osEnvs)
|
p := process.NewWithEnvs(TestContext.FRPClientPath, []string{"-c", path}, f.osEnvs)
|
||||||
f.clientConfPaths = append(f.clientConfPaths, path)
|
f.clientConfPaths = append(f.clientConfPaths, path)
|
||||||
f.clientProcesses = append(f.clientProcesses, p)
|
f.clientProcesses = append(f.clientProcesses, p)
|
||||||
|
currentClientProcesses = append(currentClientProcesses, p)
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
}
|
}
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
return currentServerProcesses, currentClientProcesses
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Framework) RunFrps(args ...string) (*process.Process, string, error) {
|
func (f *Framework) RunFrps(args ...string) (*process.Process, string, error) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package httpserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -97,7 +96,7 @@ func (s *Server) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) initListener() (err error) {
|
func (s *Server) initListener() (err error) {
|
||||||
s.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.bindAddr, s.bindPort))
|
s.l, err = net.Listen("tcp", net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
libnet "github.com/fatedier/frp/pkg/util/net"
|
libnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/rpc"
|
"github.com/fatedier/frp/test/e2e/pkg/rpc"
|
||||||
@@ -99,7 +100,7 @@ func (s *Server) Close() error {
|
|||||||
func (s *Server) initListener() (err error) {
|
func (s *Server) initListener() (err error) {
|
||||||
switch s.netType {
|
switch s.netType {
|
||||||
case TCP:
|
case TCP:
|
||||||
s.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.bindAddr, s.bindPort))
|
s.l, err = net.Listen("tcp", net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort)))
|
||||||
case UDP:
|
case UDP:
|
||||||
s.l, err = libnet.ListenUDP(s.bindAddr, s.bindPort)
|
s.l, err = libnet.ListenUDP(s.bindAddr, s.bindPort)
|
||||||
case Unix:
|
case Unix:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package port
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
@@ -57,7 +58,7 @@ func (pa *Allocator) GetByName(portName string) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
l, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Maybe not controlled by us, mark it used.
|
// Maybe not controlled by us, mark it used.
|
||||||
pa.used.Insert(port)
|
pa.used.Insert(port)
|
||||||
@@ -65,7 +66,7 @@ func (pa *Allocator) GetByName(portName string) int {
|
|||||||
}
|
}
|
||||||
l.Close()
|
l.Close()
|
||||||
|
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", port))
|
udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,56 @@ var _ = Describe("[Feature: Server-Plugins]", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("CloseProxy", func() {
|
||||||
|
newFunc := func() *plugin.Request {
|
||||||
|
var r plugin.Request
|
||||||
|
r.Content = &plugin.CloseProxyContent{}
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
It("Validate Info", func() {
|
||||||
|
localPort := f.AllocPort()
|
||||||
|
var recordProxyName string
|
||||||
|
handler := func(req *plugin.Request) *plugin.Response {
|
||||||
|
var ret plugin.Response
|
||||||
|
content := req.Content.(*plugin.CloseProxyContent)
|
||||||
|
recordProxyName = content.ProxyName
|
||||||
|
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 = CloseProxy
|
||||||
|
`, localPort)
|
||||||
|
clientConf := consts.DefaultClientConfig
|
||||||
|
|
||||||
|
remotePort := f.AllocPort()
|
||||||
|
clientConf += fmt.Sprintf(`
|
||||||
|
[tcp]
|
||||||
|
type = tcp
|
||||||
|
local_port = {{ .%s }}
|
||||||
|
remote_port = %d
|
||||||
|
`, framework.TCPEchoServerPort, remotePort)
|
||||||
|
|
||||||
|
_, clients := f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
|
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||||
|
|
||||||
|
for _, c := range clients {
|
||||||
|
c.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
framework.ExpectEqual(recordProxyName, "tcp")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Describe("Ping", func() {
|
Describe("Ping", func() {
|
||||||
newFunc := func() *plugin.Request {
|
newFunc := func() *plugin.Request {
|
||||||
var r plugin.Request
|
var r plugin.Request
|
||||||
|
|||||||
Reference in New Issue
Block a user