diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index b39ee3a659..0042f929a4 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -12,7 +12,6 @@ import ( "github.com/metacubex/mihomo/common/utils" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/provider" - types "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel" @@ -31,7 +30,7 @@ type GroupBase struct { failedTesting atomic.Bool proxies [][]C.Proxy versions []atomic.Uint32 - TestTimeout int + TestTimeout string maxFailedTimes int } @@ -40,7 +39,7 @@ type GroupBaseOption struct { filter string excludeFilter string excludeType string - TestTimeout int + TestTimeout string maxFailedTimes int providers []provider.ProxyProvider } @@ -74,8 +73,8 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase { maxFailedTimes: opt.maxFailedTimes, } - if gb.TestTimeout == 0 { - gb.TestTimeout = 5000 + if gb.TestTimeout == "" { + gb.TestTimeout = "5000" } if gb.maxFailedTimes == 0 { gb.maxFailedTimes = 5 @@ -108,7 +107,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { pd.Touch() } - if pd.VehicleType() == types.Compatible { + if pd.VehicleType() == provider.Compatible { gb.versions[i].Store(pd.Version()) gb.proxies[i] = pd.Proxies() continue @@ -244,6 +243,11 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) { return } + var timeout time.Duration + if gb.TestTimeout != "" { + timeout = utils.ParseDuration(gb.TestTimeout, "ms") + } + go func() { gb.failedTestMux.Lock() defer gb.failedTestMux.Unlock() @@ -253,7 +257,7 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) { log.Debugln("ProxyGroup: %s first failed", gb.Name()) gb.failedTime = time.Now() } else { - if time.Since(gb.failedTime) > time.Duration(gb.TestTimeout)*time.Millisecond { + if time.Since(gb.failedTime) > timeout { gb.failedTimes = 0 return } diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 876c92fa32..e1bd31a3f0 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -27,8 +27,8 @@ type GroupCommonOption struct { Proxies []string `group:"proxies,omitempty"` Use []string `group:"use,omitempty"` URL string `group:"url,omitempty"` - Interval int `group:"interval,omitempty"` - TestTimeout int `group:"timeout,omitempty"` + Interval string `group:"interval,omitempty"` + TestTimeout string `group:"timeout,omitempty"` MaxFailedTimes int `group:"max-failed-times,omitempty"` Lazy bool `group:"lazy,omitempty"` DisableUDP bool `group:"disable-udp,omitempty"` @@ -88,6 +88,17 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide } groupOption.ExpectedStatus = status + var ( + interval uint + timeout uint + ) + if groupOption.Interval != "" { + interval = uint(utils.ParseDuration(groupOption.Interval, "s").Seconds()) + } + if groupOption.TestTimeout != "" { + timeout = uint(utils.ParseDuration(groupOption.TestTimeout, "ms").Milliseconds()) + } + if len(groupOption.Use) != 0 { PDs, err := getProviders(providersMap, groupOption.Use) if err != nil { @@ -106,7 +117,7 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide groupOption.URL = C.DefaultTestURL } } else { - addTestUrlToProviders(PDs, groupOption.URL, expectedStatus, groupOption.Filter, uint(groupOption.Interval)) + addTestUrlToProviders(PDs, groupOption.URL, expectedStatus, groupOption.Filter, interval) } providers = append(providers, PDs...) } @@ -127,12 +138,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide // select don't need auto health check if groupOption.Type != "select" && groupOption.Type != "relay" { - if groupOption.Interval == 0 { - groupOption.Interval = 300 + if interval == 0 { + interval = 300 } } - hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus) + hc := provider.NewHealthCheck(ps, groupOption.URL, timeout, interval, groupOption.Lazy, expectedStatus) pd, err := provider.NewCompatibleProvider(groupName, ps, hc) if err != nil { diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 07fbcd9588..4128a4b309 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -160,7 +160,7 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re "", "", "", - 5000, + "5000", 5, providers, }), diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 1094668d46..780a4c743a 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -21,8 +21,8 @@ var ( type healthCheckSchema struct { Enable bool `provider:"enable"` URL string `provider:"url"` - Interval int `provider:"interval"` - TestTimeout int `provider:"timeout,omitempty"` + Interval string `provider:"interval"` + TestTimeout string `provider:"timeout,omitempty"` Lazy bool `provider:"lazy,omitempty"` ExpectedStatus string `provider:"expected-status,omitempty"` } @@ -45,7 +45,7 @@ type proxyProviderSchema struct { Path string `provider:"path,omitempty"` URL string `provider:"url,omitempty"` Proxy string `provider:"proxy,omitempty"` - Interval int `provider:"interval,omitempty"` + Interval string `provider:"interval,omitempty"` Filter string `provider:"filter,omitempty"` ExcludeFilter string `provider:"exclude-filter,omitempty"` ExcludeType string `provider:"exclude-type,omitempty"` @@ -73,14 +73,27 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide return nil, err } - var hcInterval uint + var ( + interval time.Duration + hcInterval uint + timeout uint + ) + if schema.Interval != "" { + interval = utils.ParseDuration(schema.Interval, "s") + } if schema.HealthCheck.Enable { - if schema.HealthCheck.Interval == 0 { - schema.HealthCheck.Interval = 300 + if schema.HealthCheck.Interval != "" { + hcInterval = uint(utils.ParseDuration(schema.HealthCheck.Interval, "s").Seconds()) + } + if hcInterval == 0 { + hcInterval = 300 } - hcInterval = uint(schema.HealthCheck.Interval) } - hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, uint(schema.HealthCheck.TestTimeout), hcInterval, schema.HealthCheck.Lazy, expectedStatus) + if schema.HealthCheck.TestTimeout != "" { + timeout = uint(utils.ParseDuration(schema.HealthCheck.TestTimeout, "ms").Milliseconds()) + } + + hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, timeout, hcInterval, schema.HealthCheck.Lazy, expectedStatus) var vehicle types.Vehicle switch schema.Type { @@ -100,7 +113,6 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) } - interval := time.Duration(uint(schema.Interval)) * time.Second filter := schema.Filter excludeFilter := schema.ExcludeFilter excludeType := schema.ExcludeType diff --git a/common/net/tcpip.go b/common/net/tcpip.go index 0499e54c17..65a67b9912 100644 --- a/common/net/tcpip.go +++ b/common/net/tcpip.go @@ -50,6 +50,7 @@ func SplitHostPort(s string) (host, port string, hasPort bool, err error) { func TCPKeepAlive(c net.Conn) { if tcp, ok := c.(*net.TCPConn); ok { + fmt.Println(KeepAliveInterval) _ = tcp.SetKeepAlive(true) _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) } diff --git a/common/utils/time.go b/common/utils/time.go new file mode 100644 index 0000000000..5a68409a7d --- /dev/null +++ b/common/utils/time.go @@ -0,0 +1,31 @@ +package utils + +import ( + "strconv" + "time" +) + +func ParseDuration(interval string, unit string) time.Duration { + var Duration time.Duration + switch unit { + case "ms": + _, err := strconv.Atoi(interval) + if err == nil { + interval += "ms" + } + Duration, _ = time.ParseDuration(interval) + case "s": + _, err := strconv.Atoi(interval) + if err == nil { + interval += "s" + } + Duration, _ = time.ParseDuration(interval) + case "h": + _, err := strconv.Atoi(interval) + if err == nil { + interval += "h" + } + Duration, _ = time.ParseDuration(interval) + } + return Duration +} diff --git a/component/geodata/utils.go b/component/geodata/utils.go index 981d7eba4f..9b1063c837 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -15,7 +15,7 @@ import ( var ( geoMode bool AutoUpdate bool - UpdateInterval int + UpdateInterval string geoLoaderName = "memconservative" geoSiteMatcher = "succinct" ) @@ -30,7 +30,7 @@ func GeoAutoUpdate() bool { return AutoUpdate } -func GeoUpdateInterval() int { +func GeoUpdateInterval() string { return UpdateInterval } @@ -48,7 +48,7 @@ func SetGeodataMode(newGeodataMode bool) { func SetGeoAutoUpdate(newAutoUpdate bool) { AutoUpdate = newAutoUpdate } -func SetGeoUpdateInterval(newGeoUpdateInterval int) { +func SetGeoUpdateInterval(newGeoUpdateInterval string) { UpdateInterval = newGeoUpdateInterval } @@ -144,7 +144,7 @@ func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, int, error) { /** linear: linear algorithm matcher, err := router.NewDomainMatcher(domains) - mph:minimal perfect hash algorithm + mph: minimal perfect hash algorithm */ var matcher router.DomainMatcher if geoSiteMatcher == "mph" { diff --git a/config/config.go b/config/config.go index c5c4fa88f4..6117503cee 100644 --- a/config/config.go +++ b/config/config.go @@ -56,7 +56,7 @@ type General struct { RoutingMark int `json:"-"` GeoXUrl GeoXUrl `json:"geox-url"` GeoAutoUpdate bool `json:"geo-auto-update"` - GeoUpdateInterval int `json:"geo-update-interval"` + GeoUpdateInterval string `json:"geo-update-interval"` GeodataMode bool `json:"geodata-mode"` GeodataLoader string `json:"geodata-loader"` GeositeMatcher string `json:"geosite-matcher"` @@ -313,7 +313,7 @@ type RawConfig struct { RoutingMark int `yaml:"routing-mark"` Tunnels []LC.Tunnel `yaml:"tunnels"` GeoAutoUpdate bool `yaml:"geo-auto-update" json:"geo-auto-update"` - GeoUpdateInterval int `yaml:"geo-update-interval" json:"geo-update-interval"` + GeoUpdateInterval string `yaml:"geo-update-interval" json:"geo-update-interval"` GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"` GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"` GeositeMatcher string `yaml:"geosite-matcher" json:"geosite-matcher"` @@ -321,7 +321,7 @@ type RawConfig struct { FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint"` GlobalUA string `yaml:"global-ua"` - KeepAliveInterval int `yaml:"keep-alive-interval"` + KeepAliveInterval string `yaml:"keep-alive-interval"` Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` @@ -401,7 +401,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { IPv6: true, Mode: T.Rule, GeoAutoUpdate: false, - GeoUpdateInterval: 24, + GeoUpdateInterval: "24h", GeodataMode: C.GeodataMode, GeodataLoader: "memconservative", UnifiedDelay: false, @@ -631,8 +631,8 @@ func parseGeneral(cfg *RawConfig) (*General, error) { C.ASNUrl = cfg.GeoXUrl.ASN C.GeodataMode = cfg.GeodataMode C.UA = cfg.GlobalUA - if cfg.KeepAliveInterval != 0 { - N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second + if cfg.KeepAliveInterval != "" { + N.KeepAliveInterval = utils.ParseDuration(cfg.KeepAliveInterval, "s") } ExternalUIPath = cfg.ExternalUI diff --git a/constant/geodata.go b/constant/geodata.go index cd3f74e30c..35a63257e5 100644 --- a/constant/geodata.go +++ b/constant/geodata.go @@ -4,7 +4,7 @@ var ( ASNEnable bool GeodataMode bool GeoAutoUpdate bool - GeoUpdateInterval int + GeoUpdateInterval string GeoIpUrl string MmdbUrl string GeoSiteUrl string diff --git a/main.go b/main.go index 748fa2e30c..85329a4dde 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "syscall" "time" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" @@ -111,9 +112,9 @@ func main() { } if C.GeoAutoUpdate { - ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) + ticker := time.NewTicker(utils.ParseDuration(C.GeoUpdateInterval, "h")) - log.Infoln("[GEO] Start update GEO database every %d hours", C.GeoUpdateInterval) + log.Infoln("[GEO] Start update GEO database every %s", utils.ParseDuration(C.GeoUpdateInterval, "h")) go func() { for range ticker.C { updateGeoDatabases() diff --git a/rules/provider/parse.go b/rules/provider/parse.go index a20da28d5c..4e82d70d45 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -6,6 +6,7 @@ import ( "time" "github.com/metacubex/mihomo/common/structure" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" @@ -23,7 +24,7 @@ type ruleProviderSchema struct { URL string `provider:"url,omitempty"` Proxy string `provider:"proxy,omitempty"` Format string `provider:"format,omitempty"` - Interval int `provider:"interval,omitempty"` + Interval string `provider:"interval,omitempty"` } func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (P.RuleProvider, error) { @@ -56,6 +57,11 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t return nil, fmt.Errorf("unsupported format type: %s", schema.Format) } + var interval time.Duration + if schema.Interval != "" { + interval = utils.ParseDuration(schema.Interval, "s") + } + var vehicle P.Vehicle switch schema.Type { case "file": @@ -74,5 +80,5 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } - return NewRuleSetProvider(name, behavior, format, time.Duration(uint(schema.Interval))*time.Second, vehicle, parse), nil + return NewRuleSetProvider(name, behavior, format, interval, vehicle, parse), nil }