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