123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- package urltest
- import (
- "context"
- "net"
- "net/http"
- "net/url"
- "sync"
- "time"
- M "github.com/sagernet/sing/common/metadata"
- N "github.com/sagernet/sing/common/network"
- )
- type History struct {
- Time time.Time `json:"time"`
- Delay uint16 `json:"delay"`
- }
- type HistoryStorage struct {
- access sync.RWMutex
- delayHistory map[string]*History
- }
- func NewHistoryStorage() *HistoryStorage {
- return &HistoryStorage{
- delayHistory: make(map[string]*History),
- }
- }
- func (s *HistoryStorage) LoadURLTestHistory(tag string) *History {
- if s == nil {
- return nil
- }
- s.access.RLock()
- defer s.access.RUnlock()
- return s.delayHistory[tag]
- }
- func (s *HistoryStorage) DeleteURLTestHistory(tag string) {
- s.access.Lock()
- defer s.access.Unlock()
- delete(s.delayHistory, tag)
- }
- func (s *HistoryStorage) StoreURLTestHistory(tag string, history *History) {
- s.access.Lock()
- defer s.access.Unlock()
- s.delayHistory[tag] = history
- }
- func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
- linkURL, err := url.Parse(link)
- if err != nil {
- return
- }
- hostname := linkURL.Hostname()
- port := linkURL.Port()
- if port == "" {
- switch linkURL.Scheme {
- case "http":
- port = "80"
- case "https":
- port = "443"
- }
- }
- start := time.Now()
- instance, err := detour.DialContext(ctx, "tcp", M.ParseSocksaddrHostPortStr(hostname, port))
- if err != nil {
- return
- }
- defer instance.Close()
- req, err := http.NewRequest(http.MethodHead, link, nil)
- if err != nil {
- return
- }
- req = req.WithContext(ctx)
- transport := &http.Transport{
- Dial: func(string, string) (net.Conn, error) {
- return instance, nil
- },
- // from http.DefaultTransport
- MaxIdleConns: 100,
- IdleConnTimeout: 90 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- ExpectContinueTimeout: 1 * time.Second,
- }
- client := http.Client{
- Transport: transport,
- CheckRedirect: func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- },
- }
- defer client.CloseIdleConnections()
- resp, err := client.Do(req)
- if err != nil {
- return
- }
- resp.Body.Close()
- t = uint16(time.Since(start) / time.Millisecond)
- return
- }
|