2018-08-11 11:46:10 +08:00
|
|
|
package strategy
|
2017-07-13 21:55:41 +08:00
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"net"
|
2018-08-12 18:50:44 +08:00
|
|
|
"sort"
|
2017-08-23 16:35:39 +08:00
|
|
|
"strings"
|
2018-01-12 20:11:21 +08:00
|
|
|
"time"
|
2018-06-26 16:15:48 +08:00
|
|
|
|
|
|
|
"github.com/nadoo/glider/common/log"
|
|
|
|
"github.com/nadoo/glider/proxy"
|
2017-08-23 16:35:39 +08:00
|
|
|
)
|
|
|
|
|
2018-08-11 11:46:10 +08:00
|
|
|
// Config of strategy
|
|
|
|
type Config struct {
|
2018-08-10 19:03:30 +08:00
|
|
|
Strategy string
|
|
|
|
CheckWebSite string
|
|
|
|
CheckInterval int
|
2018-08-12 21:40:22 +08:00
|
|
|
MaxFailures int
|
2018-08-10 19:03:30 +08:00
|
|
|
}
|
|
|
|
|
2018-08-11 11:46:10 +08:00
|
|
|
// NewDialer returns a new strategy dialer
|
|
|
|
func NewDialer(s []string, c *Config) proxy.Dialer {
|
2018-08-10 19:03:30 +08:00
|
|
|
// global forwarders in xx.conf
|
|
|
|
var fwdrs []*proxy.Forwarder
|
|
|
|
for _, chain := range s {
|
|
|
|
fwdr, err := proxy.ForwarderFromURL(chain)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2018-08-12 21:40:22 +08:00
|
|
|
fwdr.MaxFailures = uint32(c.MaxFailures)
|
2018-08-10 19:03:30 +08:00
|
|
|
fwdrs = append(fwdrs, fwdr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(fwdrs) == 0 {
|
2018-06-26 16:15:48 +08:00
|
|
|
return proxy.Direct
|
2018-01-13 20:08:49 +08:00
|
|
|
}
|
|
|
|
|
2018-08-10 19:03:30 +08:00
|
|
|
if len(fwdrs) == 1 {
|
|
|
|
return fwdrs[0]
|
2018-01-13 20:08:49 +08:00
|
|
|
}
|
|
|
|
|
2018-06-26 16:15:48 +08:00
|
|
|
var dialer proxy.Dialer
|
2018-08-10 19:03:30 +08:00
|
|
|
switch c.Strategy {
|
2018-01-13 20:08:49 +08:00
|
|
|
case "rr":
|
2018-08-10 19:03:30 +08:00
|
|
|
dialer = newRRDialer(fwdrs, c.CheckWebSite, c.CheckInterval)
|
2018-06-26 16:15:48 +08:00
|
|
|
log.F("forward to remote servers in round robin mode.")
|
2018-01-13 20:08:49 +08:00
|
|
|
case "ha":
|
2018-08-10 19:03:30 +08:00
|
|
|
dialer = newHADialer(fwdrs, c.CheckWebSite, c.CheckInterval)
|
2018-06-26 16:15:48 +08:00
|
|
|
log.F("forward to remote servers in high availability mode.")
|
2018-01-13 20:08:49 +08:00
|
|
|
default:
|
2018-08-10 19:03:30 +08:00
|
|
|
log.F("not supported forward mode '%s', just use the first forward server.", c.Strategy)
|
|
|
|
dialer = fwdrs[0]
|
2017-07-29 21:31:01 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
return dialer
|
2017-07-29 21:31:01 +08:00
|
|
|
}
|
|
|
|
|
2018-08-12 18:50:44 +08:00
|
|
|
type forwarderSlice []*proxy.Forwarder
|
|
|
|
|
|
|
|
func (p forwarderSlice) Len() int { return len(p) }
|
|
|
|
func (p forwarderSlice) Less(i, j int) bool { return p[i].Priority > p[j].Priority }
|
|
|
|
func (p forwarderSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
2017-08-23 16:35:39 +08:00
|
|
|
|
2018-08-12 18:50:44 +08:00
|
|
|
// rrDialer is a rr dialer
|
|
|
|
type rrDialer struct {
|
|
|
|
fwdrs forwarderSlice
|
|
|
|
idx int
|
|
|
|
priority int
|
2017-08-23 16:35:39 +08:00
|
|
|
|
|
|
|
// for checking
|
|
|
|
website string
|
2018-01-13 20:08:49 +08:00
|
|
|
interval int
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2017-09-10 20:33:35 +08:00
|
|
|
// newRRDialer returns a new rrDialer
|
2018-08-12 18:50:44 +08:00
|
|
|
func newRRDialer(fs []*proxy.Forwarder, website string, interval int) *rrDialer {
|
|
|
|
rr := &rrDialer{fwdrs: fs}
|
2017-08-23 16:35:39 +08:00
|
|
|
rr.website = website
|
2018-01-13 20:08:49 +08:00
|
|
|
rr.interval = interval
|
2017-08-23 16:35:39 +08:00
|
|
|
|
2018-08-12 18:50:44 +08:00
|
|
|
sort.Sort(rr.fwdrs)
|
|
|
|
rr.priority = rr.fwdrs[0].Priority
|
|
|
|
|
|
|
|
for k := range rr.fwdrs {
|
2017-08-23 16:35:39 +08:00
|
|
|
go rr.checkDialer(k)
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
return rr
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
func (rr *rrDialer) Addr() string { return "STRATEGY" }
|
|
|
|
func (rr *rrDialer) Dial(network, addr string) (net.Conn, error) {
|
2017-08-23 17:45:57 +08:00
|
|
|
return rr.NextDialer(addr).Dial(network, addr)
|
2017-08-23 16:35:39 +08:00
|
|
|
}
|
2017-07-13 21:55:41 +08:00
|
|
|
|
2018-01-17 00:26:38 +08:00
|
|
|
func (rr *rrDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
|
|
|
return rr.NextDialer(addr).DialUDP(network, addr)
|
|
|
|
}
|
|
|
|
|
2018-08-10 19:03:30 +08:00
|
|
|
func (rr *rrDialer) nextDialer(dstAddr string) *proxy.Forwarder {
|
|
|
|
n := len(rr.fwdrs)
|
2017-07-13 21:55:41 +08:00
|
|
|
if n == 1 {
|
2017-08-23 16:35:39 +08:00
|
|
|
rr.idx = 0
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2018-08-12 18:50:44 +08:00
|
|
|
for _, fwder := range rr.fwdrs {
|
|
|
|
if fwder.Enabled() {
|
|
|
|
rr.priority = fwder.Priority
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rr.fwdrs[rr.idx].Priority < rr.priority {
|
|
|
|
rr.idx = 0
|
|
|
|
}
|
|
|
|
|
2017-07-16 12:16:50 +08:00
|
|
|
found := false
|
|
|
|
for i := 0; i < n; i++ {
|
2017-08-23 16:35:39 +08:00
|
|
|
rr.idx = (rr.idx + 1) % n
|
2018-08-12 18:50:44 +08:00
|
|
|
if rr.fwdrs[rr.idx].Enabled() &&
|
|
|
|
rr.fwdrs[rr.idx].Priority >= rr.priority {
|
2017-07-16 12:16:50 +08:00
|
|
|
found = true
|
2018-08-12 18:50:44 +08:00
|
|
|
rr.priority = rr.fwdrs[rr.idx].Priority
|
2017-07-16 12:16:50 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !found {
|
2018-06-26 16:15:48 +08:00
|
|
|
log.F("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.")
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2018-08-10 19:03:30 +08:00
|
|
|
return rr.fwdrs[rr.idx]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer {
|
|
|
|
return rr.nextDialer(dstAddr)
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
// Check dialer
|
|
|
|
func (rr *rrDialer) checkDialer(idx int) {
|
|
|
|
retry := 1
|
|
|
|
buf := make([]byte, 4)
|
|
|
|
|
|
|
|
if strings.IndexByte(rr.website, ':') == -1 {
|
|
|
|
rr.website = rr.website + ":80"
|
|
|
|
}
|
|
|
|
|
2018-08-10 19:03:30 +08:00
|
|
|
d := rr.fwdrs[idx]
|
2017-08-23 16:35:39 +08:00
|
|
|
|
|
|
|
for {
|
2018-01-13 20:08:49 +08:00
|
|
|
time.Sleep(time.Duration(rr.interval) * time.Second * time.Duration(retry>>1))
|
2017-08-23 16:35:39 +08:00
|
|
|
retry <<= 1
|
2017-07-30 01:54:19 +08:00
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
if retry > 16 {
|
|
|
|
retry = 16
|
|
|
|
}
|
|
|
|
|
|
|
|
startTime := time.Now()
|
|
|
|
c, err := d.Dial("tcp", rr.website)
|
|
|
|
if err != nil {
|
2018-08-12 18:50:44 +08:00
|
|
|
rr.fwdrs[idx].Disable()
|
2018-06-28 23:20:04 +08:00
|
|
|
log.F("[check] %s -> %s, set to DISABLED. error in dial: %s", d.Addr(), rr.website, err)
|
2017-08-23 16:35:39 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-01-29 23:33:53 +08:00
|
|
|
c.Write([]byte("GET / HTTP/1.0\r\n\r\n"))
|
2017-08-23 16:35:39 +08:00
|
|
|
|
|
|
|
_, err = io.ReadFull(c, buf)
|
|
|
|
if err != nil {
|
2018-08-12 18:50:44 +08:00
|
|
|
rr.fwdrs[idx].Disable()
|
2018-06-28 23:20:04 +08:00
|
|
|
log.F("[check] %s -> %s, set to DISABLED. error in read: %s", d.Addr(), rr.website, err)
|
2017-08-23 16:35:39 +08:00
|
|
|
} else if bytes.Equal([]byte("HTTP"), buf) {
|
2018-08-12 18:50:44 +08:00
|
|
|
rr.fwdrs[idx].Enable()
|
2017-08-23 16:35:39 +08:00
|
|
|
retry = 2
|
|
|
|
dialTime := time.Since(startTime)
|
2018-06-28 23:20:04 +08:00
|
|
|
log.F("[check] %s -> %s, set to ENABLED. connect time: %s", d.Addr(), rr.website, dialTime.String())
|
2017-08-23 16:35:39 +08:00
|
|
|
} else {
|
2018-08-12 18:50:44 +08:00
|
|
|
rr.fwdrs[idx].Disable()
|
2018-06-28 23:20:04 +08:00
|
|
|
log.F("[check] %s -> %s, set to DISABLED. server response: %s", d.Addr(), rr.website, buf)
|
2017-08-23 16:35:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
c.Close()
|
|
|
|
}
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// high availability proxy
|
2017-08-23 16:35:39 +08:00
|
|
|
type haDialer struct {
|
|
|
|
*rrDialer
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
// newHADialer .
|
2018-08-10 19:03:30 +08:00
|
|
|
func newHADialer(dialers []*proxy.Forwarder, webhost string, duration int) proxy.Dialer {
|
2017-08-23 16:35:39 +08:00
|
|
|
return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)}
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 16:35:39 +08:00
|
|
|
func (ha *haDialer) Dial(network, addr string) (net.Conn, error) {
|
2018-08-10 19:03:30 +08:00
|
|
|
d := ha.fwdrs[ha.idx]
|
2018-08-12 18:50:44 +08:00
|
|
|
if !d.Enabled() {
|
2018-08-10 19:03:30 +08:00
|
|
|
d = ha.nextDialer(addr)
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
2017-08-23 16:35:39 +08:00
|
|
|
return d.Dial(network, addr)
|
2017-07-31 19:29:43 +08:00
|
|
|
}
|
2018-01-17 00:26:38 +08:00
|
|
|
|
|
|
|
func (ha *haDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
2018-08-10 19:03:30 +08:00
|
|
|
d := ha.fwdrs[ha.idx]
|
2018-08-12 18:50:44 +08:00
|
|
|
if !d.Enabled() {
|
2018-08-10 19:03:30 +08:00
|
|
|
d = ha.nextDialer(addr)
|
2018-01-17 00:26:38 +08:00
|
|
|
}
|
|
|
|
return d.DialUDP(network, addr)
|
|
|
|
}
|