github.com/igoogolx/clash@v1.19.8/adapter/provider/provider.go (about)

     1  package provider
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"runtime"
     8  	"time"
     9  
    10  	"github.com/igoogolx/clash/adapter"
    11  	"github.com/igoogolx/clash/adapter/outbound"
    12  	"github.com/igoogolx/clash/common/singledo"
    13  	C "github.com/igoogolx/clash/constant"
    14  	types "github.com/igoogolx/clash/constant/provider"
    15  
    16  	regexp "github.com/dlclark/regexp2"
    17  	"github.com/samber/lo"
    18  	"gopkg.in/yaml.v3"
    19  )
    20  
    21  var reject = adapter.NewProxy(outbound.NewReject())
    22  
    23  const (
    24  	ReservedName = "default"
    25  )
    26  
    27  type ProxySchema struct {
    28  	Proxies []map[string]any `yaml:"proxies"`
    29  }
    30  
    31  // for auto gc
    32  type ProxySetProvider struct {
    33  	*proxySetProvider
    34  }
    35  
    36  type proxySetProvider struct {
    37  	*fetcher
    38  	proxies     []C.Proxy
    39  	healthCheck *HealthCheck
    40  }
    41  
    42  func (pp *proxySetProvider) MarshalJSON() ([]byte, error) {
    43  	return json.Marshal(map[string]any{
    44  		"name":        pp.Name(),
    45  		"type":        pp.Type().String(),
    46  		"vehicleType": pp.VehicleType().String(),
    47  		"proxies":     pp.Proxies(),
    48  		"updatedAt":   pp.updatedAt,
    49  	})
    50  }
    51  
    52  func (pp *proxySetProvider) Name() string {
    53  	return pp.name
    54  }
    55  
    56  func (pp *proxySetProvider) HealthCheck() {
    57  	pp.healthCheck.checkAll()
    58  }
    59  
    60  func (pp *proxySetProvider) Update() error {
    61  	elm, same, err := pp.fetcher.Update()
    62  	if err == nil && !same {
    63  		pp.onUpdate(elm)
    64  	}
    65  	return err
    66  }
    67  
    68  func (pp *proxySetProvider) Initial() error {
    69  	elm, err := pp.fetcher.Initial()
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	pp.onUpdate(elm)
    75  	return nil
    76  }
    77  
    78  func (pp *proxySetProvider) Type() types.ProviderType {
    79  	return types.Proxy
    80  }
    81  
    82  func (pp *proxySetProvider) Proxies() []C.Proxy {
    83  	return pp.proxies
    84  }
    85  
    86  func (pp *proxySetProvider) Touch() {
    87  	pp.healthCheck.touch()
    88  }
    89  
    90  func (pp *proxySetProvider) setProxies(proxies []C.Proxy) {
    91  	pp.proxies = proxies
    92  	pp.healthCheck.setProxy(proxies)
    93  	if pp.healthCheck.auto() {
    94  		go pp.healthCheck.checkAll()
    95  	}
    96  }
    97  
    98  func stopProxyProvider(pd *ProxySetProvider) {
    99  	pd.healthCheck.close()
   100  	pd.fetcher.Destroy()
   101  }
   102  
   103  func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
   104  	filterReg, err := regexp.Compile(filter, regexp.None)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("invalid filter regex: %w", err)
   107  	}
   108  
   109  	if hc.auto() {
   110  		go hc.process()
   111  	}
   112  
   113  	pd := &proxySetProvider{
   114  		proxies:     []C.Proxy{},
   115  		healthCheck: hc,
   116  	}
   117  
   118  	onUpdate := func(elm any) {
   119  		ret := elm.([]C.Proxy)
   120  		pd.setProxies(ret)
   121  	}
   122  
   123  	proxiesParseAndFilter := func(buf []byte) (any, error) {
   124  		schema := &ProxySchema{}
   125  
   126  		if err := yaml.Unmarshal(buf, schema); err != nil {
   127  			return nil, err
   128  		}
   129  
   130  		if schema.Proxies == nil {
   131  			return nil, errors.New("file must have a `proxies` field")
   132  		}
   133  
   134  		proxies := []C.Proxy{}
   135  		for idx, mapping := range schema.Proxies {
   136  			if name, ok := mapping["name"].(string); ok && len(filter) > 0 {
   137  				matched, err := filterReg.MatchString(name)
   138  				if err != nil {
   139  					return nil, fmt.Errorf("regex filter failed: %w", err)
   140  				}
   141  				if !matched {
   142  					continue
   143  				}
   144  			}
   145  			proxy, err := adapter.ParseProxy(mapping)
   146  			if err != nil {
   147  				return nil, fmt.Errorf("proxy %d error: %w", idx, err)
   148  			}
   149  			proxies = append(proxies, proxy)
   150  		}
   151  
   152  		if len(proxies) == 0 {
   153  			if len(filter) > 0 {
   154  				return nil, errors.New("doesn't match any proxy, please check your filter")
   155  			}
   156  			return nil, errors.New("file doesn't have any proxy")
   157  		}
   158  
   159  		return proxies, nil
   160  	}
   161  
   162  	fetcher := newFetcher(name, interval, vehicle, proxiesParseAndFilter, onUpdate)
   163  	pd.fetcher = fetcher
   164  
   165  	wrapper := &ProxySetProvider{pd}
   166  	runtime.SetFinalizer(wrapper, stopProxyProvider)
   167  	return wrapper, nil
   168  }
   169  
   170  // for auto gc
   171  type CompatibleProvider struct {
   172  	*compatibleProvider
   173  }
   174  
   175  type compatibleProvider struct {
   176  	name        string
   177  	healthCheck *HealthCheck
   178  	proxies     []C.Proxy
   179  }
   180  
   181  func (cp *compatibleProvider) MarshalJSON() ([]byte, error) {
   182  	return json.Marshal(map[string]any{
   183  		"name":        cp.Name(),
   184  		"type":        cp.Type().String(),
   185  		"vehicleType": cp.VehicleType().String(),
   186  		"proxies":     cp.Proxies(),
   187  	})
   188  }
   189  
   190  func (cp *compatibleProvider) Name() string {
   191  	return cp.name
   192  }
   193  
   194  func (cp *compatibleProvider) HealthCheck() {
   195  	cp.healthCheck.checkAll()
   196  }
   197  
   198  func (cp *compatibleProvider) Update() error {
   199  	return nil
   200  }
   201  
   202  func (cp *compatibleProvider) Initial() error {
   203  	return nil
   204  }
   205  
   206  func (cp *compatibleProvider) VehicleType() types.VehicleType {
   207  	return types.Compatible
   208  }
   209  
   210  func (cp *compatibleProvider) Type() types.ProviderType {
   211  	return types.Proxy
   212  }
   213  
   214  func (cp *compatibleProvider) Proxies() []C.Proxy {
   215  	return cp.proxies
   216  }
   217  
   218  func (cp *compatibleProvider) Touch() {
   219  	cp.healthCheck.touch()
   220  }
   221  
   222  func stopCompatibleProvider(pd *CompatibleProvider) {
   223  	pd.healthCheck.close()
   224  }
   225  
   226  func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) {
   227  	if len(proxies) == 0 {
   228  		return nil, errors.New("provider need one proxy at least")
   229  	}
   230  
   231  	if hc.auto() {
   232  		go hc.process()
   233  	}
   234  
   235  	pd := &compatibleProvider{
   236  		name:        name,
   237  		proxies:     proxies,
   238  		healthCheck: hc,
   239  	}
   240  
   241  	wrapper := &CompatibleProvider{pd}
   242  	runtime.SetFinalizer(wrapper, stopCompatibleProvider)
   243  	return wrapper, nil
   244  }
   245  
   246  var _ types.ProxyProvider = (*FilterableProvider)(nil)
   247  
   248  type FilterableProvider struct {
   249  	name      string
   250  	providers []types.ProxyProvider
   251  	filterReg *regexp.Regexp
   252  	single    *singledo.Single
   253  }
   254  
   255  func (fp *FilterableProvider) MarshalJSON() ([]byte, error) {
   256  	return json.Marshal(map[string]any{
   257  		"name":        fp.Name(),
   258  		"type":        fp.Type().String(),
   259  		"vehicleType": fp.VehicleType().String(),
   260  		"proxies":     fp.Proxies(),
   261  	})
   262  }
   263  
   264  func (fp *FilterableProvider) Name() string {
   265  	return fp.name
   266  }
   267  
   268  func (fp *FilterableProvider) HealthCheck() {
   269  }
   270  
   271  func (fp *FilterableProvider) Update() error {
   272  	return nil
   273  }
   274  
   275  func (fp *FilterableProvider) Initial() error {
   276  	return nil
   277  }
   278  
   279  func (fp *FilterableProvider) VehicleType() types.VehicleType {
   280  	return types.Compatible
   281  }
   282  
   283  func (fp *FilterableProvider) Type() types.ProviderType {
   284  	return types.Proxy
   285  }
   286  
   287  func (fp *FilterableProvider) Proxies() []C.Proxy {
   288  	elm, _, _ := fp.single.Do(func() (any, error) {
   289  		proxies := lo.FlatMap(
   290  			fp.providers,
   291  			func(item types.ProxyProvider, _ int) []C.Proxy {
   292  				return lo.Filter(
   293  					item.Proxies(),
   294  					func(item C.Proxy, _ int) bool {
   295  						matched, _ := fp.filterReg.MatchString(item.Name())
   296  						return matched
   297  					})
   298  			})
   299  
   300  		if len(proxies) == 0 {
   301  			proxies = append(proxies, reject)
   302  		}
   303  		return proxies, nil
   304  	})
   305  
   306  	return elm.([]C.Proxy)
   307  }
   308  
   309  func (fp *FilterableProvider) Touch() {
   310  	for _, provider := range fp.providers {
   311  		provider.Touch()
   312  	}
   313  }
   314  
   315  func NewFilterableProvider(name string, providers []types.ProxyProvider, filterReg *regexp.Regexp) *FilterableProvider {
   316  	return &FilterableProvider{
   317  		name:      name,
   318  		providers: providers,
   319  		filterReg: filterReg,
   320  		single:    singledo.NewSingle(time.Second * 10),
   321  	}
   322  }