fix(core): send changed geo resource URLs to the Core without a full apply
The Core only learned geox-url from a full config apply, so after a link was edited on the resources page both a manual sync and the auto updater kept downloading from the previous link until the profile was applied again. The URLs now travel with the update params, which are pushed whenever the patch config changes. Only a link that changed is written, because mihomo's updaters read these links without a lock. Changelog: A changed resource link is now used by the next sync and by auto update
This commit is contained in:
@@ -318,6 +318,9 @@ func updateConfig(params *UpdateParams) error {
|
||||
}
|
||||
|
||||
updateListeners(currentConfig)
|
||||
for geoType, link := range params.GeoXUrl {
|
||||
setGeoResourceUrl(geoType, link)
|
||||
}
|
||||
syncGeoUpdater(params.GeoAutoUpdate, params.GeoUpdateInterval)
|
||||
if routeChanged {
|
||||
bumpRouteEpoch()
|
||||
|
||||
+56
-4
@@ -122,6 +122,55 @@ func TestUpdateConfigAppliesAllowLan(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateConfigAppliesGeoResourceUrls(t *testing.T) {
|
||||
withCurrentConfig(t, &config.Config{General: &config.General{}, Controller: &config.Controller{}})
|
||||
requested := map[string]string{
|
||||
"mmdb": "https://example.test/geoip.metadb",
|
||||
"asn": "https://example.test/GeoLite2-ASN.mmdb",
|
||||
"geoip": "https://example.test/geoip.dat",
|
||||
"geosite": "https://example.test/geosite.dat",
|
||||
}
|
||||
for _, resource := range geoResources {
|
||||
resource, previous := resource, resource.url()
|
||||
t.Cleanup(func() { resource.setUrl(previous) })
|
||||
}
|
||||
|
||||
if err := updateConfig(&UpdateParams{GeoXUrl: requested}); err != nil {
|
||||
t.Fatalf("updateConfig error: %v", err)
|
||||
}
|
||||
|
||||
for key, want := range requested {
|
||||
if got := geoResources[strings.ToUpper(key)].url(); got != want {
|
||||
t.Errorf("%s url = %q, want %q; sync and the auto updater would keep the old link", key, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetGeoResourceUrlWritesOnlyAChangedLink(t *testing.T) {
|
||||
const current = "https://example.test/geosite.dat"
|
||||
var writes []string
|
||||
previous := geoResources
|
||||
geoResources = map[string]geoResource{
|
||||
"GEOSITE": {
|
||||
url: func() string { return current },
|
||||
setUrl: func(link string) { writes = append(writes, link) },
|
||||
},
|
||||
}
|
||||
t.Cleanup(func() { geoResources = previous })
|
||||
|
||||
setGeoResourceUrl("geosite", current)
|
||||
setGeoResourceUrl("geosite", "")
|
||||
setGeoResourceUrl("nope", "https://example.test/nope.dat")
|
||||
if len(writes) != 0 {
|
||||
t.Fatalf("writes = %q, want none for an unchanged, empty or unknown link", writes)
|
||||
}
|
||||
|
||||
setGeoResourceUrl("geosite", "https://example.test/next.dat")
|
||||
if len(writes) != 1 || writes[0] != "https://example.test/next.dat" {
|
||||
t.Errorf("writes = %q, want the changed link once", writes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateConfigAppliesAuthenticationAndClearsLoopbackExemptions(t *testing.T) {
|
||||
withCurrentConfig(t, &config.Config{General: &config.General{}, Controller: &config.Controller{}})
|
||||
currentConfig.General.SkipAuthPrefixes = []netip.Prefix{
|
||||
@@ -359,7 +408,7 @@ func TestHandleUpdateGeoDataRejectsAnUnknownResource(t *testing.T) {
|
||||
if message := handleUpdateGeoData("NOPE"); message == "" {
|
||||
t.Error("an unknown geo resource reported success and silently did nothing")
|
||||
}
|
||||
if _, exists := geoResourceUpdaters["MMDB"]; !exists {
|
||||
if _, exists := geoResources["MMDB"]; !exists {
|
||||
t.Error("MMDB is missing from the geo resource table")
|
||||
}
|
||||
}
|
||||
@@ -415,10 +464,13 @@ func settleMessageBatcher() {
|
||||
|
||||
func withGeoResourceUpdaters(t *testing.T, updaters map[string]func() error) {
|
||||
t.Helper()
|
||||
previous := geoResourceUpdaters
|
||||
geoResourceUpdaters = updaters
|
||||
previous := geoResources
|
||||
geoResources = make(map[string]geoResource, len(updaters))
|
||||
for name, update := range updaters {
|
||||
geoResources[name] = geoResource{update: update}
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
geoResourceUpdaters = previous
|
||||
geoResources = previous
|
||||
for name := range updaters {
|
||||
releaseGeoUpdate(name)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type UpdateParams struct {
|
||||
Authentication *[]string `json:"authentication"`
|
||||
GeoAutoUpdate *bool `json:"geo-auto-update"`
|
||||
GeoUpdateInterval *int `json:"geo-update-interval"`
|
||||
GeoXUrl map[string]string `json:"geox-url"`
|
||||
}
|
||||
|
||||
type tunSchema struct {
|
||||
|
||||
+26
-7
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/metacubex/mihomo/adapter"
|
||||
"github.com/metacubex/mihomo/common/observable"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/geodata"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
@@ -428,11 +429,29 @@ func handleGetExternalProvider(externalProviderName string) *ExternalProvider {
|
||||
return externalProvider
|
||||
}
|
||||
|
||||
var geoResourceUpdaters = map[string]func() error{
|
||||
"MMDB": updater.UpdateMMDB,
|
||||
"ASN": updater.UpdateASN,
|
||||
"GEOIP": updater.UpdateGeoIp,
|
||||
"GEOSITE": updater.UpdateGeoSite,
|
||||
type geoResource struct {
|
||||
update func() error
|
||||
url func() string
|
||||
setUrl func(string)
|
||||
}
|
||||
|
||||
var geoResources = map[string]geoResource{
|
||||
"MMDB": {update: updater.UpdateMMDB, url: geodata.MmdbUrl, setUrl: geodata.SetMmdbUrl},
|
||||
"ASN": {update: updater.UpdateASN, url: geodata.ASNUrl, setUrl: geodata.SetASNUrl},
|
||||
"GEOIP": {update: updater.UpdateGeoIp, url: geodata.GeoIpUrl, setUrl: geodata.SetGeoIpUrl},
|
||||
"GEOSITE": {update: updater.UpdateGeoSite, url: geodata.GeoSiteUrl, setUrl: geodata.SetGeoSiteUrl},
|
||||
}
|
||||
|
||||
// mihomo's updaters read these links without a lock, so an unchanged one is never rewritten.
|
||||
func setGeoResourceUrl(geoType string, link string) {
|
||||
resource, exist := geoResources[strings.ToUpper(geoType)]
|
||||
if !exist {
|
||||
log.Warnln("geox-url: unknown geo resource %q", geoType)
|
||||
return
|
||||
}
|
||||
if link != "" && link != resource.url() {
|
||||
resource.setUrl(link)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -494,7 +513,7 @@ func releaseGeoUpdateFromHook(geoType string) {
|
||||
}
|
||||
|
||||
func handleUpdateGeoData(geoType string) string {
|
||||
update, exist := geoResourceUpdaters[geoType]
|
||||
resource, exist := geoResources[geoType]
|
||||
if !exist {
|
||||
logError("updateGeoData: unknown geo resource %q", geoType)
|
||||
return "unknown geo resource: " + geoType
|
||||
@@ -504,7 +523,7 @@ func handleUpdateGeoData(geoType string) string {
|
||||
}
|
||||
safeGoDetached("updateGeoData("+geoType+")", func() {
|
||||
defer releaseGeoUpdate(geoType)
|
||||
if err := update(); err != nil {
|
||||
if err := resource.update(); err != nil {
|
||||
logError("updateGeoData(%s) error: %v", geoType, err)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user