Files
proxypool/pkg/proxy/check.go
2020-09-05 10:30:20 +08:00

136 lines
2.5 KiB
Go

package proxy
import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/ivpusic/grpool"
"github.com/Dreamacro/clash/adapters/outbound"
)
const defaultURLTestTimeout = time.Second * 5
func testDelay(p Proxy) (delay uint16, err error) {
pmap := make(map[string]interface{})
err = json.Unmarshal([]byte(p.String()), &pmap)
if err != nil {
return
}
pmap["port"] = int(pmap["port"].(float64))
if p.TypeName() == "vmess" {
pmap["alterId"] = int(pmap["alterId"].(float64))
}
clashProxy, err := outbound.ParseProxy(pmap)
if err != nil {
fmt.Println(err.Error())
return
}
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
delay, err = clashProxy.URLTest(ctx, "http://www.gstatic.com/generate_204")
cancel()
return delay, err
}
func CleanBadProxiesWithGrpool(proxies []Proxy) (cproxies []Proxy) {
pool := grpool.NewPool(500, 200)
c := make(chan checkResult)
defer close(c)
pool.WaitCount(len(proxies))
go func() {
for _, p := range proxies {
pp := p
pool.JobQueue <- func() {
defer pool.JobDone()
delay, err := testDelay(pp)
if err == nil {
c <- checkResult{
name: pp.Identifier(),
delay: delay,
}
}
}
}
}()
done := make(chan struct{})
defer close(done)
go func() {
pool.WaitAll()
pool.Release()
done <- struct{}{}
}()
okMap := make(map[string]struct{})
for {
select {
case r := <-c:
if r.delay > 0 {
okMap[r.name] = struct{}{}
}
case <-done:
cproxies = make(ProxyList, 0, 500)
for _, p := range proxies {
if _, ok := okMap[p.Identifier()]; ok {
cproxies = append(cproxies, p.Clone())
}
}
return
}
}
}
func CleanBadProxies(proxies []Proxy) (cproxies []Proxy) {
c := make(chan checkResult, 40)
wg := &sync.WaitGroup{}
wg.Add(len(proxies))
for _, p := range proxies {
go testProxyDelayToChan(p, c, wg)
}
go func() {
wg.Wait()
close(c)
}()
okMap := make(map[string]struct{})
for r := range c {
if r.delay > 0 {
okMap[r.name] = struct{}{}
}
}
cproxies = make(ProxyList, 0, 500)
for _, p := range proxies {
if _, ok := okMap[p.Identifier()]; ok {
p.SetUseable(true)
cproxies = append(cproxies, p.Clone())
} else {
p.SetUseable(false)
}
}
return
}
type checkResult struct {
name string
delay uint16
}
func testProxyDelayToChan(p Proxy, c chan checkResult, wg *sync.WaitGroup) {
defer wg.Done()
delay, err := testDelay(p)
if err == nil {
c <- checkResult{
name: p.Identifier(),
delay: delay,
}
}
}