1
0

urltest.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package urltest
  2. import (
  3. "context"
  4. "net"
  5. "net/http"
  6. "net/url"
  7. "sync"
  8. "time"
  9. M "github.com/sagernet/sing/common/metadata"
  10. N "github.com/sagernet/sing/common/network"
  11. )
  12. type History struct {
  13. Time time.Time `json:"time"`
  14. Delay uint16 `json:"delay"`
  15. }
  16. type HistoryStorage struct {
  17. access sync.RWMutex
  18. delayHistory map[string]*History
  19. }
  20. func NewHistoryStorage() *HistoryStorage {
  21. return &HistoryStorage{
  22. delayHistory: make(map[string]*History),
  23. }
  24. }
  25. func (s *HistoryStorage) LoadURLTestHistory(tag string) *History {
  26. if s == nil {
  27. return nil
  28. }
  29. s.access.RLock()
  30. defer s.access.RUnlock()
  31. return s.delayHistory[tag]
  32. }
  33. func (s *HistoryStorage) DeleteURLTestHistory(tag string) {
  34. s.access.Lock()
  35. defer s.access.Unlock()
  36. delete(s.delayHistory, tag)
  37. }
  38. func (s *HistoryStorage) StoreURLTestHistory(tag string, history *History) {
  39. s.access.Lock()
  40. defer s.access.Unlock()
  41. s.delayHistory[tag] = history
  42. }
  43. func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
  44. linkURL, err := url.Parse(link)
  45. if err != nil {
  46. return
  47. }
  48. hostname := linkURL.Hostname()
  49. port := linkURL.Port()
  50. if port == "" {
  51. switch linkURL.Scheme {
  52. case "http":
  53. port = "80"
  54. case "https":
  55. port = "443"
  56. }
  57. }
  58. start := time.Now()
  59. instance, err := detour.DialContext(ctx, "tcp", M.ParseSocksaddrHostPortStr(hostname, port))
  60. if err != nil {
  61. return
  62. }
  63. defer instance.Close()
  64. req, err := http.NewRequest(http.MethodHead, link, nil)
  65. if err != nil {
  66. return
  67. }
  68. req = req.WithContext(ctx)
  69. transport := &http.Transport{
  70. Dial: func(string, string) (net.Conn, error) {
  71. return instance, nil
  72. },
  73. // from http.DefaultTransport
  74. MaxIdleConns: 100,
  75. IdleConnTimeout: 90 * time.Second,
  76. TLSHandshakeTimeout: 10 * time.Second,
  77. ExpectContinueTimeout: 1 * time.Second,
  78. }
  79. client := http.Client{
  80. Transport: transport,
  81. CheckRedirect: func(req *http.Request, via []*http.Request) error {
  82. return http.ErrUseLastResponse
  83. },
  84. }
  85. defer client.CloseIdleConnections()
  86. resp, err := client.Do(req)
  87. if err != nil {
  88. return
  89. }
  90. resp.Body.Close()
  91. t = uint16(time.Since(start) / time.Millisecond)
  92. return
  93. }