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 }