Fix URLTest outbound

This commit is contained in:
世界 2023-12-02 17:47:57 +08:00
parent fc8e49994c
commit 5269231df0
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
6 changed files with 53 additions and 58 deletions

View file

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

View file

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

15
box.go
View file

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

View file

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

View file

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

View file

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