github.com/igoogolx/clash@v1.19.8/adapter/provider/provider.go (about) 1 package provider 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "runtime" 8 "time" 9 10 "github.com/igoogolx/clash/adapter" 11 "github.com/igoogolx/clash/adapter/outbound" 12 "github.com/igoogolx/clash/common/singledo" 13 C "github.com/igoogolx/clash/constant" 14 types "github.com/igoogolx/clash/constant/provider" 15 16 regexp "github.com/dlclark/regexp2" 17 "github.com/samber/lo" 18 "gopkg.in/yaml.v3" 19 ) 20 21 var reject = adapter.NewProxy(outbound.NewReject()) 22 23 const ( 24 ReservedName = "default" 25 ) 26 27 type ProxySchema struct { 28 Proxies []map[string]any `yaml:"proxies"` 29 } 30 31 // for auto gc 32 type ProxySetProvider struct { 33 *proxySetProvider 34 } 35 36 type proxySetProvider struct { 37 *fetcher 38 proxies []C.Proxy 39 healthCheck *HealthCheck 40 } 41 42 func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { 43 return json.Marshal(map[string]any{ 44 "name": pp.Name(), 45 "type": pp.Type().String(), 46 "vehicleType": pp.VehicleType().String(), 47 "proxies": pp.Proxies(), 48 "updatedAt": pp.updatedAt, 49 }) 50 } 51 52 func (pp *proxySetProvider) Name() string { 53 return pp.name 54 } 55 56 func (pp *proxySetProvider) HealthCheck() { 57 pp.healthCheck.checkAll() 58 } 59 60 func (pp *proxySetProvider) Update() error { 61 elm, same, err := pp.fetcher.Update() 62 if err == nil && !same { 63 pp.onUpdate(elm) 64 } 65 return err 66 } 67 68 func (pp *proxySetProvider) Initial() error { 69 elm, err := pp.fetcher.Initial() 70 if err != nil { 71 return err 72 } 73 74 pp.onUpdate(elm) 75 return nil 76 } 77 78 func (pp *proxySetProvider) Type() types.ProviderType { 79 return types.Proxy 80 } 81 82 func (pp *proxySetProvider) Proxies() []C.Proxy { 83 return pp.proxies 84 } 85 86 func (pp *proxySetProvider) Touch() { 87 pp.healthCheck.touch() 88 } 89 90 func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { 91 pp.proxies = proxies 92 pp.healthCheck.setProxy(proxies) 93 if pp.healthCheck.auto() { 94 go pp.healthCheck.checkAll() 95 } 96 } 97 98 func stopProxyProvider(pd *ProxySetProvider) { 99 pd.healthCheck.close() 100 pd.fetcher.Destroy() 101 } 102 103 func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { 104 filterReg, err := regexp.Compile(filter, regexp.None) 105 if err != nil { 106 return nil, fmt.Errorf("invalid filter regex: %w", err) 107 } 108 109 if hc.auto() { 110 go hc.process() 111 } 112 113 pd := &proxySetProvider{ 114 proxies: []C.Proxy{}, 115 healthCheck: hc, 116 } 117 118 onUpdate := func(elm any) { 119 ret := elm.([]C.Proxy) 120 pd.setProxies(ret) 121 } 122 123 proxiesParseAndFilter := func(buf []byte) (any, error) { 124 schema := &ProxySchema{} 125 126 if err := yaml.Unmarshal(buf, schema); err != nil { 127 return nil, err 128 } 129 130 if schema.Proxies == nil { 131 return nil, errors.New("file must have a `proxies` field") 132 } 133 134 proxies := []C.Proxy{} 135 for idx, mapping := range schema.Proxies { 136 if name, ok := mapping["name"].(string); ok && len(filter) > 0 { 137 matched, err := filterReg.MatchString(name) 138 if err != nil { 139 return nil, fmt.Errorf("regex filter failed: %w", err) 140 } 141 if !matched { 142 continue 143 } 144 } 145 proxy, err := adapter.ParseProxy(mapping) 146 if err != nil { 147 return nil, fmt.Errorf("proxy %d error: %w", idx, err) 148 } 149 proxies = append(proxies, proxy) 150 } 151 152 if len(proxies) == 0 { 153 if len(filter) > 0 { 154 return nil, errors.New("doesn't match any proxy, please check your filter") 155 } 156 return nil, errors.New("file doesn't have any proxy") 157 } 158 159 return proxies, nil 160 } 161 162 fetcher := newFetcher(name, interval, vehicle, proxiesParseAndFilter, onUpdate) 163 pd.fetcher = fetcher 164 165 wrapper := &ProxySetProvider{pd} 166 runtime.SetFinalizer(wrapper, stopProxyProvider) 167 return wrapper, nil 168 } 169 170 // for auto gc 171 type CompatibleProvider struct { 172 *compatibleProvider 173 } 174 175 type compatibleProvider struct { 176 name string 177 healthCheck *HealthCheck 178 proxies []C.Proxy 179 } 180 181 func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { 182 return json.Marshal(map[string]any{ 183 "name": cp.Name(), 184 "type": cp.Type().String(), 185 "vehicleType": cp.VehicleType().String(), 186 "proxies": cp.Proxies(), 187 }) 188 } 189 190 func (cp *compatibleProvider) Name() string { 191 return cp.name 192 } 193 194 func (cp *compatibleProvider) HealthCheck() { 195 cp.healthCheck.checkAll() 196 } 197 198 func (cp *compatibleProvider) Update() error { 199 return nil 200 } 201 202 func (cp *compatibleProvider) Initial() error { 203 return nil 204 } 205 206 func (cp *compatibleProvider) VehicleType() types.VehicleType { 207 return types.Compatible 208 } 209 210 func (cp *compatibleProvider) Type() types.ProviderType { 211 return types.Proxy 212 } 213 214 func (cp *compatibleProvider) Proxies() []C.Proxy { 215 return cp.proxies 216 } 217 218 func (cp *compatibleProvider) Touch() { 219 cp.healthCheck.touch() 220 } 221 222 func stopCompatibleProvider(pd *CompatibleProvider) { 223 pd.healthCheck.close() 224 } 225 226 func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) { 227 if len(proxies) == 0 { 228 return nil, errors.New("provider need one proxy at least") 229 } 230 231 if hc.auto() { 232 go hc.process() 233 } 234 235 pd := &compatibleProvider{ 236 name: name, 237 proxies: proxies, 238 healthCheck: hc, 239 } 240 241 wrapper := &CompatibleProvider{pd} 242 runtime.SetFinalizer(wrapper, stopCompatibleProvider) 243 return wrapper, nil 244 } 245 246 var _ types.ProxyProvider = (*FilterableProvider)(nil) 247 248 type FilterableProvider struct { 249 name string 250 providers []types.ProxyProvider 251 filterReg *regexp.Regexp 252 single *singledo.Single 253 } 254 255 func (fp *FilterableProvider) MarshalJSON() ([]byte, error) { 256 return json.Marshal(map[string]any{ 257 "name": fp.Name(), 258 "type": fp.Type().String(), 259 "vehicleType": fp.VehicleType().String(), 260 "proxies": fp.Proxies(), 261 }) 262 } 263 264 func (fp *FilterableProvider) Name() string { 265 return fp.name 266 } 267 268 func (fp *FilterableProvider) HealthCheck() { 269 } 270 271 func (fp *FilterableProvider) Update() error { 272 return nil 273 } 274 275 func (fp *FilterableProvider) Initial() error { 276 return nil 277 } 278 279 func (fp *FilterableProvider) VehicleType() types.VehicleType { 280 return types.Compatible 281 } 282 283 func (fp *FilterableProvider) Type() types.ProviderType { 284 return types.Proxy 285 } 286 287 func (fp *FilterableProvider) Proxies() []C.Proxy { 288 elm, _, _ := fp.single.Do(func() (any, error) { 289 proxies := lo.FlatMap( 290 fp.providers, 291 func(item types.ProxyProvider, _ int) []C.Proxy { 292 return lo.Filter( 293 item.Proxies(), 294 func(item C.Proxy, _ int) bool { 295 matched, _ := fp.filterReg.MatchString(item.Name()) 296 return matched 297 }) 298 }) 299 300 if len(proxies) == 0 { 301 proxies = append(proxies, reject) 302 } 303 return proxies, nil 304 }) 305 306 return elm.([]C.Proxy) 307 } 308 309 func (fp *FilterableProvider) Touch() { 310 for _, provider := range fp.providers { 311 provider.Touch() 312 } 313 } 314 315 func NewFilterableProvider(name string, providers []types.ProxyProvider, filterReg *regexp.Regexp) *FilterableProvider { 316 return &FilterableProvider{ 317 name: name, 318 providers: providers, 319 filterReg: filterReg, 320 single: singledo.NewSingle(time.Second * 10), 321 } 322 }