2019-12-08 04:17:24 +00:00
|
|
|
package provider
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-06-25 00:53:04 +00:00
|
|
|
"github.com/Dreamacro/clash/common/singledo"
|
2019-12-08 04:17:24 +00:00
|
|
|
"time"
|
|
|
|
|
2021-07-21 09:01:02 +00:00
|
|
|
"github.com/Dreamacro/clash/common/batch"
|
2019-12-08 04:17:24 +00:00
|
|
|
C "github.com/Dreamacro/clash/constant"
|
2020-11-18 16:53:22 +00:00
|
|
|
|
|
|
|
"go.uber.org/atomic"
|
2019-12-08 04:17:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultURLTestTimeout = time.Second * 5
|
|
|
|
)
|
|
|
|
|
|
|
|
type HealthCheckOption struct {
|
|
|
|
URL string
|
|
|
|
Interval uint
|
|
|
|
}
|
|
|
|
|
2019-12-26 10:41:06 +00:00
|
|
|
type HealthCheck struct {
|
2020-11-18 16:53:22 +00:00
|
|
|
url string
|
|
|
|
proxies []C.Proxy
|
|
|
|
interval uint
|
|
|
|
lazy bool
|
|
|
|
lastTouch *atomic.Int64
|
|
|
|
done chan struct{}
|
2022-06-25 00:53:04 +00:00
|
|
|
singleDo *singledo.Single[struct{}]
|
2019-12-08 04:17:24 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 10:41:06 +00:00
|
|
|
func (hc *HealthCheck) process() {
|
|
|
|
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
|
|
|
|
2022-03-27 16:46:44 +00:00
|
|
|
go func() {
|
2022-06-07 09:19:25 +00:00
|
|
|
time.Sleep(30 * time.Second)
|
2022-03-27 16:46:44 +00:00
|
|
|
hc.check()
|
|
|
|
}()
|
|
|
|
|
2019-12-26 10:41:06 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
2020-11-18 16:53:22 +00:00
|
|
|
now := time.Now().Unix()
|
|
|
|
if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) {
|
|
|
|
hc.check()
|
|
|
|
}
|
2019-12-26 10:41:06 +00:00
|
|
|
case <-hc.done:
|
|
|
|
ticker.Stop()
|
|
|
|
return
|
|
|
|
}
|
2019-12-08 04:17:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 10:41:06 +00:00
|
|
|
func (hc *HealthCheck) setProxy(proxies []C.Proxy) {
|
|
|
|
hc.proxies = proxies
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hc *HealthCheck) auto() bool {
|
|
|
|
return hc.interval != 0
|
|
|
|
}
|
|
|
|
|
2020-11-18 16:53:22 +00:00
|
|
|
func (hc *HealthCheck) touch() {
|
|
|
|
hc.lastTouch.Store(time.Now().Unix())
|
|
|
|
}
|
|
|
|
|
2019-12-26 10:41:06 +00:00
|
|
|
func (hc *HealthCheck) check() {
|
2022-06-25 00:53:04 +00:00
|
|
|
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
|
|
|
b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10))
|
|
|
|
for _, proxy := range hc.proxies {
|
|
|
|
p := proxy
|
|
|
|
b.Go(p.Name(), func() (bool, error) {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
|
|
|
|
defer cancel()
|
|
|
|
_, _ = p.URLTest(ctx, hc.url)
|
|
|
|
return false, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
b.Wait()
|
|
|
|
return struct{}{}, nil
|
|
|
|
})
|
2019-12-08 04:17:24 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 10:41:06 +00:00
|
|
|
func (hc *HealthCheck) close() {
|
|
|
|
hc.done <- struct{}{}
|
2019-12-08 04:17:24 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 16:53:22 +00:00
|
|
|
func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *HealthCheck {
|
2019-12-26 10:41:06 +00:00
|
|
|
return &HealthCheck{
|
2020-11-18 16:53:22 +00:00
|
|
|
proxies: proxies,
|
|
|
|
url: url,
|
|
|
|
interval: interval,
|
|
|
|
lazy: lazy,
|
|
|
|
lastTouch: atomic.NewInt64(0),
|
|
|
|
done: make(chan struct{}, 1),
|
2022-06-25 00:53:04 +00:00
|
|
|
singleDo: singledo.NewSingle[struct{}](time.Second),
|
2019-12-08 04:17:24 +00:00
|
|
|
}
|
|
|
|
}
|