Jelajahi Sumber

Fix URLTest outbound

世界 1 tahun lalu
induk
melakukan
5269231df0
6 mengubah file dengan 53 tambahan dan 58 penghapusan
  1. 1 1
      adapter/experimental.go
  2. 1 0
      adapter/router.go
  3. 8 7
      box.go
  4. 1 1
      experimental/clashapi/api_meta_group.go
  5. 31 47
      outbound/urltest.go
  6. 11 2
      route/router.go

+ 1 - 1
adapter/experimental.go

@@ -43,7 +43,7 @@ type OutboundGroup interface {
 
 type URLTestGroup interface {
 	OutboundGroup
-	URLTest(ctx context.Context, url string) (map[string]uint16, error)
+	URLTest(ctx context.Context) (map[string]uint16, error)
 }
 
 func OutboundTag(detour Outbound) string {

+ 1 - 0
adapter/router.go

@@ -15,6 +15,7 @@ import (
 
 type Router interface {
 	Service
+	PostStarter
 
 	Outbounds() []Outbound
 	Outbound(tag string) (Outbound, bool)

+ 8 - 7
box.go

@@ -258,7 +258,7 @@ func (s *Box) start() error {
 			return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
 		}
 	}
-	return nil
+	return s.postStart()
 }
 
 func (s *Box) postStart() error {
@@ -269,16 +269,17 @@ func (s *Box) postStart() error {
 			return E.Cause(err, "start ", serviceName)
 		}
 	}
-	for serviceName, service := range s.outbounds {
-		if lateService, isLateService := service.(adapter.PostStarter); isLateService {
-			s.logger.Trace("post-starting ", service)
-			err := lateService.PostStart()
+	for _, outbound := range s.outbounds {
+		if lateOutbound, isLateOutbound := outbound.(adapter.PostStarter); isLateOutbound {
+			s.logger.Trace("post-starting outbound/", outbound.Tag())
+			err := lateOutbound.PostStart()
 			if err != nil {
-				return E.Cause(err, "post-start ", serviceName)
+				return E.Cause(err, "post-start outbound/", outbound.Tag())
 			}
 		}
 	}
-	return nil
+	s.logger.Trace("post-starting router")
+	return s.router.PostStart()
 }
 
 func (s *Box) Close() error {

+ 1 - 1
experimental/clashapi/api_meta_group.go

@@ -83,7 +83,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
 
 		var result map[string]uint16
 		if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
-			result, err = urlTestGroup.URLTest(ctx, url)
+			result, err = urlTestGroup.URLTest(ctx)
 		} else {
 			outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound {
 				itOutbound, _ := server.router.Outbound(it)

+ 31 - 47
outbound/urltest.go

@@ -3,7 +3,6 @@ package outbound
 import (
 	"context"
 	"net"
-	"sort"
 	"sync"
 	"time"
 
@@ -38,7 +37,6 @@ type URLTest struct {
 	tolerance                    uint16
 	group                        *URLTestGroup
 	interruptExternalConnections bool
-	started                      bool
 }
 
 func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
@@ -84,8 +82,7 @@ func (s *URLTest) Start() error {
 }
 
 func (s *URLTest) PostStart() error {
-	s.started = true
-	go s.CheckOutbounds()
+	s.group.PostStart()
 	return nil
 }
 
@@ -103,8 +100,8 @@ func (s *URLTest) All() []string {
 	return s.tags
 }
 
-func (s *URLTest) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
-	return s.group.URLTest(ctx, link)
+func (s *URLTest) URLTest(ctx context.Context) (map[string]uint16, error) {
+	return s.group.URLTest(ctx)
 }
 
 func (s *URLTest) CheckOutbounds() {
@@ -112,9 +109,7 @@ func (s *URLTest) CheckOutbounds() {
 }
 
 func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
-	if s.started {
-		s.group.Start()
-	}
+	s.group.Touch()
 	outbound := s.group.Select(network)
 	conn, err := outbound.DialContext(ctx, network, destination)
 	if err == nil {
@@ -126,9 +121,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
 }
 
 func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
-	if s.started {
-		s.group.Start()
-	}
+	s.group.Touch()
 	outbound := s.group.Select(N.NetworkUDP)
 	conn, err := outbound.ListenPacket(ctx, destination)
 	if err == nil {
@@ -170,9 +163,11 @@ type URLTestGroup struct {
 	interruptGroup               *interrupt.Group
 	interruptExternalConnections bool
 
-	access sync.Mutex
-	ticker *time.Ticker
-	close  chan struct{}
+	access     sync.Mutex
+	ticker     *time.Ticker
+	close      chan struct{}
+	started    bool
+	lastActive atomic.TypedValue[time.Time]
 }
 
 func NewURLTestGroup(
@@ -214,8 +209,18 @@ func NewURLTestGroup(
 	}
 }
 
-func (g *URLTestGroup) Start() {
+func (g *URLTestGroup) PostStart() {
+	g.started = true
+	g.lastActive.Store(time.Now())
+	go g.CheckOutbounds(false)
+}
+
+func (g *URLTestGroup) Touch() {
+	if !g.started {
+		return
+	}
 	if g.ticker != nil {
+		g.lastActive.Store(time.Now())
 		return
 	}
 	g.access.Lock()
@@ -266,51 +271,30 @@ func (g *URLTestGroup) Select(network string) adapter.Outbound {
 	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.SliceStable(outbounds, func(i, j int) bool {
-		oi := outbounds[i]
-		oj := outbounds[j]
-		hi := g.history.LoadURLTestHistory(RealTag(oi))
-		if hi == nil {
-			return false
-		}
-		hj := g.history.LoadURLTestHistory(RealTag(oj))
-		if hj == nil {
-			return false
-		}
-		return hi.Delay < hj.Delay
-	})
-	return outbounds
-}
-
 func (g *URLTestGroup) loopCheck() {
-	go g.CheckOutbounds(true)
+	if time.Now().Sub(g.lastActive.Load()) > g.interval {
+		g.CheckOutbounds(false)
+	}
 	for {
-		g.pauseManager.WaitActive()
 		select {
 		case <-g.close:
 			return
 		case <-g.ticker.C:
-			g.CheckOutbounds(false)
 		}
+		g.pauseManager.WaitActive()
+		g.CheckOutbounds(false)
 	}
 }
 
 func (g *URLTestGroup) CheckOutbounds(force bool) {
-	_, _ = g.urlTest(g.ctx, g.link, force)
+	_, _ = g.urlTest(g.ctx, force)
 }
 
-func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
-	return g.urlTest(ctx, link, false)
+func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) {
+	return g.urlTest(ctx, false)
 }
 
-func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (map[string]uint16, error) {
+func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint16, error) {
 	result := make(map[string]uint16)
 	if g.checking.Swap(true) {
 		return result, nil
@@ -337,7 +321,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
 		b.Go(realTag, func() (any, error) {
 			ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
 			defer cancel()
-			t, err := urltest.URLTest(ctx, link, p)
+			t, err := urltest.URLTest(ctx, g.link, p)
 			if err != nil {
 				g.logger.Debug("outbound ", tag, " unavailable: ", err)
 				g.history.DeleteURLTestHistory(realTag)

+ 11 - 2
route/router.go

@@ -88,6 +88,7 @@ type Router struct {
 	platformInterface                  platform.Interface
 	needWIFIState                      bool
 	wifiState                          adapter.WIFIState
+	started                            bool
 }
 
 func NewRouter(
@@ -571,6 +572,11 @@ func (r *Router) Close() error {
 	return err
 }
 
+func (r *Router) PostStart() error {
+	r.started = true
+	return nil
+}
+
 func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
 	outbound, loaded := r.outboundByTag[tag]
 	return outbound, loaded
@@ -1015,8 +1021,11 @@ func (r *Router) notifyNetworkUpdate(event int) {
 		}
 	}
 
-	r.ResetNetwork()
-	return
+	if !r.started {
+		return
+	}
+
+	_ = r.ResetNetwork()
 }
 
 func (r *Router) ResetNetwork() error {