- computeFingerprint 使用 rune 索引替代字节索引,修复多字节字符指纹不匹配 - utls Chrome TLS 指纹仅对 Anthropic 官方域名生效,自定义 base_url 走标准 transport - IPv6 地址使用 net.JoinHostPort 正确拼接端口
Unified · Split
| @@ -1200,15 +1200,16 @@ const fingerprintSalt = "59cf53e54c78" · 1199 unmodified lines | |||
| 1200 | // Algorithm: SHA256(salt + messageText[4] + messageText[7] + messageText[20] + version)[:3] | 1200 | // Algorithm: SHA256(salt + messageText[4] + messageText[7] + messageText[20] + version)[:3] |
| 1201 | func computeFingerprint(messageText, version string) string { | 1201 | func computeFingerprint(messageText, version string) string { |
| 1202 | indices := [3]int{4, 7, 20} | 1202 | indices := [3]int{4, 7, 20} |
| 1203 | var chars [3]byte | 1203 | runes := []rune(messageText) |
| 1204 | for i, idx := range indices { | 1204 | var sb strings.Builder |
| 1205 | if idx < len(messageText) { | 1205 | for _, idx := range indices { |
| 1206 | chars[i] = messageText[idx] | 1206 | if idx < len(runes) { |
| 1207 | sb.WriteRune(runes[idx]) | ||
| 1207 | } else { | 1208 | } else { |
| 1208 | chars[i] = '0' | 1209 | sb.WriteRune('0') |
| 1209 | } | 1210 | } |
| 1210 | } | 1211 | } |
| 1211 | input := fingerprintSalt + string(chars[:]) + version | 1212 | input := fingerprintSalt + sb.String() + version |
| 1212 | h := sha256.Sum256([]byte(input)) | 1213 | h := sha256.Sum256([]byte(input)) |
| 1213 | return hex.EncodeToString(h[:])[:3] | 1214 | return hex.EncodeToString(h[:])[:3] |
| 1214 | } | 1215 | } |
| @@ -103,13 +103,12 @@ func (t *utlsRoundTripper) createConnection(host, addr string) (*http2.ClientCon · 102 unmodified lines | |||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | func (t *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | 105 | func (t *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { |
| 106 | host := req.URL.Host | ||
| 107 | addr := host | ||
| 108 | if !strings.Contains(addr, ":") { | ||
| 109 | addr += ":443" | ||
| 110 | } | ||
| 111 | |||
| 112 | hostname := req.URL.Hostname() | 106 | hostname := req.URL.Hostname() |
| 107 | port := req.URL.Port() | ||
| 108 | if port == "" { | ||
| 109 | port = "443" | ||
| 110 | } | ||
| 111 | addr := net.JoinHostPort(hostname, port) | ||
| 113 | 112 | ||
| 114 | h2Conn, err := t.getOrCreateConnection(hostname, addr) | 113 | h2Conn, err := t.getOrCreateConnection(hostname, addr) |
| 115 | if err != nil { | 114 | if err != nil { |
| @@ -129,8 +128,13 @@ func (t *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) · 13 unmodified lines | |||
| 129 | return resp, nil | 128 | return resp, nil |
| 130 | } | 129 | } |
| 131 | 130 | ||
| 132 | // fallbackRoundTripper tries utls first; if the target is plain HTTP or a | 131 | // anthropicHosts contains the hosts that should use utls Chrome TLS fingerprint. |
| 133 | // non-HTTPS scheme it falls back to a standard transport. | 132 | var anthropicHosts = map[string]struct{}{ |
| 133 | "api.anthropic.com": {}, | ||
| 134 | } | ||
| 135 | |||
| 136 | // fallbackRoundTripper uses utls for Anthropic HTTPS hosts and falls back to | ||
| 137 | // standard transport for all other requests (non-HTTPS or non-Anthropic hosts). | ||
| 134 | type fallbackRoundTripper struct { | 138 | type fallbackRoundTripper struct { |
| 135 | utls *utlsRoundTripper | 139 | utls *utlsRoundTripper |
| 136 | fallback http.RoundTripper | 140 | fallback http.RoundTripper |
| @@ -138,7 +142,9 @@ type fallbackRoundTripper struct { · 1 unmodified line | |||
| 138 | 142 | ||
| 139 | func (f *fallbackRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | 143 | func (f *fallbackRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { |
| 140 | if req.URL.Scheme == "https" { | 144 | if req.URL.Scheme == "https" { |
| 141 | return f.utls.RoundTrip(req) | 145 | if _, ok := anthropicHosts[strings.ToLower(req.URL.Hostname())]; ok { |
| 146 | return f.utls.RoundTrip(req) | ||
| 147 | } | ||
| 142 | } | 148 | } |
| 143 | return f.fallback.RoundTrip(req) | 149 | return f.fallback.RoundTrip(req) |
| 144 | } | 150 | } |