mirror of
https://github.com/MetaCubeX/Clash.Meta.git
synced 2025-04-11 04:50:56 +00:00
Merge 53b5c8e859
into 24a9ff6d03
This commit is contained in:
commit
518bb88c54
4 changed files with 373 additions and 0 deletions
|
@ -359,6 +359,15 @@ type RawTLS struct {
|
|||
CustomTrustCert []string `yaml:"custom-certifactes" json:"custom-certifactes"`
|
||||
}
|
||||
|
||||
type RawOverride struct {
|
||||
OS string `yaml:"os" json:"os"`
|
||||
Arch string `yaml:"arch" json:"arch"`
|
||||
Hostname string `yaml:"hostname" json:"hostname"`
|
||||
Username string `yaml:"username" json:"username"`
|
||||
ListStrategy ListMergeStrategy `yaml:"list-strategy" json:"list-strategy"`
|
||||
Content map[string]any `yaml:"content" json:"content"`
|
||||
}
|
||||
|
||||
type RawConfig struct {
|
||||
Port int `yaml:"port" json:"port"`
|
||||
SocksPort int `yaml:"socks-port" json:"socks-port"`
|
||||
|
@ -424,6 +433,7 @@ type RawConfig struct {
|
|||
GeoXUrl RawGeoXUrl `yaml:"geox-url" json:"geox-url"`
|
||||
Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
|
||||
TLS RawTLS `yaml:"tls" json:"tls"`
|
||||
Override []RawOverride `yaml:"override" json:"override"`
|
||||
|
||||
ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"`
|
||||
}
|
||||
|
@ -574,6 +584,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||
log.Infoln("Start initial configuration in progress") //Segment finished in xxm
|
||||
startTime := time.Now()
|
||||
|
||||
// apply overrides
|
||||
err := ApplyOverride(rawCfg, rawCfg.Override)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
general, err := parseGeneral(rawCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
84
config/override.go
Normal file
84
config/override.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type ListMergeStrategy string
|
||||
|
||||
// insert-front: [old slice] -> [new slice, old slice]
|
||||
// append: [old slice] -> [old slice, new slice]
|
||||
// override: [old slice] -> [new slice] (Default)
|
||||
|
||||
const (
|
||||
InsertFront ListMergeStrategy = "insert-front"
|
||||
Append ListMergeStrategy = "append"
|
||||
Override ListMergeStrategy = "override"
|
||||
Default ListMergeStrategy = ""
|
||||
)
|
||||
|
||||
func ApplyOverride(rawCfg *RawConfig, overrides []RawOverride) error {
|
||||
for id, override := range overrides {
|
||||
// check override conditions
|
||||
if override.OS != "" && override.OS != runtime.GOOS {
|
||||
continue
|
||||
}
|
||||
if override.Arch != "" && override.Arch != runtime.GOARCH {
|
||||
continue
|
||||
}
|
||||
if override.Hostname != "" {
|
||||
hName, err := os.Hostname()
|
||||
if err != nil {
|
||||
log.Warnln("Failed to get hostname when applying override #%v: %v", id, err)
|
||||
continue
|
||||
}
|
||||
if override.Hostname != hName {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if override.Username != "" {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
log.Warnln("Failed to get current user when applying override #%v: %v", id, err)
|
||||
continue
|
||||
}
|
||||
if override.Username != u.Username {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// marshal override content back to text
|
||||
overrideContent, err := yaml.Marshal(override.Content)
|
||||
if err != nil {
|
||||
log.Errorln("Error when applying override #%v: %v", id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshal override content into rawConfig, with custom list merge strategy
|
||||
switch override.ListStrategy {
|
||||
case Append:
|
||||
options := yaml.NewDecodeOptions().ListDecodeOption(yaml.ListDecodeAppend)
|
||||
err = yaml.UnmarshalWith(options, overrideContent, rawCfg)
|
||||
case InsertFront:
|
||||
options := yaml.NewDecodeOptions().ListDecodeOption(yaml.ListDecodeInsertFront)
|
||||
err = yaml.UnmarshalWith(options, overrideContent, rawCfg)
|
||||
case Override, Default:
|
||||
err = yaml.Unmarshal(overrideContent, rawCfg)
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf("Bad list strategy in override #%v: %v", id, override.ListStrategy))
|
||||
log.Errorln(err.Error())
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorln("Error when applying override #%v: %v", id, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
271
config/override_test.go
Normal file
271
config/override_test.go
Normal file
|
@ -0,0 +1,271 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMihomo_Config_Override(t *testing.T) {
|
||||
t.Run("override_existing", func(t *testing.T) {
|
||||
config_file := `
|
||||
mixed-port: 7890
|
||||
ipv6: true
|
||||
log-level: debug
|
||||
allow-lan: false
|
||||
unified-delay: false
|
||||
tcp-concurrent: true
|
||||
external-controller: 127.0.0.1:9090
|
||||
default-nameserver:
|
||||
- "223.5.5.5"
|
||||
override:
|
||||
- content:
|
||||
external-controller: 0.0.0.0:9090
|
||||
allow-lan: true`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, true, cfg.General.AllowLan)
|
||||
assert.Equal(t, "0.0.0.0:9090", cfg.Controller.ExternalController)
|
||||
})
|
||||
|
||||
t.Run("override_zero_value_test", func(t *testing.T) {
|
||||
config_file := `
|
||||
mixed-port: 7890
|
||||
ipv6: true
|
||||
log-level: debug
|
||||
allow-lan: true
|
||||
unified-delay: false
|
||||
tcp-concurrent: true
|
||||
external-controller: 127.0.0.1:9090
|
||||
default-nameserver:
|
||||
- "223.5.5.5"
|
||||
override:
|
||||
- content:
|
||||
external-controller: ""
|
||||
allow-lan: false`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, false, cfg.General.AllowLan)
|
||||
assert.Equal(t, "", cfg.Controller.ExternalController)
|
||||
})
|
||||
|
||||
t.Run("add_new", func(t *testing.T) {
|
||||
config_file := `
|
||||
mixed-port: 7890
|
||||
ipv6: true
|
||||
log-level: debug
|
||||
unified-delay: false
|
||||
tcp-concurrent: true
|
||||
override:
|
||||
- content:
|
||||
external-controller: 0.0.0.0:9090
|
||||
- content:
|
||||
allow-lan: true`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, true, cfg.General.AllowLan)
|
||||
assert.Equal(t, "0.0.0.0:9090", cfg.Controller.ExternalController)
|
||||
})
|
||||
|
||||
t.Run("conditions", func(t *testing.T) {
|
||||
hName, err := os.Hostname()
|
||||
assert.NoError(t, err)
|
||||
u, err := user.Current()
|
||||
assert.NoError(t, err)
|
||||
|
||||
config_file := fmt.Sprintf(`
|
||||
mixed-port: 7890
|
||||
ipv6: true
|
||||
log-level: debug
|
||||
allow-lan: false
|
||||
unified-delay: false
|
||||
tcp-concurrent: true
|
||||
external-controller: 127.0.0.1:9090
|
||||
default-nameserver:
|
||||
- "223.5.5.5"
|
||||
override:
|
||||
- os: %v
|
||||
arch: %v
|
||||
hostname: %v
|
||||
username: %v
|
||||
content:
|
||||
external-controller: 0.0.0.0:9090
|
||||
allow-lan: true`, runtime.GOOS, runtime.GOARCH, hName, u.Username)
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, true, cfg.General.AllowLan)
|
||||
assert.Equal(t, "0.0.0.0:9090", cfg.Controller.ExternalController)
|
||||
})
|
||||
|
||||
t.Run("invalid_condition", func(t *testing.T) {
|
||||
config_file := `
|
||||
mixed-port: 7890
|
||||
log-level: debug
|
||||
ipv6: true
|
||||
allow-lan: false
|
||||
unified-delay: false
|
||||
tcp-concurrent: true
|
||||
external-controller: 127.0.0.1:9090
|
||||
override:
|
||||
- os: lw2eiru20f923j
|
||||
content:
|
||||
external-controller: 0.0.0.0:9090
|
||||
- arch: 32of9u8p3jrp
|
||||
content:
|
||||
allow-lan: true`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, false, cfg.General.AllowLan)
|
||||
assert.Equal(t, "127.0.0.1:9090", cfg.Controller.ExternalController)
|
||||
})
|
||||
|
||||
t.Run("list_insert_front", func(t *testing.T) {
|
||||
config_file := `
|
||||
log-level: debug
|
||||
rules:
|
||||
- DOMAIN-SUFFIX,foo.com,DIRECT
|
||||
- DOMAIN-SUFFIX,bar.org,DIRECT
|
||||
- DOMAIN-SUFFIX,bazz.io,DIRECT
|
||||
override:
|
||||
- list-strategy: insert-front
|
||||
content:
|
||||
rules:
|
||||
- GEOIP,lan,DIRECT,no-resolve`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, 4, len(cfg.Rules))
|
||||
assert.Equal(t, constant.GEOIP, cfg.Rules[0].RuleType())
|
||||
assert.Equal(t, false, cfg.Rules[0].ShouldResolveIP())
|
||||
})
|
||||
|
||||
t.Run("list_append", func(t *testing.T) {
|
||||
config_file := `
|
||||
log-level: debug
|
||||
rules:
|
||||
- DOMAIN-SUFFIX,foo.com,DIRECT
|
||||
- DOMAIN-SUFFIX,bar.org,DIRECT
|
||||
- DOMAIN-SUFFIX,bazz.io,DIRECT
|
||||
override:
|
||||
- list-strategy: append
|
||||
content:
|
||||
rules:
|
||||
- GEOIP,lan,DIRECT,no-resolve`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Equal(t, 4, len(cfg.Rules))
|
||||
assert.Equal(t, constant.GEOIP, cfg.Rules[3].RuleType())
|
||||
assert.Equal(t, false, cfg.Rules[3].ShouldResolveIP())
|
||||
})
|
||||
|
||||
t.Run("list_override", func(t *testing.T) {
|
||||
config_file := `
|
||||
log-level: debug
|
||||
proxies:
|
||||
- name: "DIRECT-PROXY"
|
||||
type: direct
|
||||
udp: true
|
||||
- name: "SOCKS-PROXY"
|
||||
type: socks5
|
||||
server: foo.com
|
||||
port: 443
|
||||
override:
|
||||
- list-strategy: override
|
||||
content:
|
||||
proxies:
|
||||
- name: "HTTP-PROXY"
|
||||
type: http
|
||||
server: bar.org
|
||||
port: 443`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.NotContains(t, cfg.Proxies, "DIRECT-PROXY")
|
||||
assert.NotContains(t, cfg.Proxies, "SOCKS-PROXY")
|
||||
assert.Contains(t, cfg.Proxies, "HTTP-PROXY")
|
||||
assert.Equal(t, constant.Http, cfg.Proxies["HTTP-PROXY"].Type())
|
||||
})
|
||||
|
||||
t.Run("map_merge", func(t *testing.T) {
|
||||
config_file := `
|
||||
log-level: debug
|
||||
proxy-providers:
|
||||
provider1:
|
||||
url: "foo.com"
|
||||
type: http
|
||||
interval: 86400
|
||||
health-check: {enable: true,url: "https://www.gstatic.com/generate_204", interval: 300}
|
||||
provider2:
|
||||
url: "bar.com"
|
||||
type: http
|
||||
interval: 86400
|
||||
health-check: {enable: true,url: "https://www.gstatic.com/generate_204", interval: 300}
|
||||
override:
|
||||
- content:
|
||||
proxy-providers:
|
||||
provider3:
|
||||
url: "buzz.com"
|
||||
type: http
|
||||
interval: 86400
|
||||
health-check: {enable: true,url: "https://www.google.com", interval: 300}`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
cfg, err := ParseRawConfig(rawCfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, log.DEBUG, cfg.General.LogLevel)
|
||||
assert.Contains(t, cfg.Providers, "provider1")
|
||||
assert.Contains(t, cfg.Providers, "provider2")
|
||||
assert.Contains(t, cfg.Providers, "provider3")
|
||||
assert.Equal(t, "https://www.google.com", cfg.Providers["provider3"].HealthCheckURL())
|
||||
})
|
||||
|
||||
t.Run("bad_override", func(t *testing.T) {
|
||||
config_file := `
|
||||
mixed-port: 7890
|
||||
ipv6: true
|
||||
log-level: debug
|
||||
allow-lan: false
|
||||
unified-delay: false
|
||||
tcp-concurrent: true
|
||||
external-controller: 127.0.0.1:9090
|
||||
default-nameserver:
|
||||
- "223.5.5.5"
|
||||
override:
|
||||
- list-strategy: 12wlfiu3o
|
||||
content:
|
||||
external-controller: 0.0.0.0:9090
|
||||
allow-lan: true`
|
||||
rawCfg, err := UnmarshalRawConfig([]byte(config_file))
|
||||
assert.NoError(t, err)
|
||||
_, err = ParseRawConfig(rawCfg)
|
||||
assert.True(t, strings.HasPrefix(err.Error(), "Bad list strategy in override #0:"))
|
||||
})
|
||||
}
|
2
go.sum
2
go.sum
|
@ -1,3 +1,5 @@
|
|||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08=
|
||||
github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY=
|
||||
github.com/RyuaNerin/elliptic2 v1.0.0/go.mod h1:wWB8fWrJI/6EPJkyV/r1Rj0hxUgrusmqSj8JN6yNf/A=
|
||||
|
|
Loading…
Add table
Reference in a new issue