package utils import ( "crypto/tls" "fmt" "io" "net/http" "net/url" "regexp" "strings" "time" ) // HTTPClient HTTP客户端工具 type HTTPClient struct { client *http.Client } // NewHTTPClient 创建HTTP客户端 func NewHTTPClient(timeout time.Duration) *HTTPClient { return &HTTPClient{ client: &http.Client{ Timeout: timeout, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { if len(via) >= 10 { return fmt.Errorf("too many redirects") } return nil }, }, } } // CheckResult 检查结果 type CheckResult struct { StatusCode int Latency time.Duration Title string Favicon string Error error } // CheckWebsite 检查网站 func (c *HTTPClient) CheckWebsite(targetURL string) CheckResult { result := CheckResult{} start := time.Now() req, err := http.NewRequest("GET", targetURL, nil) if err != nil { result.Error = err return result } req.Header.Set("User-Agent", "MengYaPing/1.0 (Website Monitor)") req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") resp, err := c.client.Do(req) if err != nil { result.Error = err result.Latency = time.Since(start) return result } defer resp.Body.Close() result.Latency = time.Since(start) result.StatusCode = resp.StatusCode // 读取响应体获取标题 body, err := io.ReadAll(io.LimitReader(resp.Body, 1024*100)) // 限制100KB if err == nil { result.Title = extractTitle(string(body)) result.Favicon = extractFavicon(string(body), targetURL) } return result } // extractTitle 提取网页标题 func extractTitle(html string) string { re := regexp.MustCompile(`(?i)