enetx/surf

GitHub: enetx/surf

Stars: 1433 | Forks: 73

Surf - Go 的高级 HTTP 客户端

[![Go Reference](https://pkg.go.dev/badge/github.com/enetx/surf.svg)](https://pkg.go.dev/github.com/enetx/surf) [![Go Report Card](https://goreportcard.com/badge/github.com/enetx/surf)](https://goreportcard.com/report/github.com/enetx/surf) [![Coverage Status](https://coveralls.io/repos/github/enetx/surf/badge.svg?branch=main&service=github)](https://coveralls.io/github/enetx/surf?branch=main) [![Go](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/dab81dba51235836.svg)](https://github.com/enetx/surf/actions/workflows/go.yml) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/enetx/surf)

Surf 是一个功能强大、特性丰富的 Go HTTP 客户端库,让处理 HTTP 请求变得直观且愉快。凭借浏览器模拟、JA3/JA4 指纹识别和全面的中间件支持,Surf 提供了现代网络交互所需的一切。

## ✨ 关键特性 ### 🎭 **浏览器模拟** - **Chrome 与 Firefox 支持**:准确模拟 Chrome v145 和 Firefox v148 的浏览器指纹 - **平台多样性**:模拟 Windows、macOS、Linux、Android 和 iOS 设备 - **TLS 指纹识别**:完整的 JA3/JA4 指纹自定义以增强隐私 - **自动头部信息**:正确的头部顺序和浏览器特定值 - **WebKit 表单边界**:与真实浏览器匹配的多部分表单边界生成 ### 🔒 **高级 TLS 与安全** - **自定义 JA3/JA4**:使用 `HelloID` 和 `HelloSpec` 配置精确的 TLS 指纹 - **HTTP/3 支持**:完整的 HTTP/3 over QUIC 支持,带有完整的浏览器特定指纹识别 - **HTTP/2 与 HTTP/3**:完整的 HTTP/2 支持,可自定义设置(SETTINGS 帧、窗口大小、优先级) - **有序头部**:浏览器准确的头部顺序以实现完美的指纹规避 - **证书固定**:自定义 TLS 证书验证 - **DNS-over-TLS**:通过 DoT 支持增强隐私 - **代理支持**:HTTP、HTTPS、SOCKS4 和 SOCKS5 代理配置,支持 HTTP/3 的 UDP ### 🚀 **性能与可靠性** - **连接池**:使用单例模式进行高效连接复用 - **自动重试**:可配置的重试逻辑,支持自定义状态码 - **响应缓存**:内置响应体缓存以供重复访问 - **流式支持**:高效处理大型响应和 SSE - **压缩**:自动解压 gzip、deflate、br 和 zstd 响应 - **Keep-Alive**:可配置参数的长连接 ### 🛠️ **开发者体验** - **标准库兼容**:可转换为 `net/http.Client`,以便与期望标准 `*http.Client` 的第三方库集成 - **流畅 API**:可链式调用的方法实现优雅的代码 - **中间件系统**:支持优先级设置的请求/响应/客户端中间件 - **类型安全**:通过 [enetx/g](https://github.com/enetx/g) 支持泛型的强类型 - **调试模式**:全面的请求/响应调试 - **错误处理**:结果类型模式以更好地管理错误 - **上下文支持**:完整的 `context.Context` 集成,用于取消和超时 ## 📦 安装 ``` go get -u github.com/enetx/surf ``` **所需 Go 版本:** 1.25+ ## 🔄 标准库兼容性 Surf 与 Go 的标准 `net/http` 包无缝集成,允许你在任何期望标准 `*http.Client` 的库中使用 Surf 的高级特性。 ``` // Create a Surf client with advanced features surfClient := surf.NewClient(). Builder(). Impersonate().Chrome(). Session(). Build(). Unwrap() // Convert to standard net/http.Client stdClient := surfClient.Std() // Use with any third-party library // Example: AWS SDK, Google APIs, OpenAI client, etc. resp, err := stdClient.Get("https://api.example.com") ``` **使用 Std() 时保留的特性:** - ✅ JA3/TLS 指纹识别 - ✅ HTTP/2、HTTP/3 设置 && 指纹识别 - ✅ 浏览器模拟头部信息 - ✅ 有序头部 - ✅ Cookie 和会话 - ✅ 代理配置 - ✅ 自定义头部和 User-Agent - ✅ 超时设置 - ✅ 重定向策略 - ✅ 请求/响应中间件 **Std() 的限制:** - ❌ 重试逻辑(在应用层实现) - ❌ 响应体缓存 - ❌ 远程地址跟踪 - ❌ 请求计时信息 ## 🚀 快速开始 ### 基本 GET 请求 ``` package main import ( "fmt" "log" "github.com/enetx/surf" ) func main() { resp := surf.NewClient().Get("https://api.github.com/users/github").Do() if resp.IsErr() { log.Fatal(resp.Err()) } fmt.Println(resp.Ok().Body.String().Unwrap()) } ``` ### JSON 响应处理 ``` type User struct { Name string `json:"name"` Company string `json:"company"` Location string `json:"location"` } resp := surf.NewClient().Get("https://api.github.com/users/github").Do() if resp.IsOk() { var user User resp.Ok().Body.JSON(&user) fmt.Printf("User: %+v\n", user) } ``` ## 🎭 浏览器模拟 ### Chrome 模拟 ``` client := surf.NewClient(). Builder(). Impersonate(). Chrome(). // Latest Chrome v145 Build(). Unwrap() resp := client.Get("https://example.com").Do() ``` ### 随机操作系统的 Firefox ``` client := surf.NewClient(). Builder(). Impersonate(). RandomOS(). // Randomly selects Windows, macOS, Linux, Android, or iOS Firefox(). // Latest Firefox v148 Build(). Unwrap() ``` ### 平台特定模拟 ``` // iOS Chrome client := surf.NewClient(). Builder(). Impersonate(). IOS(). Chrome(). Build(). Unwrap() // Android Chrome client := surf.NewClient(). Builder(). Impersonate(). Android(). Chrome(). Build(). Unwrap() ``` ## 🚀 HTTP/3 与完整的 QUIC 指纹识别 ### 自动检测的 Chrome HTTP/3 ``` // Automatic HTTP/3 with Chrome fingerprinting client := surf.NewClient(). Builder(). Impersonate().Chrome(). ForceHTTP3(). // Auto-detects Chrome and applies appropriate HTTP/3 settings Build(). Unwrap() resp := client.Get("https://cloudflare-quic.com/").Do() if resp.IsOk() { fmt.Printf("Protocol: %s\n", resp.Ok().Proto) // HTTP/3.0 } ``` ### Firefox HTTP/3 ``` // Firefox with HTTP/3 fingerprinting client := surf.NewClient(). Builder(). Impersonate().Firefox(). ForceHTTP3(). // Auto-detects Firefox and applies Firefox HTTP/3 settings Build(). Unwrap() resp := client.Get("https://cloudflare-quic.com/").Do() ``` ### 手动 HTTP/3 配置 ``` // Custom fingerprint settings client := surf.NewClient(). Builder(). HTTP3Settings().Grease().Set(). Build(). Unwrap() ``` ### HTTP/3 兼容性与回退 HTTP/3 自动处理兼容性问题: ``` // With HTTP proxy - automatically falls back to HTTP/2 client := surf.NewClient(). Builder(). Proxy("http://proxy:8080"). // HTTP proxies incompatible with HTTP/3 ForceHTTP3(). // Will use HTTP/2 instead Build(). Unwrap() // With SOCKS5 proxy - HTTP/3 works over UDP client := surf.NewClient(). Builder(). Proxy("socks5://127.0.0.1:1080"). // SOCKS5 UDP proxy supports HTTP/3 ForceHTTP3(). // Will use HTTP/3 over SOCKS5 Build(). Unwrap() // With DNS settings - works seamlessly client := surf.NewClient(). Builder(). DNS("8.8.8.8:53"). // Custom DNS works with HTTP/3 ForceHTTP3(). Build(). Unwrap() // With DNS-over-TLS - works seamlessly client := surf.NewClient(). Builder(). DNSOverTLS().Google(). // DoT works with HTTP/3 ForceHTTP3() Build(). Unwrap() ``` **关键 HTTP/3 功能:** - ✅ **完整的 QUIC 指纹识别**:完整的 Chrome 和 Firefox QUIC 传输参数匹配 - ✅ **头部顺序**:保持类似浏览器的头部序列 - ✅ **SOCKS5 UDP 支持**:HTTP/3 无缝工作在 SOCKS5 UDP 代理上 - ✅ **自动回退**:当配置 HTTP 代理时智能回退到 HTTP/2 - ✅ **DNS 集成**:自定义 DNS 和 DNS-over-TLS 支持 - ✅ **JA4QUIC 支持**:带有初始数据包 + TLS ClientHello 的高级 QUIC 指纹识别 - ✅ **与顺序无关**:`ForceHTTP3()` 调用顺序无关 ## 🔧 高级配置 ### 自定义 JA3 指纹 ``` // Use specific browser versions client := surf.NewClient(). Builder(). JA(). Chrome(). // Latest Chrome Build(). Unwrap() // Randomized fingerprints for evasion client := surf.NewClient(). Builder(). JA(). Randomized(). // Random TLS fingerprint Build(). Unwrap() // With custom HelloID client := surf.NewClient(). Builder(). JA(). SetHelloID(utls.HelloChrome_Auto). Build(). Unwrap() // With custom HelloSpec client := surf.NewClient(). Builder(). JA(). SetHelloSpec(customSpec). Build(). Unwrap() ``` ### HTTP/2 配置 ``` client := surf.NewClient(). Builder(). HTTP2Settings(). HeaderTableSize(65536). EnablePush(0). InitialWindowSize(6291456). MaxHeaderListSize(262144). ConnectionFlow(15663105). Set(). Build(). Unwrap() ``` ### HTTP/3 配置 ``` client := surf.NewClient(). Builder(). HTTP3Settings(). QpackMaxTableCapacity(65536). MaxFieldSectionSize(262144). QpackBlockedStreams(100). H3Datagram(1). Grease(). Set(). Build(). Unwrap() ``` ### 代理配置 ``` // Single proxy client := surf.NewClient(). Builder(). Proxy("http://proxy.example.com:8080"). Build(). Unwrap() ``` ### SOCKS5 UDP 代理支持 Surf 支持通过 SOCKS5 UDP 代理进行 HTTP/3,将现代 QUIC 协议的优势与代理功能结合: ``` // HTTP/3 over SOCKS5 UDP proxy client := surf.NewClient(). Builder(). Proxy("socks5://127.0.0.1:1080"). Impersonate().Chrome(). ForceHTTP3(). // Uses HTTP/3 over SOCKS5 UDP Build(). Unwrap() // SOCKS5 with custom DNS resolution client := surf.NewClient(). Builder(). DNS("8.8.8.8:53"). // Custom DNS resolver Proxy("socks5://proxy:1080"). // SOCKS5 UDP proxy ForceHTTP3(). // HTTP/3 over SOCKS5 Build(). Unwrap() ``` ## 🔌 中间件系统 ### 请求中间件 ``` client := surf.NewClient(). Builder(). With(func(req *surf.Request) error { req.AddHeaders("X-Custom-Header", "value") fmt.Printf("Request to: %s\n", req.GetRequest().URL) return nil }). Build(). Unwrap() ``` ### 响应中间件 ``` client := surf.NewClient(). Builder(). With(func(resp *surf.Response) error { fmt.Printf("Response status: %d\n", resp.StatusCode) fmt.Printf("Response time: %v\n", resp.Time) return nil }). Build(). Unwrap() ``` ### 客户端中间件 ``` client := surf.NewClient(). Builder(). With(func(client *surf.Client) error { // Modify client configuration client.GetClient().Timeout = 30 * time.Second return nil }). Build(). Unwrap() ``` ## 📤 请求类型 ### 带 JSON 的 POST ``` user := map[string]string{ "name": "John Doe", "email": "john@example.com", } resp := surf.NewClient(). Post("https://api.example.com/users"). Body(user). Do() ``` ### 表单数据 ``` // Standard form data (field order not guaranteed) formData := map[string]string{ "username": "john", "password": "secret", } resp := surf.NewClient(). Post("https://example.com/login"). Body(formData). Do() // Ordered form data (preserves field insertion order) orderedForm := g.NewMapOrd[string, string]() orderedForm.Insert("username", "john") orderedForm.Insert("password", "secret") orderedForm.Insert("remember_me", "true") resp := surf.NewClient(). Post("https://example.com/login"). Body(orderedForm). Do() ``` ### 文件上传 ``` // Single file upload mp := surf.NewMultipart(). File("file", g.NewFile("/path/to/file.pdf")) resp := surf.NewClient(). Post("https://api.example.com/upload"). Multipart(mp). Do() // With additional form fields mp := surf.NewMultipart(). Field("description", "Important document"). Field("category", "reports"). File("file", g.NewFile("/path/to/file.pdf")) resp := surf.NewClient(). Post("https://api.example.com/upload"). Multipart(mp). Do() ``` ### 多部分表单 ``` // Simple multipart form with fields only mp := surf.NewMultipart(). Field("field1", "value1"). Field("field2", "value2") resp := surf.NewClient(). Post("https://api.example.com/form"). Multipart(mp). Do() // Advanced multipart with files from different sources mp := surf.NewMultipart(). Field("description", "Multiple files"). File("document", g.NewFile("/path/to/doc.pdf")). // Physical file FileBytes("data", "data.json", g.Bytes(`{"key": "value"}`)). // Bytes with custom filename FileString("text", "note.txt", "Hello, World!"). // String content FileReader("stream", "upload.bin", someReader). // io.Reader ContentType("application/pdf") // Custom Content-Type for last file resp := surf.NewClient(). Post("https://api.example.com/upload"). Multipart(mp). Do() ``` ## 🔄 会话管理 ### 持久会话 ``` client := surf.NewClient(). Builder(). Session(). // Enable cookie jar Build(). Unwrap() // Login client.Post("https://example.com/login").Body(credentials).Do() // Subsequent requests will include session cookies resp := client.Get("https://example.com/dashboard").Do() ``` ### 手动 Cookie 管理 ``` // Set cookies cookies := []*http.Cookie{ {Name: "session", Value: "abc123"}, {Name: "preference", Value: "dark_mode"}, } resp := surf.NewClient(). Get("https://example.com"). AddCookies(cookies...). Do() // Get cookies from response if resp.IsOk() { for _, cookie := range resp.Ok().Cookies { fmt.Printf("Cookie: %s = %s\n", cookie.Name, cookie.Value) } } ``` ## 📊 响应处理 ### 状态码检查 ``` resp := surf.NewClient().Get("https://api.example.com/data").Do() if resp.IsOk() { switch { case resp.Ok().StatusCode.IsSuccess(): fmt.Println("Success!") case resp.Ok().StatusCode.IsRedirection(): fmt.Println("Redirected to:", resp.Ok().Location()) case resp.Ok().StatusCode.IsClientError(): fmt.Println("Client error:", resp.Ok().StatusCode) case resp.Ok().StatusCode.IsServerError(): fmt.Println("Server error:", resp.Ok().StatusCode) } } ``` ### 响应体处理 ``` resp := surf.NewClient().Get("https://example.com/data").Do() if resp.IsOk() { body := resp.Ok().Body // As string (returns g.Result[g.String]) if content := body.String(); content.IsOk() { fmt.Println(content.Ok()) } // As bytes (returns g.Result[g.Bytes]) if data := body.Bytes(); data.IsOk() { fmt.Println(len(data.Ok())) } // UTF-8 conversion (returns g.Result[g.String]) if utf8Content := body.UTF8(); utf8Content.IsOk() { fmt.Println(utf8Content.Ok()) } // Check content if body.Contains("success") { fmt.Println("Request succeeded!") } // Save to file err := body.Dump("response.html") } ``` ### 流式处理大型响应 ``` resp := surf.NewClient().Get("https://example.com/large-file").Do() if resp.IsOk() { stream := resp.Ok().Body.Stream() defer stream.Close() scanner := bufio.NewScanner(stream) for scanner.Scan() { fmt.Println(scanner.Text()) } } ``` ### 服务器发送事件 (SSE) ``` resp := surf.NewClient().Get("https://example.com/events").Do() if resp.IsOk() { resp.Ok().Body.SSE(func(event *sse.Event) bool { fmt.Printf("Event: %s, Data: %s\n", event.Event, event.Data) return true // Continue reading (false to stop) }) } ``` ## 🔍 调试 ### 请求/响应调试 ``` resp := surf.NewClient(). Get("https://api.example.com"). Do() if resp.IsOk() { resp.Ok().Debug(). Request(). // Show request details Response(true). // Show response with body Print() } ``` ### TLS 信息 ``` resp := surf.NewClient().Get("https://example.com").Do() if resp.IsOk() { if tlsInfo := resp.Ok().TLSGrabber(); tlsInfo != nil { fmt.Printf("TLS Version: %s\n", tlsInfo.TLSVersion) fmt.Printf("Server Name: %s\n", tlsInfo.ExtensionServerName) fmt.Printf("Fingerprint: %s\n", tlsInfo.FingerprintSHA256) fmt.Printf("Common Name: %v\n", tlsInfo.CommonName) fmt.Printf("Organization: %v\n", tlsInfo.Organization) } } ``` ## ⚡ 性能优化 ### 连接复用 ``` // Create a reusable client client := surf.NewClient(). Builder(). Impersonate(). Chrome(). Build(). Unwrap() // Reuse for multiple requests for i := 0; i < 100; i++ { resp := client.Get("https://api.example.com/data").Do() // Process response } // Clean up when done defer client.CloseIdleConnections() ``` ### 响应缓存 ``` client := surf.NewClient(). Builder(). CacheBody(). // Enable body caching Build(). Unwrap() resp := client.Get("https://api.example.com/data").Do() if resp.IsOk() { // First access reads from network data1 := resp.Ok().Body.Bytes().Unwrap() // Subsequent accesses use cache data2 := resp.Ok().Body.Bytes().Unwrap() // No network I/O } ``` ### 重试配置 ``` client := surf.NewClient(). Builder(). Retry(3, 2*time.Second). // Max 3 retries, 2 second wait Build(). Unwrap() ``` ## 🌐 高级功能 ### H2C (HTTP/2 明文) ``` // Enable HTTP/2 without TLS client := surf.NewClient(). Builder(). H2C(). Build(). Unwrap() resp := client.Get("http://localhost:8080/h2c-endpoint").Do() ``` ### 自定义头部顺序 ``` // Control exact header order for fingerprinting evasion headers := g.NewMapOrd[g.String, g.String]() headers.Insert("User-Agent", "Custom/1.0") headers.Insert("Accept", "*/*") headers.Insert("Accept-Language", "en-US") headers.Insert("Accept-Encoding", "gzip, deflate") client := surf.NewClient(). Builder(). SetHeaders(headers). // Headers will be sent in this exact order Build(). Unwrap() ``` ### 自定义 DNS 解析器 ``` client := surf.NewClient(). Builder(). DNS("8.8.8.8:53"). // Use Google DNS Build(). Unwrap() ``` ### DNS-over-TLS ``` client := surf.NewClient(). Builder(). DNSOverTLS().Cloudflare(). // Cloudflare DoT Build(). Unwrap() ``` ### Unix 域套接字 ``` client := surf.NewClient(). Builder(). UnixSocket("/var/run/docker.sock"). Build(). Unwrap() resp := client.Get("http://localhost/v1.41/containers/json").Do() ``` ### 网络接口绑定 ``` client := surf.NewClient(). Builder(). InterfaceAddr("192.168.1.100"). // Bind to specific IP Build(). Unwrap() ``` ### 原始 HTTP 请求 ``` client := surf.NewClient(). Builder(). Impersonate(). Chrome(). // Latest Chrome v145 Build(). Unwrap() resp := client.Get("https://example.com").Do() ``` ## 📚 API 参考 ### 客户端方法 | 方法 | 描述 | |------|------| | `NewClient()` | 创建一个带有默认值的新 HTTP 客户端 | | `Get(url)` | 创建 GET 请求 | | `Post(url)` | 创建 POST 请求 | | `Put(url)` | 创建 PUT 请求 | | `Patch(url)` | 创建 PATCH 请求 | | `Delete(url)` | 创建 DELETE 请求 | | `Head(url)` | 创建 HEAD 请求 | | `Options(url)` | 创建 OPTIONS 请求 | | `Connect(url)` | 创建 CONNECT 请求 | | `Trace(url)` | 创建 TRACE 请求 | | `Raw(raw, scheme)` | 从原始 HTTP 创建请求 | | `Builder()` | 返回用于客户端配置的新 Builder | | `Std()` | 转换为标准 `*net/http.Client` | | `CloseIdleConnections()` | 关闭空闲连接,同时保持客户端可用 | | `Close()` | 完全关闭客户端并释放所有资源 | ### 构建器方法 | 方法 | 描述 | |------|------| | `Impersonate()` | 启用浏览器模拟 | | `JA()` | 配置 JA3/JA4 指纹识别 | | `HTTP2Settings()` | 配置 HTTP/2 参数 | | `HTTP3Settings()` | 配置 HTTP/3 参数 | | `H2C()` | 启用 HTTP/2 明文 | | `Proxy(proxy)` | 设置代理配置 | | `DNS(dns)` | 设置自定义 DNS 解析器 | | `DNSOverTLS()` | 配置 DNS-over-TLS | | `Session()` | 启用 Cookie Jar 以进行会话管理 | | `Timeout(duration)` | 设置请求超时 | | `MaxRedirects(n)` | 设置最大重定向次数 | | `NotFollowRedirects()` | 禁用重定向跟随 | | `FollowOnlyHostRedirects()` | 仅跟随同主机重定向 | | `ForwardHeadersOnRedirect()` | 在重定向时转发头部 | | `RedirectPolicy(fn)` | 自定义重定向策略函数 | | `Retry(max, wait, codes...)` | 配置重试逻辑 | | `CacheBody()` | 启用响应体缓存 | | `With(middleware, priority...)` | 添加中间件 | | `BasicAuth(auth)` | 设置基本身份验证 | | `BearerAuth(token)` | 设置承载令牌身份验证 | | `UserAgent(ua)` | 设置自定义用户代理 | | `SetHeaders(headers...)` | 设置请求头部 | | `AddHeaders(headers...)` | 添加请求头部 | | `AddCookies(cookies...)` | 添加 Cookie 到请求 | | `WithContext(ctx)` | 添加上下文 | | `ContentType(type)` | 设置内容类型 | | `GetRemoteAddress()` | 跟踪远程地址 | | `DisableKeepAlive()` | 禁用 Keep-Alive | | `DisableCompression()` | 禁用压缩 | | `ForceHTTP1()` | 强制使用 HTTP/1.1 | | `ForceHTTP2()` | 强制使用 HTTP/2 | | `ForceHTTP3()` | 强制使用 HTTP/3 | | `UnixSocket(path)` | 使用 Unix 套接字 | | `InterfaceAddr(addr)` | 绑定到网络接口 | | `Boundary(fn)` | 自定义多部分边界生成器 | ### 请求方法 | 方法 | 描述 | |------|------| | `Do()` | 执行请求 | | `WithContext(ctx)` | 为请求添加上下文 | | `Body(data)` | 设置请求体(JSON、表单数据、字节、字符串、io.Reader) | | `SetHeaders(headers...)` | 设置请求头部 | | `AddHeaders(headers...)` | 添加请求头部 | | `AddCookies(cookies...)` | 添加 Cookie 到请求 | | `Multipart(mp)` | 为请求设置多部分表单数据 | | `GetRequest()` | 返回底层的 `*http.Request` | ### 多部分方法 | 方法 | 描述 | |------|------| | `NewMultipart()` | 创建一个新的 Multipart 构建器 | | `Field(name, value)` | 添加表单字段 | | `File(fieldName, file)` | 添加来自 `*g.File` 的文件 | | `FileReader(fieldName, fileName, reader)` | 添加来自 `io.Reader` 的文件 | | `FileString(fieldName, fileName, content)` | 添加来自字符串内容的文件 | | `FileBytes(fieldName, fileName, data)` | 添加来自字节切片的文件 | | `ContentType(ct)` | 为最后添加的文件设置自定义 Content-Type | | `FileName(name)` | 覆盖最后添加文件的文件名 | | `Retry()` | 缓冲多部分主体以支持重试 | ### 响应属性 | 属性 | 类型 | 描述 | |------|------|------| | `StatusCode` | `StatusCode` | HTTP 状态码 | | `Headers` | `Headers` | 响应头部 | | `Cookies` | `Cookies` | 响应 Cookie | | `Body` | `*Body` | 响应体 | | `URL` | `*url.URL` | 重定向后的最终 URL | | `Time` | `time.Duration` | 请求耗时 | | `ContentLength` | `int64` | 内容长度 | | `Proto` | `string` | HTTP 协议版本 | | `Attempts` | `int` | 重试尝试次数 | ### 响应方法 | 方法 | 描述 | |------|------| | `Debug()` | 返回请求/响应的调试信息 | | `Location()` | 返回 Location 头部(重定向 URL) | | `TLSGrabber()` | 返回 TLS 连接信息 | | `Referer()` | 返回原始请求的 HTTP Referer 头部 | | `GetResponse()` | 返回底层的 `*http.Response` | | `GetCookies(url)` | 返回特定 URL 的 Cookie | | `SetCookies(url, cookies)` | 在客户端的 Cookie Jar 中存储 Cookie | | `RemoteAddress()` | 返回远程服务器地址 | ### 响应体方法 | 方法 | 描述 | |------|------| | `String()` | 以字符串形式获取响应体(返回 `g.Result[g.String]`) | | `Bytes()` | 以字节形式获取响应体(返回 `g.Result[g.Bytes]`) | | `JSON(v)` | 解码 JSON 到结构体 | | `XML(v)` | 解码 XML 到结构体 | | `UTF8()` | 转换为 UTF-8(返回 `g.Result[g.String]`) | | `Stream()` | 获取支持 Close 的 StreamReader 用于流式处理 | | `SSE(fn)` | 处理服务器发送事件 | | `Dump(file)` | 保存到文件 | | `Contains(pattern)` | 检查是否包含指定模式 | | `Limit(n)` | 限制响应体大小 | | `WithContext(ctx)` | 设置上下文以取消读取操作 | | `Close()` | 关闭响应体读取器 | ## 🤝 贡献 欢迎贡献代码!请随时提交 Pull Request。如需进行重大更改,请先打开 issue 进行讨论。 1. Fork 该仓库 2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) 4. 推送到分支 (`git push origin feature/AmazingFeature`) 5. 打开 Pull Request ## ❤️ 支持 / 赞助 如果你喜欢 **Surf** 并希望帮助开发持续进行,你可以通过加密货币捐赠来支持该项目: | USDT | TON | SOL | BTC | ETH | |--------------|-----|-----|-----|-----| | | | | | | 感谢你的支持! ## 📄 许可证 本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。 ## 🙏 感谢 - 基于 [enetx/http](https://github.com/enetx/http) 构建,增强 HTTP 功能 - HTTP/3 支持和完整的 QUIC 指纹识别由 [QUIC-GO](https://github.com/quic-go/quic-go) 提供支持 - TLS 指纹识别由 [uTLS](https://github.com/refraction-networking/utls) 提供支持 - 泛型工具来自 [enetx/g](https://github.com/enetx/g) ## 📞 支持 - **问题反馈**:[GitHub Issues](https://github.com/enetx/surf/issues) - **讨论**:[GitHub Discussions](https://github.com/enetx/surf/discussions) - **文档**:[pkg.go.dev](https://pkg.go.dev/github.com/enetx/surf)

由 Surf 贡献者用心 ❤️ 打造

标签:Chrome 伪装, enetx, EVTX分析, Firefox 伪装, Go HTTP 客户端, Go 语言库, HelloID, HelloSpec, HTTP/3, HTTP 客户端库, JA3, JA4, QUIC, SURF, TLS 指纹, WebKit 表单边界, Web 自动化, 中间件, 反机器人绕过, 多架构支持, 平台伪装, 开源 Go 库, 指纹伪造, 浏览器伪装, 浏览器指纹, 网络安全, 隐私保护