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

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