github.com/igoogolx/clash@v1.19.8/adapter/outboundgroup/fallback.go (about)

     1  package outboundgroup
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  
     7  	"github.com/igoogolx/clash/adapter/outbound"
     8  	"github.com/igoogolx/clash/common/singledo"
     9  	"github.com/igoogolx/clash/component/dialer"
    10  	C "github.com/igoogolx/clash/constant"
    11  	"github.com/igoogolx/clash/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]any{
    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() (any, 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  }