github.com/nxtrace/NTrace-core@v1.3.1-0.20240513132635-39169291e8c9/util/latency.go (about)

     1  package util
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"net"
     9  	"net/http"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/fatih/color"
    14  )
    15  
    16  type ResponseInfo struct {
    17  	IP      string
    18  	Latency string
    19  	Content string
    20  }
    21  
    22  var (
    23  	results = make(chan ResponseInfo)
    24  	timeout = 5 * time.Second
    25  )
    26  var FastIpCache = ""
    27  
    28  func GetFastIP(domain string, port string, enableOutput bool) string {
    29  	proxyUrl := GetProxy()
    30  	if proxyUrl != nil {
    31  		return "origin-fallback.nxtrace.org"
    32  	}
    33  	if FastIpCache != "" {
    34  		return FastIpCache
    35  	}
    36  
    37  	var ips []net.IP
    38  	var err error
    39  	if domain == "origin-fallback.nxtrace.org" {
    40  		ips, err = net.LookupIP("api.nxtrace.org")
    41  	} else {
    42  		ips, err = net.LookupIP(domain)
    43  	}
    44  
    45  	if err != nil {
    46  		log.Fatal("DNS resolution failed, please check your system DNS Settings")
    47  	}
    48  
    49  	if len(ips) == 0 {
    50  		// 添加默认IP 45.88.195.154
    51  		ips = append(ips, net.ParseIP("45.88.195.154"))
    52  	}
    53  
    54  	for _, ip := range ips {
    55  		go checkLatency(domain, ip.String(), port)
    56  	}
    57  
    58  	var result ResponseInfo
    59  
    60  	select {
    61  	case result = <-results:
    62  	//等待5s没有结果 视为连不上API了
    63  	case <-time.After(timeout):
    64  		log.Println("IP connection has been timeout, please check your network")
    65  
    66  	}
    67  
    68  	//if len(ips) > 0 {
    69  	if enableOutput {
    70  		_, _ = fmt.Fprintf(color.Output, "%s preferred API IP - %s - %s - %s",
    71  			color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"),
    72  			color.New(color.FgGreen, color.Bold).Sprintf("%s", result.IP),
    73  			color.New(color.FgCyan, color.Bold).Sprintf("%sms", result.Latency),
    74  			color.New(color.FgGreen, color.Bold).Sprintf("%s", result.Content),
    75  		)
    76  	}
    77  	//}
    78  
    79  	//有些时候真的啥都不通,还是挑一个顶上吧
    80  	if result.IP == "" {
    81  		result.IP = "45.88.195.154"
    82  	}
    83  
    84  	FastIpCache = result.IP
    85  	return result.IP
    86  }
    87  
    88  func checkLatency(domain string, ip string, port string) {
    89  	start := time.Now()
    90  	if !strings.Contains(ip, ".") {
    91  		ip = "[" + ip + "]"
    92  	}
    93  
    94  	// 自定义DialContext以使用指定的IP连接
    95  	transport := &http.Transport{
    96  		//DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
    97  		//	return net.DialTimeout(network, addr, 1*time.Second)
    98  		//},
    99  		TLSClientConfig: &tls.Config{
   100  			ServerName: domain,
   101  		},
   102  		TLSHandshakeTimeout: timeout,
   103  	}
   104  
   105  	client := &http.Client{
   106  		Transport: transport,
   107  		Timeout:   timeout,
   108  	}
   109  
   110  	//此处虽然是 https://domain/ 但是实际上会使用指定的IP连接
   111  	req, err := http.NewRequest("GET", "https://"+ip+":"+port+"/", nil)
   112  	if err != nil {
   113  		// !!! 此处不要给results返回任何值
   114  		//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
   115  		return
   116  	}
   117  	req.Host = domain
   118  	resp, err := client.Do(req)
   119  	if err != nil {
   120  		//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
   121  		return
   122  	}
   123  	defer resp.Body.Close()
   124  
   125  	bodyBytes, err := io.ReadAll(resp.Body)
   126  	if err != nil {
   127  		//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
   128  		return
   129  	}
   130  	bodyString := string(bodyBytes)
   131  
   132  	latency := fmt.Sprintf("%.2f", float64(time.Since(start))/float64(time.Millisecond))
   133  	results <- ResponseInfo{IP: ip, Latency: latency, Content: bodyString}
   134  }