github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/libbox/command_urltest.go (about)

     1  package libbox
     2  
     3  import (
     4  	"encoding/binary"
     5  	"net"
     6  	"time"
     7  
     8  	"github.com/sagernet/sing-box/adapter"
     9  	"github.com/sagernet/sing-box/common/urltest"
    10  	"github.com/sagernet/sing-box/outbound"
    11  	"github.com/sagernet/sing/common"
    12  	"github.com/sagernet/sing/common/batch"
    13  	E "github.com/sagernet/sing/common/exceptions"
    14  	"github.com/sagernet/sing/common/rw"
    15  	"github.com/sagernet/sing/service"
    16  )
    17  
    18  func (c *CommandClient) URLTest(groupTag string) error {
    19  	conn, err := c.directConnect()
    20  	if err != nil {
    21  		return err
    22  	}
    23  	defer conn.Close()
    24  	err = binary.Write(conn, binary.BigEndian, uint8(CommandURLTest))
    25  	if err != nil {
    26  		return err
    27  	}
    28  	err = rw.WriteVString(conn, groupTag)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	return readError(conn)
    33  }
    34  
    35  func (s *CommandServer) handleURLTest(conn net.Conn) error {
    36  	groupTag, err := rw.ReadVString(conn)
    37  	if err != nil {
    38  		return err
    39  	}
    40  	serviceNow := s.service
    41  	if serviceNow == nil {
    42  		return nil
    43  	}
    44  	abstractOutboundGroup, isLoaded := serviceNow.instance.Router().Outbound(groupTag)
    45  	if !isLoaded {
    46  		return writeError(conn, E.New("outbound group not found: ", groupTag))
    47  	}
    48  	outboundGroup, isOutboundGroup := abstractOutboundGroup.(adapter.OutboundGroup)
    49  	if !isOutboundGroup {
    50  		return writeError(conn, E.New("outbound is not a group: ", groupTag))
    51  	}
    52  	urlTest, isURLTest := abstractOutboundGroup.(*outbound.URLTest)
    53  	if isURLTest {
    54  		go urlTest.CheckOutbounds()
    55  	} else {
    56  		historyStorage := service.PtrFromContext[urltest.HistoryStorage](serviceNow.ctx)
    57  		outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
    58  			itOutbound, _ := serviceNow.instance.Router().Outbound(it)
    59  			return itOutbound
    60  		}), func(it adapter.Outbound) bool {
    61  			if it == nil {
    62  				return false
    63  			}
    64  			_, isGroup := it.(adapter.OutboundGroup)
    65  			if isGroup {
    66  				return false
    67  			}
    68  			return true
    69  		})
    70  		b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
    71  		for _, detour := range outbounds {
    72  			outboundToTest := detour
    73  			outboundTag := outboundToTest.Tag()
    74  			b.Go(outboundTag, func() (any, error) {
    75  				t, err := urltest.URLTest(serviceNow.ctx, "", outboundToTest)
    76  				if err != nil {
    77  					historyStorage.DeleteURLTestHistory(outboundTag)
    78  				} else {
    79  					historyStorage.StoreURLTestHistory(outboundTag, &urltest.History{
    80  						Time:  time.Now(),
    81  						Delay: t,
    82  					})
    83  				}
    84  				return nil, nil
    85  			})
    86  		}
    87  	}
    88  	return writeError(conn, nil)
    89  }