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  }