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 }