From 0b0611a0dc246f5952548594fcb3d043f7c5fce0 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Tue, 21 Apr 2020 00:50:12 +0800 Subject: [PATCH] strategy: added new option checkfailedonly #138 --- conf.go | 1 + go.mod | 4 +- go.sum | 8 +-- strategy/strategy.go | 123 +++++++++++++++++++++++++++---------------- 4 files changed, 84 insertions(+), 52 deletions(-) diff --git a/conf.go b/conf.go index f03f9e0..d013795 100644 --- a/conf.go +++ b/conf.go @@ -43,6 +43,7 @@ func confInit() { flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkinterval", 30, "proxy check interval(seconds)") flag.IntVar(&conf.StrategyConfig.CheckTimeout, "checktimeout", 10, "proxy check timeout(seconds)") + flag.BoolVar(&conf.StrategyConfig.CheckFailedOnly, "checkfailedonly", false, "check failed fowarder only") flag.IntVar(&conf.StrategyConfig.MaxFailures, "maxfailures", 3, "max failures to change forwarder status to disabled") flag.StringVar(&conf.StrategyConfig.IntFace, "interface", "", "source ip or source interface") diff --git a/go.mod b/go.mod index d1824c5..6c8870d 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,9 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/tjfoc/gmsm v1.3.0 // indirect github.com/xtaci/kcp-go/v5 v5.5.12 - golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 + golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect - golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect + golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect ) // Replace dependency modules with local developing copy diff --git a/go.sum b/go.sum index 117de00..1ff026e 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,8 @@ golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 h1:IhZPbxNd1UjBCaD5AfpSSbJTRlp+ZSuyuH5uoksNS04= +golang.org/x/crypto v0.0.0-20200420104511-884d27f42877/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -73,8 +73,8 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/strategy/strategy.go b/strategy/strategy.go index 06f5d43..dceea12 100644 --- a/strategy/strategy.go +++ b/strategy/strategy.go @@ -17,12 +17,13 @@ import ( // Config is strategy config struct. type Config struct { - Strategy string - CheckWebSite string - CheckInterval int - CheckTimeout int - MaxFailures int - IntFace string + Strategy string + CheckWebSite string + CheckInterval int + CheckTimeout int + CheckFailedOnly bool + MaxFailures int + IntFace string } // forwarder slice orderd by priority @@ -185,66 +186,96 @@ func (p *Proxy) Check() { // no need to check when there's only 1 forwarder if len(p.fwdrs) > 1 { for i := 0; i < len(p.fwdrs); i++ { - go p.check(i) + go p.check(p.fwdrs[i]) } } } -func (p *Proxy) check(i int) { - f := p.fwdrs[i] - retry := 1 +func (p *Proxy) check(f *Forwarder) { + wait := uint8(0) buf := make([]byte, 4) + intval := time.Duration(p.config.CheckInterval) * time.Second for { - time.Sleep(time.Duration(p.config.CheckInterval) * time.Second * time.Duration(retry>>1)) + time.Sleep(intval * time.Duration(wait)) // check all forwarders at least one time - if retry > 1 && f.Priority() < p.Priority() { + if wait > 0 && (f.Priority() < p.Priority()) { continue } - retry <<= 1 - if retry > 16 { - retry = 16 - } - - startTime := time.Now() - rc, err := f.Dial("tcp", p.config.CheckWebSite) - if err != nil { - f.Disable() - log.F("[check] %s(%d) -> %s, DISABLED. error in dial: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, err) + if f.Enabled() && p.config.CheckFailedOnly { continue } - rc.Write([]byte("GET / HTTP/1.0\r\n\r\n")) - - _, err = io.ReadFull(rc, buf) - if err != nil { - f.Disable() - log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, err) - } else if bytes.Equal([]byte("HTTP"), buf) { - - readTime := time.Since(startTime) - f.SetLatency(int64(readTime)) - - if readTime > time.Duration(p.config.CheckTimeout)*time.Second { - f.Disable() - log.F("[check] %s(%d) -> %s, DISABLED. check timeout: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, readTime) - } else { - retry = 2 - f.Enable() - log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, readTime) - } - - } else { - f.Disable() - log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, buf) + if checkWebSite(f, p.config.CheckWebSite, time.Duration(p.config.CheckTimeout)*time.Second, buf) { + wait = 1 + continue } - rc.Close() + if wait == 0 { + wait = 1 + } + + wait *= 2 + if wait > 16 { + wait = 16 + } } } +func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []byte) bool { + startTime := time.Now() + + rc, err := fwdr.Dial("tcp", website) + if err != nil { + fwdr.Disable() + log.F("[check] %s(%d) -> %s, DISABLED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), + website, err) + return false + } + defer rc.Close() + + _, err = rc.Write([]byte("GET / HTTP/1.0\r\n\r\n")) + if err != nil { + fwdr.Disable() + log.F("[check] %s(%d) -> %s, DISABLED. error in write: %s", fwdr.Addr(), fwdr.Priority(), + website, err) + return false + } + + _, err = io.ReadFull(rc, buf) + if err != nil { + fwdr.Disable() + log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", fwdr.Addr(), fwdr.Priority(), + website, err) + return false + } + + if !bytes.Equal([]byte("HTTP"), buf) { + fwdr.Disable() + log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", fwdr.Addr(), fwdr.Priority(), + website, buf) + return false + } + + readTime := time.Since(startTime) + fwdr.SetLatency(int64(readTime)) + + if readTime > timeout { + fwdr.Disable() + log.F("[check] %s(%d) -> %s, DISABLED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), + website, readTime) + return false + } + + fwdr.Enable() + log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", fwdr.Addr(), fwdr.Priority(), + website, readTime) + + return true +} + // Round Robin func (p *Proxy) scheduleRR(dstAddr string) *Forwarder { return p.avail[atomic.AddUint32(&p.index, 1)%uint32(len(p.avail))]