Refactor: allow to add an empty proxy provider to proxy group
This commit is contained in:
parent
e31be4edc2
commit
1f12ef069b
@ -3,6 +3,8 @@ package outboundgroup
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter"
|
||||||
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
)
|
)
|
||||||
@ -11,14 +13,19 @@ const (
|
|||||||
defaultGetProxiesDuration = time.Second * 5
|
defaultGetProxiesDuration = time.Second * 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var defaultRejectProxy = adapter.NewProxy(outbound.NewReject())
|
||||||
|
|
||||||
func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy {
|
func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy {
|
||||||
proxies := []C.Proxy{}
|
proxies := []C.Proxy{}
|
||||||
for _, provider := range providers {
|
for _, pd := range providers {
|
||||||
if touch {
|
if touch {
|
||||||
proxies = append(proxies, provider.ProxiesWithTouch()...)
|
proxies = append(proxies, pd.ProxiesWithTouch()...)
|
||||||
} else {
|
} else {
|
||||||
proxies = append(proxies, provider.Proxies()...)
|
proxies = append(proxies, pd.Proxies()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(proxies) == 0 {
|
||||||
|
proxies = append(proxies, defaultRejectProxy)
|
||||||
|
}
|
||||||
return proxies
|
return proxies
|
||||||
}
|
}
|
||||||
|
@ -78,9 +78,11 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||||||
return nil, errDuplicateProvider
|
return nil, errDuplicateProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// select don't need health check
|
hc, err := newHealthCheck(ps, groupOption)
|
||||||
if groupOption.Type == "select" || groupOption.Type == "relay" {
|
if err != nil {
|
||||||
hc := provider.NewHealthCheck(ps, "", 0, true)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
|
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -88,20 +90,6 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||||||
|
|
||||||
providers = append(providers, pd)
|
providers = append(providers, pd)
|
||||||
providersMap[groupName] = pd
|
providersMap[groupName] = pd
|
||||||
} else {
|
|
||||||
if groupOption.URL == "" || groupOption.Interval == 0 {
|
|
||||||
return nil, errMissHealthCheck
|
|
||||||
}
|
|
||||||
|
|
||||||
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
|
|
||||||
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
providers = append(providers, pd)
|
|
||||||
providersMap[groupName] = pd
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(groupOption.Use) != 0 {
|
if len(groupOption.Use) != 0 {
|
||||||
@ -109,8 +97,13 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if groupOption.Type == "fallback" {
|
||||||
|
providers = append(list, providers...)
|
||||||
|
} else {
|
||||||
providers = append(providers, list...)
|
providers = append(providers, list...)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var group C.ProxyAdapter
|
var group C.ProxyAdapter
|
||||||
switch groupOption.Type {
|
switch groupOption.Type {
|
||||||
@ -163,22 +156,18 @@ func getProviders(mapping map[string]types.ProxyProvider, groupOption *GroupComm
|
|||||||
}
|
}
|
||||||
|
|
||||||
if filterRegx != nil {
|
if filterRegx != nil {
|
||||||
var hc *provider.HealthCheck
|
hc, err := newHealthCheck([]C.Proxy{}, groupOption)
|
||||||
if groupOption.Type == "select" || groupOption.Type == "relay" {
|
if err != nil {
|
||||||
hc = provider.NewHealthCheck([]C.Proxy{}, "", 0, true)
|
return nil, err
|
||||||
} else {
|
|
||||||
if groupOption.URL == "" || groupOption.Interval == 0 {
|
|
||||||
return nil, errMissHealthCheck
|
|
||||||
}
|
|
||||||
hc = provider.NewHealthCheck([]C.Proxy{}, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok = mapping[groupName]; ok {
|
gName := groupName
|
||||||
groupName += "->" + p.Name()
|
if _, ok = mapping[gName]; ok {
|
||||||
|
gName = groupName + " -> " + p.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
pd := p.(*provider.ProxySetProvider)
|
pd := p.(*provider.ProxySetProvider)
|
||||||
p = provider.NewProxyFilterProvider(groupName, pd, hc, filterRegx)
|
p = provider.NewProxyFilterProvider(gName, pd, hc, filterRegx)
|
||||||
pd.RegisterProvidersInUse(p)
|
pd.RegisterProvidersInUse(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,3 +175,18 @@ func getProviders(mapping map[string]types.ProxyProvider, groupOption *GroupComm
|
|||||||
}
|
}
|
||||||
return ps, nil
|
return ps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newHealthCheck(ps []C.Proxy, groupOption *GroupCommonOption) (*provider.HealthCheck, error) {
|
||||||
|
var hc *provider.HealthCheck
|
||||||
|
|
||||||
|
// select don't need health check
|
||||||
|
if groupOption.Type == "select" || groupOption.Type == "relay" {
|
||||||
|
hc = provider.NewHealthCheck(ps, "", 0, true)
|
||||||
|
} else {
|
||||||
|
if groupOption.URL == "" || groupOption.Interval == 0 {
|
||||||
|
return nil, errMissHealthCheck
|
||||||
|
}
|
||||||
|
hc = provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
|
||||||
|
}
|
||||||
|
return hc, nil
|
||||||
|
}
|
||||||
|
@ -98,7 +98,11 @@ func (s *Selector) selectedProxy(touch bool) C.Proxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) *Selector {
|
func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) *Selector {
|
||||||
selected := providers[0].Proxies()[0].Name()
|
selected := "REJECT"
|
||||||
|
if len(providers) != 0 && len(providers[0].Proxies()) != 0 {
|
||||||
|
selected = providers[0].Proxies()[0].Name()
|
||||||
|
}
|
||||||
|
|
||||||
return &Selector{
|
return &Selector{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
|
func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
|
||||||
decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true})
|
decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true})
|
||||||
proxyType, existType := mapping["type"].(string)
|
proxyType, existType := mapping["type"]
|
||||||
if !existType {
|
if !existType {
|
||||||
return nil, fmt.Errorf("missing type")
|
return nil, fmt.Errorf("missing type")
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
|
|||||||
proxy C.ProxyAdapter
|
proxy C.ProxyAdapter
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
switch proxyType {
|
switch proxyType.(string) {
|
||||||
case "ss":
|
case "ss":
|
||||||
ssOption := &outbound.ShadowSocksOption{}
|
ssOption := &outbound.ShadowSocksOption{}
|
||||||
err = decoder.Decode(mapping, ssOption)
|
err = decoder.Decode(mapping, ssOption)
|
||||||
|
@ -25,10 +25,16 @@ type HealthCheck struct {
|
|||||||
interval uint
|
interval uint
|
||||||
lazy bool
|
lazy bool
|
||||||
lastTouch *atomic.Int64
|
lastTouch *atomic.Int64
|
||||||
|
running *atomic.Bool
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) process() {
|
func (hc *HealthCheck) process() {
|
||||||
|
if hc.running.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hc.running.Store(true)
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -84,6 +90,10 @@ func (hc *HealthCheck) check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) close() {
|
func (hc *HealthCheck) close() {
|
||||||
|
if !hc.running.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hc.running.Store(false)
|
||||||
hc.done <- struct{}{}
|
hc.done <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +104,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *He
|
|||||||
interval: interval,
|
interval: interval,
|
||||||
lazy: lazy,
|
lazy: lazy,
|
||||||
lastTouch: atomic.NewInt64(0),
|
lastTouch: atomic.NewInt64(0),
|
||||||
|
running: atomic.NewBool(false),
|
||||||
done: make(chan struct{}, 1),
|
done: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,9 @@ func (pf *proxyFilterProvider) HealthCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pf *proxyFilterProvider) Update() error {
|
func (pf *proxyFilterProvider) Update() error {
|
||||||
var proxies []C.Proxy
|
pf.healthCheck.close()
|
||||||
|
|
||||||
|
proxies := []C.Proxy{}
|
||||||
if pf.filter != nil {
|
if pf.filter != nil {
|
||||||
for _, proxy := range pf.psd.Proxies() {
|
for _, proxy := range pf.psd.Proxies() {
|
||||||
if !pf.filter.MatchString(proxy.Name()) {
|
if !pf.filter.MatchString(proxy.Name()) {
|
||||||
@ -248,6 +250,10 @@ func (pf *proxyFilterProvider) Update() error {
|
|||||||
|
|
||||||
pf.proxies = proxies
|
pf.proxies = proxies
|
||||||
pf.healthCheck.setProxy(proxies)
|
pf.healthCheck.setProxy(proxies)
|
||||||
|
|
||||||
|
if len(proxies) != 0 && pf.healthCheck.auto() {
|
||||||
|
go pf.healthCheck.process()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,10 +292,6 @@ func NewProxyFilterProvider(name string, psd *ProxySetProvider, hc *HealthCheck,
|
|||||||
|
|
||||||
_ = pd.Update()
|
_ = pd.Update()
|
||||||
|
|
||||||
if hc.auto() {
|
|
||||||
go hc.process()
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper := &ProxyFilterProvider{pd}
|
wrapper := &ProxyFilterProvider{pd}
|
||||||
runtime.SetFinalizer(wrapper, stopProxyFilterProvider)
|
runtime.SetFinalizer(wrapper, stopProxyFilterProvider)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -415,11 +415,11 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
|||||||
|
|
||||||
// keep the original order of ProxyGroups in config file
|
// keep the original order of ProxyGroups in config file
|
||||||
for idx, mapping := range groupsConfig {
|
for idx, mapping := range groupsConfig {
|
||||||
groupName, existName := mapping["name"].(string)
|
groupName, existName := mapping["name"]
|
||||||
if !existName {
|
if !existName {
|
||||||
return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
|
return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
|
||||||
}
|
}
|
||||||
proxyList = append(proxyList, groupName)
|
proxyList = append(proxyList, groupName.(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if any loop exists and sort the ProxyGroups
|
// check if any loop exists and sort the ProxyGroups
|
||||||
|
Reference in New Issue
Block a user