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