From c0a2f772582efe42cb355627578331e69d4bd52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 28 Jul 2022 16:36:31 +0800 Subject: [PATCH] Remove urltest outbound --- adapter/experimental.go | 7 + adapter/router.go | 2 - constant/proxy.go | 1 - docs/configuration/outbound/index.md | 1 - docs/configuration/outbound/urltest.md | 41 ---- experimental/clashapi/proxies.go | 9 +- experimental/clashapi/server.go | 3 + mkdocs.yml | 1 - option/clash.go | 7 - option/outbound.go | 5 - outbound/builder.go | 2 - outbound/selector.go | 7 + outbound/urltest.go | 249 ------------------------- route/router.go | 9 - 14 files changed, 20 insertions(+), 324 deletions(-) delete mode 100644 docs/configuration/outbound/urltest.md delete mode 100644 outbound/urltest.go diff --git a/adapter/experimental.go b/adapter/experimental.go index cb2b3b34..bdbf941a 100644 --- a/adapter/experimental.go +++ b/adapter/experimental.go @@ -25,3 +25,10 @@ type OutboundGroup interface { Now() string All() []string } + +func OutboundTag(detour Outbound) string { + if group, isGroup := detour.(OutboundGroup); isGroup { + return group.Now() + } + return detour.Tag() +} diff --git a/adapter/router.go b/adapter/router.go index ad667636..e6744822 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -6,7 +6,6 @@ import ( "net/netip" "github.com/sagernet/sing-box/common/geoip" - "github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/control" N "github.com/sagernet/sing/common/network" @@ -40,7 +39,6 @@ type Router interface { Rules() []Rule SetTrafficController(controller TrafficController) - URLTestHistoryStorage(create bool) *urltest.HistoryStorage } type Rule interface { diff --git a/constant/proxy.go b/constant/proxy.go index 43e39671..f2be807e 100644 --- a/constant/proxy.go +++ b/constant/proxy.go @@ -16,5 +16,4 @@ const ( const ( TypeSelector = "selector" - TypeURLTest = "urltest" ) diff --git a/docs/configuration/outbound/index.md b/docs/configuration/outbound/index.md index bd547acc..dad87189 100644 --- a/docs/configuration/outbound/index.md +++ b/docs/configuration/outbound/index.md @@ -22,7 +22,6 @@ | `shadowsocks` | [Shadowsocks](./shadowsocks) | | `dns` | [DNS](./dns) | | `selector` | [Selector](./selector) | -| `urltest` | [URLTest](./urltest) | #### tag diff --git a/docs/configuration/outbound/urltest.md b/docs/configuration/outbound/urltest.md deleted file mode 100644 index b960f757..00000000 --- a/docs/configuration/outbound/urltest.md +++ /dev/null @@ -1,41 +0,0 @@ -### Structure - -```json -{ - "outbounds": [ - { - "type": "urltest", - "tag": "auto", - - "outbounds": [ - "proxy-a", - "proxy-b", - "proxy-c" - ], - "url": "http://www.gstatic.com/generate_204", - "interval": "1m", - "tolerance": 50 - } - ] -} -``` - -### Fields - -#### outbounds - -==Required== - -List of outbound tags to test. - -#### url - -The URL to test. `http://www.gstatic.com/generate_204` will be used if empty. - -#### interval - -The test interval. `1m` will be used if empty. - -#### tolerance - -The test tolerance in milliseconds. `50` will be used if empty. diff --git a/experimental/clashapi/proxies.go b/experimental/clashapi/proxies.go index 6d8be6bb..0324d448 100644 --- a/experimental/clashapi/proxies.go +++ b/experimental/clashapi/proxies.go @@ -77,16 +77,13 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject { case C.TypeSelector: clashType = "Selector" isGroup = true - case C.TypeURLTest: - clashType = "URLTest" - isGroup = true default: clashType = "Unknown" } info.Put("type", clashType) info.Put("name", detour.Tag()) info.Put("udp", common.Contains(detour.Network(), C.NetworkUDP)) - delayHistory := server.router.URLTestHistoryStorage(false).LoadURLTestHistory(outbound.RealTag(detour)) + delayHistory := server.urlTestHistory.LoadURLTestHistory(adapter.OutboundTag(detour)) if delayHistory != nil { info.Put("history", []*urltest.History{delayHistory}) } else { @@ -218,9 +215,9 @@ func getProxyDelay(server *Server) func(w http.ResponseWriter, r *http.Request) defer func() { realTag := outbound.RealTag(proxy) if err != nil { - server.router.URLTestHistoryStorage(true).DeleteURLTestHistory(realTag) + server.urlTestHistory.DeleteURLTestHistory(realTag) } else { - server.router.URLTestHistoryStorage(true).StoreURLTestHistory(realTag, &urltest.History{ + server.urlTestHistory.StoreURLTestHistory(realTag, &urltest.History{ Time: time.Now(), Delay: delay, }) diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 36a81b4e..c0a89afd 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -12,6 +12,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/json" + "github.com/sagernet/sing-box/common/urltest" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/log" @@ -33,6 +34,7 @@ type Server struct { logger log.Logger httpServer *http.Server trafficManager *trafficontrol.Manager + urlTestHistory *urltest.HistoryStorage } func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server { @@ -46,6 +48,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options Handler: chiRouter, }, trafficManager, + urltest.NewHistoryStorage(), } cors := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, diff --git a/mkdocs.yml b/mkdocs.yml index 87cd4e1d..7d0f1b67 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -57,7 +57,6 @@ nav: - Shadowsocks: configuration/outbound/shadowsocks.md - DNS: configuration/outbound/dns.md - Selector: configuration/outbound/selector.md - - URLTest: configuration/outbound/urltest.md - Route: - configuration/route/index.md - GeoIP: configuration/route/geoip.md diff --git a/option/clash.go b/option/clash.go index ba46a319..eb8b0ef6 100644 --- a/option/clash.go +++ b/option/clash.go @@ -10,10 +10,3 @@ type SelectorOutboundOptions struct { Outbounds []string `json:"outbounds"` Default string `json:"default,omitempty"` } - -type URLTestOutboundOptions struct { - Outbounds []string `json:"outbounds"` - URL string `json:"url,omitempty"` - Interval Duration `json:"interval,omitempty"` - Tolerance uint16 `json:"tolerance,omitempty"` -} diff --git a/option/outbound.go b/option/outbound.go index fab6dd07..2a664a08 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -16,7 +16,6 @@ type _Outbound struct { ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"` VMessOptions VMessOutboundOptions `json:"-"` SelectorOptions SelectorOutboundOptions `json:"-"` - URLTestOptions URLTestOutboundOptions `json:"-"` } type Outbound _Outbound @@ -38,8 +37,6 @@ func (h Outbound) MarshalJSON() ([]byte, error) { v = h.VMessOptions case C.TypeSelector: v = h.SelectorOptions - case C.TypeURLTest: - v = h.URLTestOptions default: return nil, E.New("unknown outbound type: ", h.Type) } @@ -67,8 +64,6 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error { v = &h.VMessOptions case C.TypeSelector: v = &h.SelectorOptions - case C.TypeURLTest: - v = &h.URLTestOptions default: return E.New("unknown outbound type: ", h.Type) } diff --git a/outbound/builder.go b/outbound/builder.go index 15a49d81..bfb4c8c9 100644 --- a/outbound/builder.go +++ b/outbound/builder.go @@ -29,8 +29,6 @@ func New(router adapter.Router, logger log.ContextLogger, options option.Outboun return NewVMess(router, logger, options.Tag, options.VMessOptions) case C.TypeSelector: return NewSelector(router, logger, options.Tag, options.SelectorOptions) - case C.TypeURLTest: - return NewURLTest(router, logger, options.Tag, options.URLTestOptions) default: return nil, E.New("unknown outbound type: ", options.Type) } diff --git a/outbound/selector.go b/outbound/selector.go index 6492a5e1..24968500 100644 --- a/outbound/selector.go +++ b/outbound/selector.go @@ -103,3 +103,10 @@ func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata ad func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { return s.selected.NewPacketConnection(ctx, conn, metadata) } + +func RealTag(detour adapter.Outbound) string { + if group, isGroup := detour.(adapter.OutboundGroup); isGroup { + return group.Now() + } + return detour.Tag() +} diff --git a/outbound/urltest.go b/outbound/urltest.go deleted file mode 100644 index 7ebb0252..00000000 --- a/outbound/urltest.go +++ /dev/null @@ -1,249 +0,0 @@ -package outbound - -import ( - "context" - "net" - "sort" - "time" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/urltest" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/batch" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -var ( - _ adapter.Outbound = (*URLTest)(nil) - _ adapter.OutboundGroup = (*URLTest)(nil) -) - -type URLTest struct { - myOutboundAdapter - tags []string - link string - interval time.Duration - tolerance uint16 - group *URLTestGroup -} - -func NewURLTest(router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) { - outbound := &URLTest{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeURLTest, - router: router, - logger: logger, - tag: tag, - }, - tags: options.Outbounds, - link: options.URL, - interval: time.Duration(options.Interval), - tolerance: options.Tolerance, - } - if len(outbound.tags) == 0 { - return nil, E.New("missing tags") - } - return outbound, nil -} - -func (s *URLTest) Network() []string { - if s.group == nil { - return []string{C.NetworkTCP, C.NetworkUDP} - } - return s.group.Select().Network() -} - -func (s *URLTest) Start() error { - outbounds := make([]adapter.Outbound, 0, len(s.tags)) - for i, tag := range s.tags { - detour, loaded := s.router.Outbound(tag) - if !loaded { - return E.New("outbound ", i, " not found: ", tag) - } - outbounds = append(outbounds, detour) - } - s.group = NewURLTestGroup(s.router, s.logger, outbounds, s.link, s.interval, s.tolerance) - return s.group.Start() -} - -func (s URLTest) Close() error { - return common.Close( - common.PtrOrNil(s.group), - ) -} - -func (s *URLTest) Now() string { - return s.group.Select().Tag() -} - -func (s *URLTest) All() []string { - return s.tags -} - -func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - return s.group.Select().DialContext(ctx, network, destination) -} - -func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return s.group.Select().ListenPacket(ctx, destination) -} - -func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return s.group.Select().NewConnection(ctx, conn, metadata) -} - -func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return s.group.Select().NewPacketConnection(ctx, conn, metadata) -} - -type URLTestGroup struct { - router adapter.Router - logger log.Logger - outbounds []adapter.Outbound - link string - interval time.Duration - tolerance uint16 - - ticker *time.Ticker - close chan struct{} -} - -func NewURLTestGroup(router adapter.Router, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16) *URLTestGroup { - if link == "" { - //goland:noinspection HttpUrlsUsage - link = "http://www.gstatic.com/generate_204" - } - if interval == 0 { - interval = C.DefaultURLTestInterval - } - if tolerance == 0 { - tolerance = 50 - } - return &URLTestGroup{ - router: router, - logger: logger, - outbounds: outbounds, - link: link, - interval: interval, - tolerance: tolerance, - close: make(chan struct{}), - } -} - -func (g *URLTestGroup) Start() error { - g.ticker = time.NewTicker(g.interval) - go g.loopCheck() - return nil -} - -func (g *URLTestGroup) Close() error { - g.ticker.Stop() - close(g.close) - return nil -} - -func (g *URLTestGroup) Select() adapter.Outbound { - var minDelay uint16 - var minTime time.Time - var minOutbound adapter.Outbound - for _, detour := range g.outbounds { - history := g.router.URLTestHistoryStorage(false).LoadURLTestHistory(RealTag(detour)) - if history == nil { - continue - } - if minDelay == 0 || minDelay > history.Delay+g.tolerance || minDelay > history.Delay-g.tolerance && minTime.Before(history.Time) { - minDelay = history.Delay - minTime = history.Time - minOutbound = detour - } - } - if minOutbound == nil { - minOutbound = g.outbounds[0] - } - return minOutbound -} - -func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound { - outbounds := make([]adapter.Outbound, 0, len(g.outbounds)-1) - for _, detour := range g.outbounds { - if detour != used { - outbounds = append(outbounds, detour) - } - } - sort.Slice(outbounds, func(i, j int) bool { - oi := outbounds[i] - oj := outbounds[j] - hi := g.router.URLTestHistoryStorage(false).LoadURLTestHistory(RealTag(oi)) - if hi == nil { - return false - } - hj := g.router.URLTestHistoryStorage(false).LoadURLTestHistory(RealTag(oj)) - if hj == nil { - return false - } - return hi.Delay < hj.Delay - }) - return outbounds -} - -func (g *URLTestGroup) loopCheck() { - go g.checkOutbounds() - for { - select { - case <-g.close: - return - case <-g.ticker.C: - g.checkOutbounds() - } - } -} - -func (g *URLTestGroup) checkOutbounds() { - b, _ := batch.New(context.Background(), batch.WithConcurrencyNum[any](10)) - checked := make(map[string]bool) - for _, detour := range g.outbounds { - tag := detour.Tag() - realTag := RealTag(detour) - if checked[realTag] { - continue - } - history := g.router.URLTestHistoryStorage(false).LoadURLTestHistory(realTag) - if history != nil && time.Now().Sub(history.Time) < g.interval { - continue - } - checked[realTag] = true - p, loaded := g.router.Outbound(realTag) - if !loaded { - continue - } - b.Go(realTag, func() (any, error) { - ctx, cancel := context.WithTimeout(context.Background(), C.URLTestTimeout) - defer cancel() - t, err := urltest.URLTest(ctx, g.link, p) - if err != nil { - g.logger.Debug("outbound ", tag, " unavailable: ", err) - g.router.URLTestHistoryStorage(true).DeleteURLTestHistory(realTag) - } else { - g.logger.Debug("outbound ", tag, " available: ", t, "ms") - g.router.URLTestHistoryStorage(true).StoreURLTestHistory(realTag, &urltest.History{ - Time: time.Now(), - Delay: t, - }) - } - return nil, nil - }) - } - b.Wait() -} - -func RealTag(detour adapter.Outbound) string { - if group, isGroup := detour.(adapter.OutboundGroup); isGroup { - return group.Now() - } - return detour.Tag() -} diff --git a/route/router.go b/route/router.go index 3eacc41b..409603db 100644 --- a/route/router.go +++ b/route/router.go @@ -21,7 +21,6 @@ import ( "github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/sniff" - "github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-box/common/warning" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -93,7 +92,6 @@ type Router struct { interfaceMonitor DefaultInterfaceMonitor defaultMark int trafficController adapter.TrafficController - urlTestHistoryStorage *urltest.HistoryStorage processSearcher process.Searcher } @@ -699,13 +697,6 @@ func (r *Router) SetTrafficController(controller adapter.TrafficController) { r.trafficController = controller } -func (r *Router) URLTestHistoryStorage(create bool) *urltest.HistoryStorage { - if r.urlTestHistoryStorage == nil && create { - r.urlTestHistoryStorage = urltest.NewHistoryStorage() - } - return r.urlTestHistoryStorage -} - func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool { for _, rule := range rules { switch rule.Type {