github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/experimental/libbox/command_urltest.go (about) 1 package libbox 2 3 import ( 4 "encoding/binary" 5 "net" 6 "time" 7 8 "github.com/inazumav/sing-box/adapter" 9 "github.com/inazumav/sing-box/common/urltest" 10 "github.com/inazumav/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 ) 16 17 func (c *CommandClient) URLTest(groupTag string) error { 18 conn, err := c.directConnect() 19 if err != nil { 20 return err 21 } 22 defer conn.Close() 23 err = binary.Write(conn, binary.BigEndian, uint8(CommandURLTest)) 24 if err != nil { 25 return err 26 } 27 err = rw.WriteVString(conn, groupTag) 28 if err != nil { 29 return err 30 } 31 return readError(conn) 32 } 33 34 func (s *CommandServer) handleURLTest(conn net.Conn) error { 35 defer conn.Close() 36 groupTag, err := rw.ReadVString(conn) 37 if err != nil { 38 return err 39 } 40 service := s.service 41 if service == nil { 42 return nil 43 } 44 abstractOutboundGroup, isLoaded := service.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 var historyStorage *urltest.HistoryStorage 57 if clashServer := service.instance.Router().ClashServer(); clashServer != nil { 58 historyStorage = clashServer.HistoryStorage() 59 } else { 60 return writeError(conn, E.New("Clash API is required for URLTest on non-URLTest group")) 61 } 62 63 outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound { 64 itOutbound, _ := service.instance.Router().Outbound(it) 65 return itOutbound 66 }), func(it adapter.Outbound) bool { 67 if it == nil { 68 return false 69 } 70 _, isGroup := it.(adapter.OutboundGroup) 71 if isGroup { 72 return false 73 } 74 return true 75 }) 76 b, _ := batch.New(service.ctx, batch.WithConcurrencyNum[any](10)) 77 for _, detour := range outbounds { 78 outboundToTest := detour 79 outboundTag := outboundToTest.Tag() 80 b.Go(outboundTag, func() (any, error) { 81 t, err := urltest.URLTest(service.ctx, "", outboundToTest) 82 if err != nil { 83 historyStorage.DeleteURLTestHistory(outboundTag) 84 } else { 85 historyStorage.StoreURLTestHistory(outboundTag, &urltest.History{ 86 Time: time.Now(), 87 Delay: t, 88 }) 89 } 90 return nil, nil 91 }) 92 } 93 } 94 return writeError(conn, nil) 95 }