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