diff --git a/README.md b/README.md index 768cbbf4..12503c95 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,7 @@ proxy-groups: - ss1 - ss2 - vmess1 + # tolerance: 150 url: 'http://www.gstatic.com/generate_204' interval: 300 diff --git a/adapters/outboundgroup/parser.go b/adapters/outboundgroup/parser.go index 7eac7e9a..0a33b94e 100644 --- a/adapters/outboundgroup/parser.go +++ b/adapters/outboundgroup/parser.go @@ -101,7 +101,8 @@ func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy, var group C.ProxyAdapter switch groupOption.Type { case "url-test": - group = NewURLTest(groupName, providers) + opts := parseURLTestOption(config) + group = NewURLTest(groupName, providers, opts...) case "select": group = NewSelector(groupName, providers) case "fallback": diff --git a/adapters/outboundgroup/urltest.go b/adapters/outboundgroup/urltest.go index ab1e2db3..750d809b 100644 --- a/adapters/outboundgroup/urltest.go +++ b/adapters/outboundgroup/urltest.go @@ -11,8 +11,19 @@ import ( C "github.com/Dreamacro/clash/constant" ) +type urlTestOption func(*URLTest) + +func urlTestWithTolerance(tolerance uint16) urlTestOption { + return func(u *URLTest) { + u.tolerance = tolerance + } +} + type URLTest struct { *outbound.Base + tolerance uint16 + lastDelay uint16 + fastNode C.Proxy single *singledo.Single fastSingle *singledo.Single providers []provider.ProxyProvider @@ -52,6 +63,13 @@ func (u *URLTest) proxies() []C.Proxy { func (u *URLTest) fast() C.Proxy { elm, _, _ := u.fastSingle.Do(func() (interface{}, error) { + // tolerance + if u.tolerance != 0 && u.fastNode != nil { + if u.fastNode.LastDelay() < u.lastDelay+u.tolerance { + return u.fastNode, nil + } + } + proxies := u.proxies() fast := proxies[0] min := fast.LastDelay() @@ -66,6 +84,9 @@ func (u *URLTest) fast() C.Proxy { min = delay } } + + u.fastNode = fast + u.lastDelay = fast.LastDelay() return fast, nil }) @@ -88,11 +109,30 @@ func (u *URLTest) MarshalJSON() ([]byte, error) { }) } -func NewURLTest(name string, providers []provider.ProxyProvider) *URLTest { - return &URLTest{ +func parseURLTestOption(config map[string]interface{}) []urlTestOption { + opts := []urlTestOption{} + + // tolerance + if elm, ok := config["tolerance"]; ok { + if tolerance, ok := elm.(int); ok { + opts = append(opts, urlTestWithTolerance(uint16(tolerance))) + } + } + + return opts +} + +func NewURLTest(name string, providers []provider.ProxyProvider, options ...urlTestOption) *URLTest { + urlTest := &URLTest{ Base: outbound.NewBase(name, "", C.URLTest, false), single: singledo.NewSingle(defaultGetProxiesDuration), fastSingle: singledo.NewSingle(time.Second * 10), providers: providers, } + + for _, option := range options { + option(urlTest) + } + + return urlTest }