Conversation
…ng on some groups
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The in-app delay test (the ⚡ button on the proxy page) is broken on top of current mihomo in two
independent ways:
tested successfully frequently show no value at all, and the values change on every refresh.
GLOBALgroup, anduse:-only groups whose providers have nohealth-check.url. Nothing is tested, no delay isever shown.
This patch makes the app resolve the test URL itself, run the test on the group's members with that
URL, and read the delay back with the same URL.
Root cause
1. The delay is looked up with a random test URL
mihomo keeps one delay history per test URL (
extraDelayHistories) andLastDelayForTestUrl(url)returns
0xffffunless the last test of that exact URL was alive(
adapter/adapter.go):LastDelay()was removed fromconstant.Proxy(cc642972, implementation dropped inf63acc02),so there is no URL-independent delay any more.
core/src/main/golang/native/tunnel/proxies.gotherefore had to pick a URL — and picked it by iterating a Go map, whose iteration order is
randomized on every call, with the gstatic URL hard-coded as the fallback:
As soon as a proxy has been tested with more than one URL — very common, e.g.
proxy-providers.<x>.health-check.urldiffering from a group'surl:, or one of those URLs beingblocked/hijacked for that node — the picked URL is a coin flip. Anything above
Short.MAX_VALUEisrendered as an empty string by
ProxyViewState(if (proxy.delay in 0..Short.MAX_VALUE) ... else ""),so online nodes show blank latency at random, and the result differs on every refresh.
2.
provider.HealthCheck()silently does nothing when the provider has no test URLHealthCheck()inconnectivity.goonly fanned out to the providers:mihomo builds the reserved
defaultprovider — the only provider behind the auto-createdGLOBALgroup — with an empty health check URL, and it can never get a registered URL (
use:rejectsVehicleType() == Compatible, and it is created after the groups are parsed):HealthCheck.execute()returns early for an empty URL, so zero proxies are tested:The same happens for
use:-only groups without an expliciturl:when the provider has nohealth-check.url, becauseaddTestUrlToProviders()is only reached in theelsebranch ofadapter/outboundgroup/parser.go.How to reproduce
Before this patch
proxy-providersentry withhealth-check.url: https://…while some group usesurl: http://…— or a profile whose group provider has no test URL at all (ause:-only groupwithout
url:, orGLOBALin global mode).above pressing ⚡ does nothing whatsoever. With
log-level: debug, logcat only showsHealth Check has been skipped due to testUrl is empty.After this patch
stay stable across refreshes;
GLOBALworks as well. Logcat showsHealth checking group '<name>' with url '<url>' (N proxies).Changes
core/src/main/golang/native/tunnel/connectivity.gogroupTestURL: the group-owned provider'sHealthCheckURL(), then the first provider with a URL, finallyC.DefaultTestURL) and callproxy.URLTest(ctx, url, nil)for every member (concurrency 10, 5s timeout per proxy) instead of relying onprovider.HealthCheck()core/src/main/golang/native/tunnel/proxies.golastDelay()asks for the group's own URL first and only then falls back to any alive URL; no more random map pick.QueryProxyGroup()passes the resolved URL down toconvertProxies()core/src/main/golang/native/tunnel.gonativeHealthCheck(C.release_object()), matchingload()/updateProvider()— the previous code leaked one global ref per button pressapp/src/main/java/com/github/kr328/clash/ProxyActivity.ktdesign/src/main/java/com/github/kr328/clash/design/ProxyDesign.ktfinishUrlTesting(position)used by that failure path (guards against a missing adapter / out-of-range index)Total: 5 files, +142/−31.
Verification
Build Debugworkflow in a fork (Go 1.26 + the NDK/JDK that CIprovisions, all four ABIs): run succeeded, 5 APKs uploaded.
https://github.com/Code-0-0/ClashMetaForAndroid/actions/runs/35441174195
refreshes (previously they flickered away);
GLOBALgroup in global mode now performs a real test instead of doing nothing;Health checking group '<name>' with url '<url>' (N proxies)and no longerHealth Check has been skipped due to testUrl is empty.Notes for reviewers
the delay history with the periodic health check of the same group/provider.
expected-statusis not reachable through the public provider API, so a manual test passesnilfor
expectedStatus— the same behaviour as the REST API's/group/:name/delaywithoutexpected=. The periodic health check still applies the configured status.provider.HealthCheck()defaults.