github.com/yaling888/clash@v1.53.0/adapter/outboundgroup/parser.go (about) 1 package outboundgroup 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 regexp "github.com/dlclark/regexp2" 9 10 "github.com/yaling888/clash/adapter/outbound" 11 "github.com/yaling888/clash/adapter/provider" 12 "github.com/yaling888/clash/common/structure" 13 C "github.com/yaling888/clash/constant" 14 types "github.com/yaling888/clash/constant/provider" 15 ) 16 17 var ( 18 errFormat = errors.New("format error") 19 errType = errors.New("unsupported type") 20 errMissProxy = errors.New("`use` or `proxies` missing") 21 errMissHealthCheck = errors.New("`url` or `interval` missing") 22 errDuplicateProvider = errors.New("duplicate provider name") 23 ) 24 25 type GroupCommonOption struct { 26 outbound.BasicOption 27 Name string `group:"name"` 28 Type string `group:"type"` 29 Proxies []string `group:"proxies,omitempty"` 30 Use []string `group:"use,omitempty"` 31 URL string `group:"url,omitempty"` 32 Interval time.Duration `group:"interval,omitempty"` 33 Lazy bool `group:"lazy,omitempty"` 34 DisableUDP bool `group:"disable-udp,omitempty"` 35 DisableDNS bool `group:"disable-dns,omitempty"` 36 Filter string `group:"filter,omitempty"` 37 } 38 39 func ParseProxyGroup( 40 config map[string]any, 41 proxyMap map[string]C.Proxy, 42 providersMap map[string]types.ProxyProvider, 43 ) (C.ProxyAdapter, error) { 44 decoder := structure.NewDecoder(structure.Option{TagName: "group", WeaklyTypedInput: true}) 45 46 groupOption := &GroupCommonOption{ 47 Lazy: true, 48 } 49 50 if err := decoder.Decode(config, groupOption); err != nil { 51 return nil, errFormat 52 } 53 54 if groupOption.Type == "" || groupOption.Name == "" { 55 return nil, errFormat 56 } 57 58 groupName := groupOption.Name 59 60 if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 { 61 return nil, fmt.Errorf("%s: %w", groupName, errMissProxy) 62 } 63 64 if _, ok := providersMap[groupName]; ok { 65 return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider) 66 } 67 68 var filterRegx *regexp.Regexp 69 if groupOption.Filter != "" { 70 regx, err := regexp.Compile(groupOption.Filter, 0) 71 if err != nil { 72 return nil, fmt.Errorf("%s: invalid filter regex: %w", groupName, err) 73 } 74 filterRegx = regx 75 } 76 77 var hc *provider.HealthCheck 78 79 // select and relay don't need health check 80 if groupOption.Type == "select" || groupOption.Type == "relay" { 81 hc = provider.NewHealthCheck([]C.Proxy{}, "", 0, true) 82 } else { 83 if groupOption.URL == "" || groupOption.Interval <= 0 { 84 return nil, fmt.Errorf("%s: %w", groupName, errMissHealthCheck) 85 } 86 hc = provider.NewHealthCheck([]C.Proxy{}, groupOption.URL, groupOption.Interval, groupOption.Lazy) 87 } 88 89 pd, err := provider.NewCompatibleProvider(groupName, hc, filterRegx) 90 if err != nil { 91 return nil, fmt.Errorf("%s: %w", groupName, err) 92 } 93 94 if len(groupOption.Proxies) != 0 { 95 ps, err := getProxies(proxyMap, groupOption.Proxies) 96 if err != nil { 97 pd.Finalize() 98 return nil, fmt.Errorf("%s: %w", groupName, err) 99 } 100 101 pd.SetProxies(ps) 102 } 103 104 if len(groupOption.Use) != 0 { 105 list, err := getProviders(providersMap, groupOption.Use) 106 if err != nil { 107 pd.Finalize() 108 return nil, fmt.Errorf("%s: %w", groupName, err) 109 } 110 111 pd.SetProviders(list) 112 } 113 114 providers := []types.ProxyProvider{pd} 115 providersMap[groupName] = pd 116 117 var group C.ProxyAdapter 118 switch groupOption.Type { 119 case "url-test": 120 opts := parseURLTestOption(config) 121 group = NewURLTest(groupOption, providers, opts...) 122 case "select": 123 group = NewSelector(groupOption, providers) 124 case "fallback": 125 group = NewFallback(groupOption, providers) 126 case "load-balance": 127 strategy := parseStrategy(config) 128 group, err = NewLoadBalance(groupOption, providers, strategy) 129 if err != nil { 130 pd.Finalize() 131 return nil, fmt.Errorf("%s: %w", groupName, err) 132 } 133 case "relay": 134 group = NewRelay(groupOption, providers) 135 default: 136 pd.Finalize() 137 return nil, fmt.Errorf("%s %w: %s", groupName, errType, groupOption.Type) 138 } 139 140 return group, nil 141 } 142 143 func getProxies(mapping map[string]C.Proxy, list []string) ([]C.Proxy, error) { 144 var ps []C.Proxy 145 for _, name := range list { 146 p, ok := mapping[name] 147 if !ok { 148 return nil, fmt.Errorf("'%s' not found", name) 149 } 150 ps = append(ps, p) 151 } 152 return ps, nil 153 } 154 155 func getProviders(mapping map[string]types.ProxyProvider, list []string) ([]types.ProxyProvider, error) { 156 var ps []types.ProxyProvider 157 for _, name := range list { 158 p, ok := mapping[name] 159 if !ok { 160 return nil, fmt.Errorf("'%s' not found", name) 161 } 162 if p.VehicleType() == types.Compatible { 163 return nil, fmt.Errorf("proxy group %s can't contains in `use`", name) 164 } 165 ps = append(ps, p) 166 } 167 return ps, nil 168 }