github.com/yaling888/clash@v1.53.0/adapter/outboundgroup/selector.go (about)

     1  package outboundgroup
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  
     8  	"github.com/yaling888/clash/adapter/outbound"
     9  	"github.com/yaling888/clash/common/singledo"
    10  	"github.com/yaling888/clash/component/dialer"
    11  	C "github.com/yaling888/clash/constant"
    12  	"github.com/yaling888/clash/constant/provider"
    13  )
    14  
    15  var _ C.ProxyAdapter = (*Selector)(nil)
    16  
    17  type Selector struct {
    18  	*outbound.Base
    19  	disableUDP bool
    20  	disableDNS bool
    21  	single     *singledo.Single[C.Proxy]
    22  	selected   string
    23  	providers  []provider.ProxyProvider
    24  }
    25  
    26  // DialContext implements C.ProxyAdapter
    27  func (s *Selector) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
    28  	c, err := s.selectedProxy(true).DialContext(ctx, metadata, s.Base.DialOptions(opts...)...)
    29  	if err == nil {
    30  		c.AppendToChains(s)
    31  	}
    32  	return c, err
    33  }
    34  
    35  // ListenPacketContext implements C.ProxyAdapter
    36  func (s *Selector) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
    37  	pc, err := s.selectedProxy(true).ListenPacketContext(ctx, metadata, s.Base.DialOptions(opts...)...)
    38  	if err == nil {
    39  		pc.AppendToChains(s)
    40  	}
    41  	return pc, err
    42  }
    43  
    44  // SupportUDP implements C.ProxyAdapter
    45  func (s *Selector) SupportUDP() bool {
    46  	if s.disableUDP {
    47  		return false
    48  	}
    49  
    50  	return s.selectedProxy(false).SupportUDP()
    51  }
    52  
    53  // DisableDnsResolve implements C.DisableDnsResolve
    54  func (s *Selector) DisableDnsResolve() bool {
    55  	return s.disableDNS
    56  }
    57  
    58  // MarshalJSON implements C.ProxyAdapter
    59  func (s *Selector) MarshalJSON() ([]byte, error) {
    60  	var all []string
    61  	for _, proxy := range getProvidersProxies(s.providers, false) {
    62  		all = append(all, proxy.Name())
    63  	}
    64  
    65  	return json.Marshal(map[string]any{
    66  		"type": s.Type().String(),
    67  		"now":  s.Now(),
    68  		"all":  all,
    69  	})
    70  }
    71  
    72  func (s *Selector) Now() string {
    73  	return s.selectedProxy(false).Name()
    74  }
    75  
    76  func (s *Selector) Set(name string) error {
    77  	for _, proxy := range getProvidersProxies(s.providers, false) {
    78  		if proxy.Name() == name {
    79  			s.selected = name
    80  			s.single.Reset()
    81  			return nil
    82  		}
    83  	}
    84  
    85  	return errors.New("proxy not exist")
    86  }
    87  
    88  // Unwrap implements C.ProxyAdapter
    89  func (s *Selector) Unwrap(_ *C.Metadata) C.Proxy {
    90  	return s.selectedProxy(true)
    91  }
    92  
    93  // Cleanup implements C.ProxyAdapter
    94  func (s *Selector) Cleanup() {
    95  	s.single.Reset()
    96  }
    97  
    98  func (s *Selector) selectedProxy(touch bool) C.Proxy {
    99  	elm, _, _ := s.single.Do(func() (C.Proxy, error) {
   100  		proxies := getProvidersProxies(s.providers, touch)
   101  		for _, proxy := range proxies {
   102  			if proxy.Name() == s.selected {
   103  				return proxy, nil
   104  			}
   105  		}
   106  		s.selected = proxies[0].Name()
   107  
   108  		return proxies[0], nil
   109  	})
   110  
   111  	return elm
   112  }
   113  
   114  func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) *Selector {
   115  	return &Selector{
   116  		Base: outbound.NewBase(outbound.BaseOption{
   117  			Name:        option.Name,
   118  			Type:        C.Selector,
   119  			Interface:   option.Interface,
   120  			RoutingMark: option.RoutingMark,
   121  		}),
   122  		single:     singledo.NewSingle[C.Proxy](defaultGetProxiesDuration),
   123  		providers:  providers,
   124  		selected:   "",
   125  		disableUDP: option.DisableUDP,
   126  		disableDNS: option.DisableDNS,
   127  	}
   128  }