diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 8a173966..bd393569 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -24,6 +24,7 @@ type proxyProviderSchema struct { Path string `provider:"path"` URL string `provider:"url,omitempty"` Interval int `provider:"interval,omitempty"` + Filter string `provider:"filter,omitempty"` HealthCheck healthCheckSchema `provider:"health-check,omitempty"` } @@ -58,5 +59,6 @@ func ParseProxyProvider(name string, mapping map[string]interface{}) (types.Prox } interval := time.Duration(uint(schema.Interval)) * time.Second - return NewProxySetProvider(name, interval, vehicle, hc), nil + filter := schema.Filter + return NewProxySetProvider(name, interval, filter, vehicle, hc), nil } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 32baaedd..c042b2de 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "regexp" "runtime" "time" @@ -82,33 +83,6 @@ func (pp *proxySetProvider) ProxiesWithTouch() []C.Proxy { return pp.Proxies() } -func proxiesParse(buf []byte) (interface{}, error) { - schema := &ProxySchema{} - - if err := yaml.Unmarshal(buf, schema); err != nil { - return nil, err - } - - if schema.Proxies == nil { - return nil, errors.New("file must have a `proxies` field") - } - - proxies := []C.Proxy{} - for idx, mapping := range schema.Proxies { - proxy, err := adapter.ParseProxy(mapping) - if err != nil { - return nil, fmt.Errorf("proxy %d error: %w", idx, err) - } - proxies = append(proxies, proxy) - } - - if len(proxies) == 0 { - return nil, errors.New("file doesn't have any valid proxy") - } - - return proxies, nil -} - func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { pp.proxies = proxies pp.healthCheck.setProxy(proxies) @@ -122,7 +96,7 @@ func stopProxyProvider(pd *ProxySetProvider) { pd.fetcher.Destroy() } -func NewProxySetProvider(name string, interval time.Duration, vehicle types.Vehicle, hc *HealthCheck) *ProxySetProvider { +func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck) *ProxySetProvider { if hc.auto() { go hc.process() } @@ -137,7 +111,41 @@ func NewProxySetProvider(name string, interval time.Duration, vehicle types.Vehi pd.setProxies(ret) } - fetcher := newFetcher(name, interval, vehicle, proxiesParse, onUpdate) + proxiesParseAndFilter := func(buf []byte) (interface{}, error) { + schema := &ProxySchema{} + + if err := yaml.Unmarshal(buf, schema); err != nil { + return nil, err + } + + if schema.Proxies == nil { + return nil, errors.New("file must have a `proxies` field") + } + + proxies := []C.Proxy{} + filterReg := regexp.MustCompile(filter) + for idx, mapping := range schema.Proxies { + if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { + continue + } + proxy, err := adapter.ParseProxy(mapping) + if err != nil { + return nil, fmt.Errorf("proxy %d error: %w", idx, err) + } + proxies = append(proxies, proxy) + } + + if len(proxies) == 0 { + if len(filter) > 0 { + return nil, errors.New("doesn't match any proxy, please check your filter") + } + return nil, errors.New("file doesn't have any proxy") + } + + return proxies, nil + } + + fetcher := newFetcher(name, interval, vehicle, proxiesParseAndFilter, onUpdate) pd.fetcher = fetcher wrapper := &ProxySetProvider{pd} diff --git a/constant/adapters.go b/constant/adapters.go index 59fde960..136e48eb 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -93,7 +93,6 @@ type ProxyAdapter interface { // DialContext return a C.Conn with protocol which // contains multiplexing-related reuse logic (if any) DialContext(ctx context.Context, metadata *Metadata, opts ...dialer.Option) (Conn, error) - ListenPacketContext(ctx context.Context, metadata *Metadata, opts ...dialer.Option) (PacketConn, error) // Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract.