From 5d337b75f997743dee824166591c0deaf99ede8b Mon Sep 17 00:00:00 2001 From: MetaCubeX Date: Sun, 5 Jun 2022 16:54:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20proxy=20provider=20=E6=94=AF=E6=8C=81V2?= =?UTF-8?q?ray=E6=A0=BC=E5=BC=8F=E8=AE=A2=E9=98=85=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adapter/provider/fetcher.go | 96 +++++++++++++++++------------------- adapter/provider/provider.go | 34 +++++++++---- 2 files changed, 69 insertions(+), 61 deletions(-) diff --git a/adapter/provider/fetcher.go b/adapter/provider/fetcher.go index c58d7e4d..3fcda09a 100644 --- a/adapter/provider/fetcher.go +++ b/adapter/provider/fetcher.go @@ -16,42 +16,35 @@ var ( dirMode os.FileMode = 0o755 ) -type parser = func([]byte) (any, error) +type parser[V any] func([]byte) (V, error) -type fetcher struct { +type fetcher[V any] struct { name string vehicle types.Vehicle updatedAt *time.Time ticker *time.Ticker done chan struct{} hash [16]byte - parser parser + parser parser[V] interval time.Duration - onUpdate func(any) + onUpdate func(V) } -func (f *fetcher) Name() string { +func (f *fetcher[V]) Name() string { return f.name } -func (f *fetcher) VehicleType() types.VehicleType { +func (f *fetcher[V]) VehicleType() types.VehicleType { return f.vehicle.Type() } -func (f *fetcher) Initial() (any, error) { +func (f *fetcher[V]) Initial() (V, error) { var ( buf []byte err error isLocal bool ) - defer func() { - // pull proxies automatically - if f.ticker != nil { - go f.pullLoop() - } - }() - if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() @@ -60,7 +53,7 @@ func (f *fetcher) Initial() (any, error) { if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { defer func() { log.Infoln("[Provider] %s's proxies not updated for a long time, force refresh", f.Name()) - go f.update() + go f.Update() }() } } else { @@ -68,24 +61,24 @@ func (f *fetcher) Initial() (any, error) { } if err != nil { - return nil, err + return getZero[V](), err } proxies, err := f.parser(buf) if err != nil { if !isLocal { - return nil, err + return getZero[V](), err } // parse local file error, fallback to remote buf, err = f.vehicle.Read() if err != nil { - return nil, err + return getZero[V](), err } proxies, err = f.parser(buf) if err != nil { - return nil, err + return getZero[V](), err } isLocal = false @@ -93,19 +86,24 @@ func (f *fetcher) Initial() (any, error) { if f.vehicle.Type() != types.File && !isLocal { if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return nil, err + return getZero[V](), err } } f.hash = md5.Sum(buf) + // pull proxies automatically + if f.ticker != nil { + go f.pullLoop() + } + return proxies, nil } -func (f *fetcher) Update() (any, bool, error) { +func (f *fetcher[V]) Update() (V, bool, error) { buf, err := f.vehicle.Read() if err != nil { - return nil, false, err + return getZero[V](), false, err } now := time.Now() @@ -113,17 +111,17 @@ func (f *fetcher) Update() (any, bool, error) { if bytes.Equal(f.hash[:], hash[:]) { f.updatedAt = &now os.Chtimes(f.vehicle.Path(), now, now) - return nil, true, nil + return getZero[V](), true, nil } proxies, err := f.parser(buf) if err != nil { - return nil, false, err + return getZero[V](), false, err } if f.vehicle.Type() != types.File { if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return nil, false, err + return getZero[V](), false, err } } @@ -133,22 +131,32 @@ func (f *fetcher) Update() (any, bool, error) { return proxies, false, nil } -func (f *fetcher) Destroy() error { +func (f *fetcher[V]) Destroy() error { if f.ticker != nil { f.done <- struct{}{} } return nil } -func (f *fetcher) pullLoop() { +func (f *fetcher[V]) pullLoop() { for { select { case <-f.ticker.C: - same, err := f.update() - if same || err != nil { + elm, same, err := f.Update() + if err != nil { + log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error()) continue } + if same { + log.Debugln("[Provider] %s's proxies doesn't change", f.Name()) + continue + } + + log.Infoln("[Provider] %s's proxies update", f.Name()) + if f.onUpdate != nil { + f.onUpdate(elm) + } case <-f.done: f.ticker.Stop() return @@ -156,26 +164,6 @@ func (f *fetcher) pullLoop() { } } -func (f *fetcher) update() (same bool, err error) { - elm, same, err := f.Update() - if err != nil { - log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error()) - return - } - - if same { - log.Debugln("[Provider] %s's proxies doesn't change", f.Name()) - return - } - - if f.onUpdate != nil { - f.onUpdate(elm) - } - - log.Infoln("[Provider] %s's proxies update", f.Name()) - return -} - func safeWrite(path string, buf []byte) error { dir := filepath.Dir(path) @@ -188,19 +176,23 @@ func safeWrite(path string, buf []byte) error { return os.WriteFile(path, buf, fileMode) } -func newFetcher(name string, interval time.Duration, vehicle types.Vehicle, parser parser, onUpdate func(any)) *fetcher { +func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser parser[V], onUpdate func(V)) *fetcher[V] { var ticker *time.Ticker if interval != 0 { ticker = time.NewTicker(interval) } - return &fetcher{ + return &fetcher[V]{ name: name, ticker: ticker, vehicle: vehicle, parser: parser, done: make(chan struct{}, 1), onUpdate: onUpdate, - interval: interval, } } + +func getZero[V any]() V { + var result V + return result +} diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index f082b8b3..32b95701 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -4,12 +4,13 @@ import ( "encoding/json" "errors" "fmt" + "github.com/Dreamacro/clash/common/convert" "github.com/dlclark/regexp2" + "math" "runtime" "time" "github.com/Dreamacro/clash/adapter" - "github.com/Dreamacro/clash/common/convert" C "github.com/Dreamacro/clash/constant" types "github.com/Dreamacro/clash/constant/provider" @@ -36,10 +37,6 @@ type proxySetProvider struct { version uint } -func (pp *proxySetProvider) Version() uint { - return pp.version -} - func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]any{ "name": pp.Name(), @@ -50,6 +47,10 @@ func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { }) } +func (pp *proxySetProvider) Version() uint { + return pp.version +} + func (pp *proxySetProvider) Name() string { return pp.name } @@ -71,7 +72,6 @@ func (pp *proxySetProvider) Initial() error { if err != nil { return err } - pp.onUpdate(elm) return nil } @@ -93,7 +93,7 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { pp.proxies = proxies pp.healthCheck.setProxy(proxies) if pp.healthCheck.auto() { - go pp.healthCheck.check() + defer func() { go pp.healthCheck.check() }() } } @@ -134,6 +134,7 @@ type compatibleProvider struct { name string healthCheck *HealthCheck proxies []C.Proxy + version uint } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { @@ -145,6 +146,10 @@ func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { }) } +func (cp *compatibleProvider) Version() uint { + return cp.version +} + func (cp *compatibleProvider) Name() string { return cp.name } @@ -205,6 +210,11 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) + if pd.version == math.MaxUint { + pd.version = 0 + } else { + pd.version++ + } } } @@ -213,7 +223,11 @@ func proxiesParseAndFilter(filter string, filterReg *regexp2.Regexp) parser[[]C. schema := &ProxySchema{} if err := yaml.Unmarshal(buf, schema); err != nil { - return nil, err + proxies, err1 := convert.ConvertsV2Ray(buf) + if err1 != nil { + return nil, fmt.Errorf("%s, %w", err.Error(), err1) + } + schema.Proxies = proxies } if schema.Proxies == nil { @@ -222,7 +236,9 @@ func proxiesParseAndFilter(filter string, filterReg *regexp2.Regexp) parser[[]C. proxies := []C.Proxy{} for idx, mapping := range schema.Proxies { - if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { + name, ok := mapping["name"] + mat, _ := filterReg.FindStringMatch(name.(string)) + if ok && len(filter) > 0 && mat == nil { continue } proxy, err := adapter.ParseProxy(mapping)