command_urltest.go 2.4 KB

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