// 429 详情探针:打印 429 响应的全部头部 + 响应体,看清 CDN 到底怎么限的。 // // go run ./tools/probe429 -v <图片URL> package main import ( "fmt" "io" "net/http" "os" "strings" "time" ) func main() { if len(os.Args) < 2 { fmt.Fprintln(os.Stderr, "用法: go run ./tools/probe429 [-v] <图片URL>") os.Exit(1) } url := os.Args[len(os.Args)-1] verbose := false for _, a := range os.Args { if a == "-v" { verbose = true } } // 先探一张,看当前状态 for round := 1; round <= 3; round++ { status, hdr, body, err := fullProbe(url, verbose) if err != nil { fmt.Printf("[第%d轮] 错误: %v\n", round, err) continue } fmt.Printf("[第%d轮] HTTP %d\n", round, status) if verbose || status == 429 { for _, k := range []string{ "Retry-After", "X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset", "X-Cache", "CF-RateLimited", "Server", "Content-Type", "X-Jetpack-Cache", } { if v := hdr.Get(k); v != "" { fmt.Printf(" %s: %s\n", k, v) } } if s := strings.TrimSpace(body); s != "" { fmt.Printf(" body: %.300s\n", s) } } if status == 200 { fmt.Println(" ✓ 这次成功了") break } if round < 3 { time.Sleep(3 * time.Second) } } } func fullProbe(url string, verbose bool) (int, http.Header, string, error) { tr := &http.Transport{ForceAttemptHTTP2: true} defer tr.CloseIdleConnections() client := &http.Client{Transport: tr, Timeout: 25 * time.Second} req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return 0, nil, "", err } // 用我们客户端的头 req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") req.Header.Set("Accept", "image/webp,image/*,*/*;q=0.8") req.Header.Set("Referer", "https://migmg.ssuu.uk/") resp, err := client.Do(req) if err != nil { return 0, nil, "", err } defer resp.Body.Close() b, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) fmt.Printf(" Proto=%s (2=HTTP/2, 1.1=HTTP/1.1)\n", resp.Proto) return resp.StatusCode, resp.Header, string(b), nil }