mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-13 17:44:00 +00:00
Fix URLTest group hang on unresponsive outbound
This commit is contained in:
@@ -115,6 +115,10 @@ func (s *URLTest) CheckOutbounds() {
|
||||
s.group.CheckOutbounds(true)
|
||||
}
|
||||
|
||||
func (s *URLTest) PerformUpdateCheck() {
|
||||
s.group.performUpdateCheck()
|
||||
}
|
||||
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
s.group.Touch()
|
||||
var outbound adapter.Outbound
|
||||
@@ -202,6 +206,7 @@ type URLTestGroup struct {
|
||||
interruptGroup *interrupt.Group
|
||||
interruptExternalConnections bool
|
||||
access sync.Mutex
|
||||
updateAccess sync.Mutex
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
started bool
|
||||
@@ -362,6 +367,11 @@ func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) {
|
||||
return g.urlTest(ctx, false)
|
||||
}
|
||||
|
||||
type urlTestResult struct {
|
||||
delay uint16
|
||||
err error
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint16, error) {
|
||||
result := make(map[string]uint16)
|
||||
if g.checking.Swap(true) {
|
||||
@@ -389,18 +399,28 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
||||
b.Go(realTag, func() (any, error) {
|
||||
testCtx, cancel := context.WithTimeout(g.ctx, C.TCPTimeout)
|
||||
defer cancel()
|
||||
t, err := urltest.URLTest(testCtx, g.link, p)
|
||||
if err != nil {
|
||||
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
testChan := make(chan urlTestResult, 1)
|
||||
go func() {
|
||||
delay, testErr := urltest.URLTest(testCtx, g.link, p)
|
||||
testChan <- urlTestResult{delay, testErr}
|
||||
}()
|
||||
var testResult urlTestResult
|
||||
select {
|
||||
case testResult = <-testChan:
|
||||
case <-testCtx.Done():
|
||||
testResult.err = testCtx.Err()
|
||||
}
|
||||
if testResult.err != nil {
|
||||
g.logger.Debug("outbound ", tag, " unavailable: ", testResult.err)
|
||||
g.history.DeleteURLTestHistory(realTag)
|
||||
} else {
|
||||
g.logger.Debug("outbound ", tag, " available: ", t, "ms")
|
||||
g.logger.Debug("outbound ", tag, " available: ", testResult.delay, "ms")
|
||||
g.history.StoreURLTestHistory(realTag, &adapter.URLTestHistory{
|
||||
Time: time.Now(),
|
||||
Delay: t,
|
||||
Delay: testResult.delay,
|
||||
})
|
||||
resultAccess.Lock()
|
||||
result[tag] = t
|
||||
result[tag] = testResult.delay
|
||||
resultAccess.Unlock()
|
||||
}
|
||||
return nil, nil
|
||||
@@ -412,6 +432,8 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) performUpdateCheck() {
|
||||
g.updateAccess.Lock()
|
||||
defer g.updateAccess.Unlock()
|
||||
var updated bool
|
||||
if outbound, exists := g.Select(N.NetworkTCP); outbound != nil && (g.selectedOutboundTCP == nil || (exists && outbound != g.selectedOutboundTCP)) {
|
||||
if g.selectedOutboundTCP != nil {
|
||||
|
||||
@@ -35,7 +35,6 @@ var _ adapter.InterfaceUpdateListener = (*Outbound)(nil)
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
@@ -56,7 +55,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
}
|
||||
outbound := &Outbound{
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeSSH, tag, []string{N.NetworkTCP}, options.DialerOptions),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
dialer: outboundDialer,
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
@@ -121,7 +119,7 @@ func randomVersion() string {
|
||||
return version
|
||||
}
|
||||
|
||||
func (s *Outbound) connect() (*ssh.Client, error) {
|
||||
func (s *Outbound) connect(ctx context.Context) (client *ssh.Client, err error) {
|
||||
if s.client != nil {
|
||||
return s.client, nil
|
||||
}
|
||||
@@ -133,10 +131,24 @@ func (s *Outbound) connect() (*ssh.Client, error) {
|
||||
return s.client, nil
|
||||
}
|
||||
|
||||
conn, err := s.dialer.DialContext(s.ctx, N.NetworkTCP, s.serverAddr)
|
||||
conn, err := s.dialer.DialContext(ctx, N.NetworkTCP, s.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.Done() != nil {
|
||||
handshakeConn := conn
|
||||
stopContext := context.AfterFunc(ctx, func() {
|
||||
_ = handshakeConn.Close()
|
||||
})
|
||||
defer func() {
|
||||
if !stopContext() {
|
||||
s.client = nil
|
||||
s.clientConn = nil
|
||||
client = nil
|
||||
err = ctx.Err()
|
||||
}
|
||||
}()
|
||||
}
|
||||
config := &ssh.ClientConfig{
|
||||
User: s.user,
|
||||
Auth: s.authMethod,
|
||||
@@ -161,7 +173,7 @@ func (s *Outbound) connect() (*ssh.Client, error) {
|
||||
return nil, E.Cause(err, "connect to ssh server")
|
||||
}
|
||||
|
||||
client := ssh.NewClient(clientConn, chans, reqs)
|
||||
client = ssh.NewClient(clientConn, chans, reqs)
|
||||
|
||||
s.clientConn = conn
|
||||
s.client = client
|
||||
@@ -187,7 +199,7 @@ func (s *Outbound) Close() error {
|
||||
}
|
||||
|
||||
func (s *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
client, err := s.connect()
|
||||
client, err := s.connect(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user