diff --git a/adapter/adapter.go b/adapter/adapter.go index 33f567c5..ae9584be 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -17,8 +17,6 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/dialer" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/log" - "github.com/puzpuzpuz/xsync/v3" ) @@ -41,11 +39,6 @@ type Proxy struct { extra *xsync.MapOf[string, *extraProxyState] } -// Alive implements C.Proxy -func (p *Proxy) Alive() bool { - return p.alive.Load() -} - // AliveForTestUrl implements C.Proxy func (p *Proxy) AliveForTestUrl(url string) bool { if state, ok := p.extra.Load(url); ok { @@ -181,7 +174,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { _ = json.Unmarshal(inner, &mapping) mapping["history"] = p.DelayHistory() mapping["extra"] = p.ExtraDelayHistory() - mapping["alive"] = p.Alive() + mapping["alive"] = p.AliveForTestUrl(p.url) mapping["name"] = p.Name() mapping["udp"] = p.SupportUDP() mapping["xudp"] = p.SupportXUDP() @@ -191,13 +184,11 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { // URLTest get the delay for the specified URL // implements C.Proxy -func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16], store C.DelayHistoryStoreType) (t uint16, err error) { +func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (t uint16, err error) { defer func() { alive := err == nil - store = p.determineFinalStoreType(store, url) - switch store { - case C.OriginalHistory: + if len(p.url) == 0 || url == p.url { p.alive.Store(alive) record := C.DelayHistory{Time: time.Now()} if alive { @@ -212,7 +203,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if len(p.url) == 0 { p.url = url } - case C.ExtraHistory: + } else { record := C.DelayHistory{Time: time.Now()} if alive { record.Delay = t @@ -236,8 +227,6 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if state.history.Len() > defaultHistoriesNum { state.history.Pop() } - default: - log.Debugln("health check result will be discarded, url: %s alive: %t, delay: %d", url, alive, t) } }() @@ -349,24 +338,3 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) { } return } - -func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url string) C.DelayHistoryStoreType { - if store != C.DropHistory { - return store - } - - if len(p.url) == 0 || url == p.url { - return C.OriginalHistory - } - - if p.extra.Size() < 2*C.DefaultMaxHealthCheckUrlNum { - return C.ExtraHistory - } - - _, ok := p.extra.Load(url) - if ok { - return C.ExtraHistory - } - - return store -} diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index d0dd98b1..50427e53 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -84,11 +84,11 @@ func (f *Fallback) MarshalJSON() ([]byte, error) { all = append(all, proxy.Name()) } return json.Marshal(map[string]any{ - "type": f.Type().String(), - "now": f.Now(), - "all": all, - "testUrl": f.testUrl, - "expected": f.expectedStatus, + "type": f.Type().String(), + "now": f.Now(), + "all": all, + "testUrl": f.testUrl, + "expectedStatus": f.expectedStatus, }) } @@ -102,13 +102,11 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy { proxies := f.GetProxies(touch) for _, proxy := range proxies { if len(f.selected) == 0 { - // if proxy.Alive() { if proxy.AliveForTestUrl(f.testUrl) { return proxy } } else { if proxy.Name() == f.selected { - // if proxy.Alive() { if proxy.AliveForTestUrl(f.testUrl) { return proxy } else { @@ -135,12 +133,11 @@ func (f *Fallback) Set(name string) error { } f.selected = name - // if !p.Alive() { if !p.AliveForTestUrl(f.testUrl) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000)) defer cancel() expectedStatus, _ := utils.NewIntRanges[uint16](f.expectedStatus) - _, _ = p.URLTest(ctx, f.testUrl, expectedStatus, C.ExtraHistory) + _, _ = p.URLTest(ctx, f.testUrl, expectedStatus) } return nil diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 8f5f92df..0ea3685b 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -202,7 +202,7 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti proxy := proxy wg.Add(1) go func() { - delay, err := proxy.URLTest(ctx, url, expectedStatus, C.DropHistory) + delay, err := proxy.URLTest(ctx, url, expectedStatus) if err == nil { lock.Lock() mp[proxy.Name()] = delay diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 8607bea1..422349fe 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -95,7 +95,8 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide // select don't need health check if groupOption.Type != "select" && groupOption.Type != "relay" { if groupOption.URL == "" { - groupOption.URL = "https://cp.cloudflare.com/generate_204" + groupOption.URL = C.DefaultTestURL + testUrl = groupOption.URL } if groupOption.Interval == 0 { diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 8c861768..bdac909f 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -101,7 +101,7 @@ func (u *URLTest) fast(touch bool) C.Proxy { proxies := u.GetProxies(touch) if u.selected != "" { for _, proxy := range proxies { - if !proxy.Alive() { + if !proxy.AliveForTestUrl(u.testUrl) { continue } if proxy.Name() == u.selected { @@ -113,7 +113,6 @@ func (u *URLTest) fast(touch bool) C.Proxy { elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) { fast := proxies[0] - // min := fast.LastDelay() min := fast.LastDelayForTestUrl(u.testUrl) fastNotExist := true @@ -122,12 +121,10 @@ func (u *URLTest) fast(touch bool) C.Proxy { fastNotExist = false } - // if !proxy.Alive() { if !proxy.AliveForTestUrl(u.testUrl) { continue } - // delay := proxy.LastDelay() delay := proxy.LastDelayForTestUrl(u.testUrl) if delay < min { fast = proxy @@ -136,7 +133,6 @@ func (u *URLTest) fast(touch bool) C.Proxy { } // tolerance - // if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance { if u.fastNode == nil || fastNotExist || !u.fastNode.AliveForTestUrl(u.testUrl) || u.fastNode.LastDelayForTestUrl(u.testUrl) > fast.LastDelayForTestUrl(u.testUrl)+u.tolerance { u.fastNode = fast } @@ -169,11 +165,11 @@ func (u *URLTest) MarshalJSON() ([]byte, error) { all = append(all, proxy.Name()) } return json.Marshal(map[string]any{ - "type": u.Type().String(), - "now": u.Now(), - "all": all, - "testUrl": u.testUrl, - "expected": u.expectedStatus, + "type": u.Type().String(), + "now": u.Now(), + "all": all, + "testUrl": u.testUrl, + "expectedStatus": u.expectedStatus, }) } diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index 6a7cd3ef..d8e56192 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -18,7 +18,6 @@ import ( const ( defaultURLTestTimeout = time.Second * 5 - defaultURLTestURL = "https://www.gstatic.com/generate_204" ) type HealthCheckOption struct { @@ -105,12 +104,6 @@ func (hc *HealthCheck) registerHealthCheckTask(url string, expectedStatus utils. return } - // due to the time-consuming nature of health checks, a maximum of defaultMaxTestURLNum URLs can be set for testing - if len(hc.extra) > C.DefaultMaxHealthCheckUrlNum { - log.Debugln("skip add url: %s to health check because it has reached the maximum limit: %d", url, C.DefaultMaxHealthCheckUrlNum) - return - } - option := &extraOption{filters: map[string]struct{}{}, expectedStatus: expectedStatus} splitAndAddFiltersToExtra(filter, option) hc.extra[url] = option @@ -182,13 +175,8 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex } var filterReg *regexp2.Regexp - var store = C.OriginalHistory var expectedStatus utils.IntRanges[uint16] if option != nil { - if url != hc.url { - store = C.ExtraHistory - } - expectedStatus = option.expectedStatus if len(option.filters) != 0 { filters := make([]string, 0, len(option.filters)) @@ -213,7 +201,7 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout) defer cancel() log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid) - _, _ = p.URLTest(ctx, url, expectedStatus, store) + _, _ = p.URLTest(ctx, url, expectedStatus) log.Debugln("Health Checked, proxy: %s, url: %s, alive: %t, delay: %d ms uid: {%s}", p.Name(), url, p.AliveForTestUrl(url), p.LastDelayForTestUrl(url), uid) return false, nil }) @@ -228,7 +216,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, exp if len(url) == 0 { interval = 0 expectedStatus = nil - url = defaultURLTestURL + url = C.DefaultTestURL } return &HealthCheck{ diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index cd8b0e90..01ae44ee 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -48,12 +48,18 @@ type proxySetProvider struct { } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { + expectedStatus := "*" + if pp.healthCheck.expectedStatus != nil { + expectedStatus = pp.healthCheck.expectedStatus.ToString() + } + return json.Marshal(map[string]any{ "name": pp.Name(), "type": pp.Type().String(), "vehicleType": pp.VehicleType().String(), "proxies": pp.Proxies(), "testUrl": pp.healthCheck.url, + "expectedStatus": expectedStatus, "updatedAt": pp.UpdatedAt, "subscriptionInfo": pp.subscriptionInfo, }) @@ -214,12 +220,18 @@ type compatibleProvider struct { } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { + expectedStatus := "*" + if cp.healthCheck.expectedStatus != nil { + expectedStatus = cp.healthCheck.expectedStatus.ToString() + } + return json.Marshal(map[string]any{ - "name": cp.Name(), - "type": cp.Type().String(), - "vehicleType": cp.VehicleType().String(), - "proxies": cp.Proxies(), - "testUrl": cp.healthCheck.url, + "name": cp.Name(), + "type": cp.Type().String(), + "vehicleType": cp.VehicleType().String(), + "proxies": cp.Proxies(), + "testUrl": cp.healthCheck.url, + "expectedStatus": expectedStatus, }) } diff --git a/common/utils/ranges.go b/common/utils/ranges.go index 705bbdee..f36c22ec 100644 --- a/common/utils/ranges.go +++ b/common/utils/ranges.go @@ -75,3 +75,26 @@ func (ranges IntRanges[T]) Check(status T) bool { return false } + +func (ranges IntRanges[T]) ToString() string { + if len(ranges) == 0 { + return "*" + } + + terms := make([]string, len(ranges)) + for i, r := range ranges { + start := r.Start() + end := r.End() + + var term string + if start == end { + term = strconv.Itoa(int(start)) + } else { + term = strconv.Itoa(int(start)) + "-" + strconv.Itoa(int(end)) + } + + terms[i] = term + } + + return strings.Join(terms, "/") +} diff --git a/constant/adapters.go b/constant/adapters.go index 757068f3..68d4aa4e 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -43,11 +43,11 @@ const ( ) const ( - DefaultTCPTimeout = 5 * time.Second - DefaultDropTime = 12 * DefaultTCPTimeout - DefaultUDPTimeout = DefaultTCPTimeout - DefaultTLSTimeout = DefaultTCPTimeout - DefaultMaxHealthCheckUrlNum = 16 + DefaultTCPTimeout = 5 * time.Second + DefaultDropTime = 12 * DefaultTCPTimeout + DefaultUDPTimeout = DefaultTCPTimeout + DefaultTLSTimeout = DefaultTCPTimeout + DefaultTestURL = "https://cp.cloudflare.com/generate_204" ) var ErrNotSupport = errors.New("no support") @@ -149,21 +149,13 @@ type DelayHistory struct { type DelayHistoryStoreType int -const ( - OriginalHistory DelayHistoryStoreType = iota - ExtraHistory - DropHistory -) - type Proxy interface { ProxyAdapter - Alive() bool AliveForTestUrl(url string) bool DelayHistory() []DelayHistory ExtraDelayHistory() map[string][]DelayHistory - LastDelay() uint16 LastDelayForTestUrl(url string) uint16 - URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16], store DelayHistoryStoreType) (uint16, error) + URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (uint16, error) // Deprecated: use DialContext instead. Dial(metadata *Metadata) (Conn, error) diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 759e64d2..48359749 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -125,7 +125,7 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout)) defer cancel() - delay, err := proxy.URLTest(ctx, url, expectedStatus, C.ExtraHistory) + delay, err := proxy.URLTest(ctx, url, expectedStatus) if ctx.Err() != nil { render.Status(r, http.StatusGatewayTimeout) render.JSON(w, r, ErrRequestTimeout)