frp/client/proxy/proxy_wrapper.go

251 lines
5.7 KiB
Go
Raw Normal View History

2018-12-09 22:06:22 +08:00
package proxy
2018-12-07 17:05:36 +08:00
import (
2019-10-12 20:13:12 +08:00
"context"
2018-12-07 17:05:36 +08:00
"fmt"
2019-10-12 20:13:12 +08:00
"net"
2018-12-07 17:05:36 +08:00
"sync"
"sync/atomic"
"time"
2018-12-09 22:06:22 +08:00
"github.com/fatedier/frp/client/event"
"github.com/fatedier/frp/client/health"
2018-12-07 17:05:36 +08:00
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg"
2019-10-12 20:13:12 +08:00
"github.com/fatedier/frp/utils/xlog"
2018-12-09 21:56:46 +08:00
"github.com/fatedier/golib/errors"
2018-12-07 17:05:36 +08:00
)
const (
2020-05-24 17:48:37 +08:00
ProxyPhaseNew = "new"
ProxyPhaseWaitStart = "wait start"
ProxyPhaseStartErr = "start error"
ProxyPhaseRunning = "running"
ProxyPhaseCheckFailed = "check failed"
ProxyPhaseClosed = "closed"
2018-12-07 17:05:36 +08:00
)
var (
statusCheckInterval time.Duration = 3 * time.Second
waitResponseTimeout = 20 * time.Second
startErrTimeout = 30 * time.Second
)
2020-05-24 17:48:37 +08:00
type WorkingStatus struct {
Name string `json:"name"`
Type string `json:"type"`
Phase string `json:"status"`
Err string `json:"err"`
Cfg config.ProxyConf `json:"cfg"`
2018-12-07 17:05:36 +08:00
// Got from server.
RemoteAddr string `json:"remote_addr"`
}
2020-05-24 17:48:37 +08:00
type Wrapper struct {
WorkingStatus
2018-12-07 17:05:36 +08:00
// underlying proxy
pxy Proxy
// if ProxyConf has healcheck config
// monitor will watch if it is alive
2020-05-24 17:48:37 +08:00
monitor *health.Monitor
2018-12-07 17:05:36 +08:00
// event handler
2020-05-24 17:48:37 +08:00
handler event.Handler
2018-12-07 17:05:36 +08:00
health uint32
lastSendStartMsg time.Time
lastStartErr time.Time
closeCh chan struct{}
2018-12-09 21:56:46 +08:00
healthNotifyCh chan struct{}
2018-12-07 17:05:36 +08:00
mu sync.RWMutex
2019-10-12 20:13:12 +08:00
xl *xlog.Logger
ctx context.Context
2018-12-07 17:05:36 +08:00
}
2020-05-24 17:48:37 +08:00
func NewWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.Handler, serverUDPPort int) *Wrapper {
2018-12-07 17:05:36 +08:00
baseInfo := cfg.GetBaseInfo()
2019-10-12 20:13:12 +08:00
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.ProxyName)
2020-05-24 17:48:37 +08:00
pw := &Wrapper{
WorkingStatus: WorkingStatus{
Name: baseInfo.ProxyName,
Type: baseInfo.ProxyType,
Phase: ProxyPhaseNew,
Cfg: cfg,
2018-12-07 17:05:36 +08:00
},
2018-12-09 21:56:46 +08:00
closeCh: make(chan struct{}),
healthNotifyCh: make(chan struct{}),
handler: eventHandler,
2019-10-12 20:13:12 +08:00
xl: xl,
ctx: xlog.NewContext(ctx, xl),
2018-12-07 17:05:36 +08:00
}
if baseInfo.HealthCheckType != "" {
pw.health = 1 // means failed
2020-05-24 17:48:37 +08:00
pw.monitor = health.NewMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
2018-12-07 17:05:36 +08:00
baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr,
2020-05-24 17:48:37 +08:00
baseInfo.HealthCheckURL, pw.statusNormalCallback, pw.statusFailedCallback)
2019-10-12 20:13:12 +08:00
xl.Trace("enable health check monitor")
2018-12-07 17:05:36 +08:00
}
2019-10-12 20:13:12 +08:00
pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, serverUDPPort)
2018-12-07 17:05:36 +08:00
return pw
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) SetRunningStatus(remoteAddr string, respErr string) error {
2018-12-07 17:05:36 +08:00
pw.mu.Lock()
defer pw.mu.Unlock()
2020-05-24 17:48:37 +08:00
if pw.Phase != ProxyPhaseWaitStart {
2018-12-07 17:05:36 +08:00
return fmt.Errorf("status not wait start, ignore start message")
}
pw.RemoteAddr = remoteAddr
if respErr != "" {
2020-05-24 17:48:37 +08:00
pw.Phase = ProxyPhaseStartErr
2018-12-07 17:05:36 +08:00
pw.Err = respErr
pw.lastStartErr = time.Now()
return fmt.Errorf(pw.Err)
}
if err := pw.pxy.Run(); err != nil {
pw.close()
2020-05-24 17:48:37 +08:00
pw.Phase = ProxyPhaseStartErr
2018-12-07 17:05:36 +08:00
pw.Err = err.Error()
pw.lastStartErr = time.Now()
return err
}
2020-05-24 17:48:37 +08:00
pw.Phase = ProxyPhaseRunning
2018-12-07 17:05:36 +08:00
pw.Err = ""
return nil
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) Start() {
2018-12-07 17:05:36 +08:00
go pw.checkWorker()
if pw.monitor != nil {
go pw.monitor.Start()
}
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) Stop() {
2018-12-07 17:05:36 +08:00
pw.mu.Lock()
defer pw.mu.Unlock()
2018-12-09 21:56:46 +08:00
close(pw.closeCh)
close(pw.healthNotifyCh)
2018-12-07 17:05:36 +08:00
pw.pxy.Close()
if pw.monitor != nil {
pw.monitor.Stop()
}
2020-05-24 17:48:37 +08:00
pw.Phase = ProxyPhaseClosed
pw.close()
}
2018-12-07 17:05:36 +08:00
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) close() {
2018-12-09 22:06:22 +08:00
pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{
2018-12-07 17:05:36 +08:00
CloseProxyMsg: &msg.CloseProxy{
ProxyName: pw.Name,
},
})
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) checkWorker() {
2019-10-12 20:13:12 +08:00
xl := pw.xl
2018-12-09 21:56:46 +08:00
if pw.monitor != nil {
// let monitor do check request first
time.Sleep(500 * time.Millisecond)
}
2018-12-07 17:05:36 +08:00
for {
// check proxy status
now := time.Now()
if atomic.LoadUint32(&pw.health) == 0 {
pw.mu.Lock()
2020-05-24 17:48:37 +08:00
if pw.Phase == ProxyPhaseNew ||
pw.Phase == ProxyPhaseCheckFailed ||
(pw.Phase == ProxyPhaseWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) ||
(pw.Phase == ProxyPhaseStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) {
2018-12-07 17:05:36 +08:00
2020-05-24 17:48:37 +08:00
xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseWaitStart)
pw.Phase = ProxyPhaseWaitStart
2018-12-07 17:05:36 +08:00
var newProxyMsg msg.NewProxy
pw.Cfg.MarshalToMsg(&newProxyMsg)
pw.lastSendStartMsg = now
2018-12-09 22:06:22 +08:00
pw.handler(event.EvStartProxy, &event.StartProxyPayload{
2018-12-07 17:05:36 +08:00
NewProxyMsg: &newProxyMsg,
})
}
pw.mu.Unlock()
} else {
pw.mu.Lock()
2020-05-24 17:48:37 +08:00
if pw.Phase == ProxyPhaseRunning || pw.Phase == ProxyPhaseWaitStart {
pw.close()
2020-05-24 17:48:37 +08:00
xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseCheckFailed)
pw.Phase = ProxyPhaseCheckFailed
2018-12-07 17:05:36 +08:00
}
pw.mu.Unlock()
}
select {
case <-pw.closeCh:
return
case <-time.After(statusCheckInterval):
2018-12-09 21:56:46 +08:00
case <-pw.healthNotifyCh:
2018-12-07 17:05:36 +08:00
}
}
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) statusNormalCallback() {
2019-10-12 20:13:12 +08:00
xl := pw.xl
2018-12-07 17:05:36 +08:00
atomic.StoreUint32(&pw.health, 0)
2018-12-09 21:56:46 +08:00
errors.PanicToError(func() {
select {
case pw.healthNotifyCh <- struct{}{}:
default:
}
})
2019-10-12 20:13:12 +08:00
xl.Info("health check success")
2018-12-07 17:05:36 +08:00
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) statusFailedCallback() {
2019-10-12 20:13:12 +08:00
xl := pw.xl
2018-12-07 17:05:36 +08:00
atomic.StoreUint32(&pw.health, 1)
2018-12-09 21:56:46 +08:00
errors.PanicToError(func() {
select {
case pw.healthNotifyCh <- struct{}{}:
default:
}
})
2019-10-12 20:13:12 +08:00
xl.Info("health check failed")
2018-12-07 17:05:36 +08:00
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
2019-10-12 20:13:12 +08:00
xl := pw.xl
2018-12-07 17:05:36 +08:00
pw.mu.RLock()
pxy := pw.pxy
pw.mu.RUnlock()
if pxy != nil {
2019-10-12 20:13:12 +08:00
xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
2019-03-29 19:01:18 +08:00
go pxy.InWorkConn(workConn, m)
2018-12-07 17:05:36 +08:00
} else {
workConn.Close()
}
}
2020-05-24 17:48:37 +08:00
func (pw *Wrapper) GetStatus() *WorkingStatus {
2018-12-07 17:05:36 +08:00
pw.mu.RLock()
defer pw.mu.RUnlock()
2020-05-24 17:48:37 +08:00
ps := &WorkingStatus{
2018-12-07 17:05:36 +08:00
Name: pw.Name,
Type: pw.Type,
2020-05-24 17:48:37 +08:00
Phase: pw.Phase,
2018-12-07 17:05:36 +08:00
Err: pw.Err,
Cfg: pw.Cfg,
RemoteAddr: pw.RemoteAddr,
}
return ps
}