Danny-Dasilva/CycleTLS

GitHub: Danny-Dasilva/CycleTLS

一款用于伪造 TLS/JA3 指纹、支持多协议与代理的高性能 Go 工具,帮助隐蔽探测与绕过安全检测。

Stars: 1348 | Forks: 212

# CycleTLS
CycleTLS
接受社区支持和 PR ![build](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/aad7b9e71c235815.svg) [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg)](http://godoc.org/github.com/Danny-Dasilva/CycleTLS/cycletls) [![license](https://img.shields.io/github/license/Danny-Dasilva/CycleTLS.svg)](https://github.com/Danny-Dasilva/CycleTLS/blob/main/LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/Danny-Dasilva/CycleTLS/cycletls)](https://goreportcard.com/report/github.com/Danny-Dasilva/CycleTLS/cycletls) [![npm version](https://img.shields.io/npm/v/cycletls.svg)](https://www.npmjs.org/package/cycletls) chat on Discord
如果你有 API 变更或功能请求,请随时打开一个 [问题](https://github.com/Danny-Dasilva/CycleTLS/issues/new/choose) # 🚀 功能特性 - [高性能](#-performance) 使用内置的 goroutine 池处理异步请求 - 通过 [fhttp](https://github.com/useflyent/fhttp) 自定义头部顺序 - 代理支持 | Socks4、Socks5、Socks5h - Ja3 Token 配置 - HTTP/3 和 QUIC 支持 - WebSocket 客户端 - 服务器发送事件 (SSE) - 连接复用 - JA4 指纹识别 # 目录 * [目录](#table-of-contents) * [安装](#installation) * [用法](#usage) * [快速开始(JavaScript)](#example-cycletls-request-for-typescript-and-javascript) * [快速开始(Golang)](#example-cycletls-request-for-golang) * [初始化 CycleTLS](#creating-an-instance) * [API/方法](#cycletls-alias-methods) * [请求配置](#cycletls-request-config) * [响应模式](#cycletls-response-schema) * [多请求示例](#multiple-requests-example-for-typescript-and-javascript) * [流式响应](#streaming-responses-axios-style) * [本地设置](#dev-setup) * [常见问题](#questions) * [许可证](#license) ## 依赖 ``` node ^v18.0 golang ^v1.21x ``` ## 安装 Node Js ``` $ npm install cycletls ``` Golang ``` $ go get github.com/Danny-Dasilva/CycleTLS/cycletls ``` # 用法 ## TypeScript 和 JavaScript 的 CycleTLS 请求示例 你可以在 `tests/simple.test.ts` 中运行此测试 ``` const initCycleTLS = require('cycletls'); // Typescript: import initCycleTLS from 'cycletls'; (async () => { // Initiate CycleTLS const cycleTLS = await initCycleTLS(); // Send request const response = await cycleTLS('https://ja3er.com/json', { body: '', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', proxy: 'http://username:password@hostname.com:443' }, 'get'); // Parse response as JSON const data = await response.json(); console.log(data); // Cleanly exit CycleTLS await cycleTLS.exit(); })(); ``` ## JA4R(原始)TLS 指纹识别 JA4R 是 JA4 指纹识别的原始格式,允许显式配置密码套件、扩展和签名算法: ### JavaScript 示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // Chrome JA4R fingerprint (raw format) const response = await cycleTLS('https://tls.peet.ws/api/all', { ja4r: 't13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601' }); const data = await response.json(); console.log('JA4:', data.tls.ja4); console.log('JA4_r:', data.tls.ja4_r); console.log('TLS Version:', data.tls.tls_version_negotiated); await cycleTLS.exit(); })(); ``` ### Golang JA4R 示例 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init(cycletls.WithRawBytes()) defer client.Close() // Chrome JA4R fingerprint (raw format) response, err := client.Do("https://tls.peet.ws/api/all", cycletls.Options{ Ja4r: "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601", UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", }, "GET") if err != nil { log.Fatal(err) } log.Println("Response with JA4R:", response.Status) } ``` ## HTTP/2 指纹识别 HTTP/2 指纹识别允许你模拟特定的浏览器 HTTP/2 实现: ### JavaScript 示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // Firefox HTTP/2 fingerprint const response = await cycleTLS('https://tls.peet.ws/api/all', { http2Fingerprint: '1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0' }); const data = await response.json(); console.log('HTTP/2 Fingerprint:', data.http2.akamai_fingerprint); console.log('Settings:', data.http2.sent_frames[0].settings); await cycleTLS.exit(); })(); ``` ### Golang HTTP/2 示例 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() // Firefox HTTP/2 fingerprint response, err := client.Do("https://tls.peet.ws/api/all", cycletls.Options{ HTTP2Fingerprint: "1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s", UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0", }, "GET") if err != nil { log.Fatal(err) } log.Println("Response with HTTP/2 fingerprint:", response.Status) } ``` ### 常见浏览器 HTTP/2 指纹 | 浏览器 | HTTP/2 指纹 | 描述 | |---------|-------------------|-------------| | Firefox | `1:65536;2:0;4:131072;5:16384\|12517377\|0\|m,p,a,s` | 更小的窗口大小,MPAS 优先级 | | Chrome | `1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p` | 更大的窗口大小,MASP 优先级 | ### 组合指纹识别示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // Complete Chrome browser fingerprint with JA4R const response = await cycleTLS('https://tls.peet.ws/api/all', { ja4r: 't13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601', http2Fingerprint: '1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0' }); const data = await response.json(); console.log('Complete fingerprint applied successfully'); console.log('JA4:', data.tls.ja4); console.log('HTTP/2:', data.http2.akamai_fingerprint); await cycleTLS.exit(); })(); ``` ## 流式响应(Axios 风格) CycleTLS 支持 axios 兼容的流式响应,用于实时数据处理: ### 基本流式示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // Get streaming response const response = await cycleTLS.get('https://httpbin.org/stream/3', { headers: { Authorization: `Bearer your_token_here` }, responseType: 'stream', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0' }); const stream = response.data; stream.on('data', data => { console.log('Received chunk:', data.toString()); }); stream.on('end', () => { console.log("stream done"); await cycleTLS.exit(); }); stream.on('error', (error) => { console.error('Stream error:', error); await cycleTLS.exit(); }); })(); ``` ### 高级流式处理和错误处理 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); try { const response = await cycleTLS.get('https://httpbin.org/drip?numbytes=100&duration=2', { responseType: 'stream', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', }); console.log('Status:', response.status); console.log('Headers:', response.headers); const chunks = []; response.data.on('data', (chunk) => { chunks.push(chunk); console.log(`Received ${chunk.length} bytes`); }); response.data.on('end', () => { console.log('Stream complete'); const fullData = Buffer.concat(chunks); console.log('Total received:', fullData.length, 'bytes'); await cycleTLS.exit(); }); response.data.on('error', (error) => { console.error('Stream error:', error); await cycleTLS.exit(); }); } catch (error) { console.error('Request failed:', error); await cycleTLS.exit(); } })(); ``` ### 非流式响应(默认行为) 对于非流式响应,CycleTLS 的工作方式与之前相同: ``` // These return buffered responses (existing behavior) const jsonResponse = await cycleTLS.get('https://httpbin.org/json', { responseType: 'json' // or omit for default JSON parsing }); const jsonData = await jsonResponse.json(); console.log(jsonData); // Parsed JSON object const textResponse = await cycleTLS.get('https://httpbin.org/html', { responseType: 'text' }); const textData = await textResponse.text(); console.log(textData); // String content ``` ## Golang 的 CycleTLS 请求示例 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() response, err := client.Do("https://ja3er.com/json", cycletls.Options{ Body: "", Ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", EnableConnectionReuse: true, // Enable connection reuse for better performance }, "GET") if err != nil { log.Print("Request Failed: " + err.Error()) } log.Println(response) } ``` #### 使用自定义的 http.Client 示例
``` import ( "github.com/Danny-Dasilva/CycleTLS/cycletls" http "github.com/Danny-Dasilva/fhttp" // note this is a drop-in replacement for net/http ) func main() { ja3 := "771,52393-52392-52244-52243-49195-49199-49196-49200-49171-49172-156-157-47-53-10,65281-0-23-35-13-5-18-16-30032-11-10,29-23-24,0" ua := "Chrome Version 57.0.2987.110 (64-bit) Linux" cycleClient := &http.Client{ Transport: cycletls.NewTransport(ja3, ua), } resp, err := cycleClient.Get("https://tls.peet.ws/") ... } ```
#### 性能增强:原始字节选项 默认的 `Init()` 方法提供标准 v1 API 和 `chan Response`。对于性能关键的应用场景,可以使用 `WithRawBytes()` 选项处理原始 `[]byte` 响应: ``` package main import ( "encoding/json" "fmt" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { // Use WithRawBytes() option for performance enhancement client := cycletls.Init(cycletls.WithRawBytes()) defer client.Close() // Queue a request go func() { client.Queue("https://ja3er.com/json", cycletls.Options{ Ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }, "GET") }() // Performance pattern: receive raw bytes from RespChanV2 select { case responseBytes := <-client.RespChanV2: var response cycletls.Response json.Unmarshal(responseBytes, &response) fmt.Printf("Status: %d\n", response.Status) fmt.Printf("Body: %s\n", response.Body) // Alternative: still supports v1 pattern via RespChan case response := <-client.RespChan: fmt.Printf("Status: %d\n", response.Status) fmt.Printf("Body: %s\n", response.Body) } } ``` **注意:** 使用 `Init()` 以保持与 `chan Response` 的标准兼容性。当需要处理原始 `[]byte` 响应的性能优势时,请使用 `Init(cycletls.WithRawBytes())`。 ## 创建实例 要创建 `cycleTLS` 实例,可以执行以下操作: #### JavaScript ``` // The initCycleTLS function spawns a Golang process that handles all requests concurrently via goroutine loops. const initCycleTLS = require('cycletls'); // import initCycleTLS from 'cycletls'; // Async/Await method const cycleTLS = await initCycleTLS(); // With optional configuration const cycleTLS = await initCycleTLS({ port: 9118, timeout: 30000 }); // .then method initCycleTLS().then((cycleTLS) => {}); ``` #### Golang ``` import ( "github.com/Danny-Dasilva/CycleTLS/cycletls" ) //The `Init` function initializes golang channels to process requests. client := cycletls.Init() ``` ## CycleTLS 别名方法 CycleTLS 提供以下方法: **cycleTLS(url, [config])** **cycleTLS.get(url, [config])** **cycleTLS.delete(url, [config])** **cycleTLS.head(url, [config])** **cycleTLS.options(url, [config])** **cycleTLS.post(url, [config])** **cycleTLS.put(url, config)** **cycleTLS.patch(url, [config])** URL 不是可选的,config 是可选的 ## CycleTLS 请求配置 ``` { // URL for the request (required if not specified as an argument) url: "https://example.com" // Method for the request ("head" | "get" | "post" | "put" | "delete" | "trace" | "options" | "connect" | "patch") method: "get" // Default method // Custom headers to send headers: { "Authorization": "Bearer someexampletoken" } // Custom cookies to send Cookies: [{ "name": "key", "value": "val", "path": "/docs", "domain": "google.com", "expires": "Mon, 02-Jan-2022 15:04:05 EST" "maxAge": 90, "secure": false, "httpOnly": true, "sameSite": "Lax" }], // Body to send with request (must be a string - cannot pass an object) body: '', // JA3 token to send with request ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', // JA4R token for enhanced fingerprinting (raw format) ja4r: 't13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601', // User agent for request userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', // Proxy to send request through (supports http, socks4, socks5, socks5h) proxy: 'http://username:password@hostname.com:443', // Amount of seconds before request timeout (default: 7) timeout: 2, // Toggle if CycleTLS should follow redirects disableRedirect: true, // Custom header order to send with request (This value will overwrite default header order) headerOrder: ["cache-control", "connection", "host"], // Toggle if CycleTLS should skip verify certificate (If InsecureSkipVerify is true, TLS accepts any certificate presented by the server and any host name in that certificate.) insecureSkipVerify: false // Forces CycleTLS to do a http1 handshake forceHTTP1: false // Forces HTTP/3 protocol forceHTTP3: false // Enable connection reuse across requests enableConnectionReuse: true // HTTP/2 fingerprint http2Fingerprint: '1:65536;4:131072;5:16384|12517377|3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241|m,p,a,s' // QUIC fingerprint for HTTP/3 quicFingerprint: '16030106f2010006ee03039a2b98d81139db0e128ea09eff...' // JA4H HTTP client fingerprint ja4h: 'ge11_73a4f1e_8b3fce7' } ``` ## 响应解压缩 CycleTLS 会自动处理压缩响应的解压缩,无需额外配置。 **支持的压缩格式** * `gzip` - 自动解压缩 * `deflate` - 自动解压缩 * `brotli` - 自动解压缩 ### JavaScript 解压缩示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // CycleTLS automatically handles compressed responses const response = await cycleTLS('https://httpbin.org/gzip', { headers: { 'Accept-Encoding': 'gzip, deflate, br' // Optional - CycleTLS sets this automatically } }); // Response is automatically decompressed const data = await response.json(); console.log('Decompressed data:', data); await cycleTLS.exit(); })(); ``` ### Golang 解压缩示例 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() // CycleTLS automatically handles compressed responses response, err := client.Do("https://httpbin.org/gzip", cycletls.Options{ Headers: map[string]string{ "Accept-Encoding": "gzip, deflate, br", // Optional - set automatically }, }, "GET") if err != nil { log.Fatal(err) } // Response body is automatically decompressed log.Println("Decompressed response:", response.Body) // Parse as JSON if needed jsonData := response.JSONBody() log.Println("Parsed JSON:", jsonData) } ``` **注意:** 解压缩会根据 `Content-Encoding` 头自动进行,无需手动解压响应。 ## 超时和错误处理 CycleTLS 提供全面的超时处理和错误响应: ### 超时配置 ``` // JavaScript timeout example const response = await cycleTLS('https://httpbin.org/delay/10', { timeout: 5, // 5 seconds timeout ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0' }); ``` ``` // Golang timeout example response, err := client.Do("https://httpbin.org/delay/10", cycletls.Options{ Timeout: 5, // 5 seconds timeout UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }, "GET") ``` ### 超时错误响应 当请求超时时,CycleTLS 返回以下响应: - **状态码**:`408`(请求超时) - **响应体**:包含描述超时的错误信息 - **错误**:JavaScript 将获得响应对象,Go 将获得 `err != nil` ### JavaScript 超时错误处理 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); try { const response = await cycleTLS('https://httpbin.org/delay/10', { timeout: 2, // Will timeout after 2 seconds ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0' }); if (response.status === 408) { console.log('Request timed out:', response.body); } else { const data = await response.json(); console.log('Success:', data); } } catch (error) { console.error('Request failed:', error); } finally { await cycleTLS.exit(); } })(); ``` ### Golang 超时错误处理 ``` package main import ( "log" "strings" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() response, err := client.Do("https://httpbin.org/delay/10", cycletls.Options{ Timeout: 2, // Will timeout after 2 seconds }, "GET") if err != nil { log.Printf("Request failed: %v", err) return } // Check for timeout response if response.Status == 408 { log.Printf("Request timed out: %s", response.Body) return } // Check for other error conditions if strings.Contains(response.Body, "timeout") { log.Printf("Timeout detected in response: %s", response.Body) return } // Success case log.Printf("Request succeeded: %d", response.Status) } ``` ### 常见错误状态码 - **408**:请求超时 - **502**:错误网关(代理/连接问题) - **503**:服务不可用 - **0**:连接失败(网络错误) ## 代理支持 CycleTLS 支持多种代理协议路由请求: ### 支持的代理类型 - **HTTP 代理**:`http://proxy.example.com:8080` - **HTTPS 代理**:`https://proxy.example.com:8080` - **SOCKS4**:`socks4://proxy.example.com:1080` - **SOCKS5**:`socks5://proxy.example.com:1080` - **SOCKS5h**:`socks5h://proxy.example.com:1080`(通过代理服务器解析主机名) ### JavaScript 代理示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // HTTP Proxy with authentication const httpResponse = await cycleTLS('https://httpbin.org/ip', { proxy: 'http://username:password@proxy.example.com:8080' }); // SOCKS5 Proxy const socksResponse = await cycleTLS('https://httpbin.org/ip', { proxy: 'socks5://proxy.example.com:1080' }); // SOCKS5h (hostname resolution through proxy) const socks5hResponse = await cycleTLS('https://httpbin.org/ip', { proxy: 'socks5h://proxy.example.com:1080' }); console.log('HTTP Proxy IP:', await httpResponse.json()); console.log('SOCKS5 IP:', await socksResponse.json()); await cycleTLS.exit(); })(); ``` ### Golang 代理示例 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() // HTTP Proxy with authentication httpResponse, err := client.Do("https://httpbin.org/ip", cycletls.Options{ Proxy: "http://username:password@proxy.example.com:8080", }, "GET") if err != nil { log.Printf("HTTP proxy request failed: %v", err) } else { log.Printf("HTTP Proxy Response: %s", httpResponse.Body) } // SOCKS4 Proxy socks4Response, err := client.Do("https://httpbin.org/ip", cycletls.Options{ Proxy: "socks4://proxy.example.com:1080", }, "GET") if err != nil { log.Printf("SOCKS4 proxy request failed: %v", err) } else { log.Printf("SOCKS4 Response: %s", socks4Response.Body) } // SOCKS5 Proxy socks5Response, err := client.Do("https://httpbin.org/ip", cycletls.Options{ Proxy: "socks5://proxy.example.com:1080", }, "GET") if err != nil { log.Printf("SOCKS5 proxy request failed: %v", err) } else { log.Printf("SOCKS5 Response: %s", socks5Response.Body) } // SOCKS5h (hostname resolved through proxy) socks5hResponse, err := client.Do("https://httpbin.org/ip", cycletls.Options{ Proxy: "socks5h://proxy.example.com:1080", }, "GET") if err != nil { log.Printf("SOCKS5h proxy request failed: %v", err) } else { log.Printf("SOCKS5h Response: %s", socks5hResponse.Body) } } ``` ### 代理错误处理 ``` // Check for proxy connection errors response, err := client.Do("https://example.com", cycletls.Options{ Proxy: "socks5://proxy.example.com:1080", Timeout: 10, }, "GET") if err != nil { log.Printf("Proxy connection failed: %v", err) return } // Check for proxy authentication errors if response.Status == 407 { log.Printf("Proxy authentication required") return } // Check for proxy server errors if response.Status == 502 { log.Printf("Bad gateway - proxy server error") return } ``` **注意:** SOCKS5h 通过代理服务器解析主机名,提供更好的隐私并允许通过代理访问内部网络。 ## CycleTLS 响应模式 ``` { // Status code returned from server (Number) status: 200, // Body returned from the server (String) body: "", // Headers returned from the server (Object) headers: { "some": "header", ... }, // FinalUrl returned from the server (String). This field is useful when redirection is active. finalUrl: "https://final.url/" } ``` ## 多请求示例(TypeScript 和 JavaScript) 如果在 JavaScript 环境中使用 CycleTLS,它会启动一个 Golang 进程来处理请求。该 Golang 进程会并发处理请求池中的请求。由于此特性,CycleTLS 会尽快返回响应对象(**不保证顺序**) 如果你在 JavaScript 中使用 CycleTLS,需要退出实例以防止僵尸进程。以下示例展示了如何安全退出 CycleTLS 以处理多个请求(注意:调用 `exit()` 会终止所有进行中的请求)。如果你的工作流需要请求持续运行,模块如 [exit-hook](https://www.npmjs.com/package/exit-hook) 可以作为安全退出的替代方案。 ``` const initCycleTLS = require("cycletls"); // Typescript: import initCycleTLS from 'cycletls'; // Defining JA3 token and user agent const ja3 = "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0"; const userAgent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0"; // Defining multiple requests const requestDict = { "https://httpbin.org/user-agent": { ja3: ja3, userAgent: userAgent, }, "http://httpbin.org/post": { body: '{"field":"POST-VAL"}', method: "POST", }, "http://httpbin.org/cookies": { cookies: [ { name: "example1", value: "aaaaaaa", expires: "Mon, 02-Jan-2022 15:04:05 EST", }, ], }, }; // Anonymous async function (async () => { // Initiate CycleTLS const cycleTLS = await initCycleTLS(); // Create promises for all requests const promises = Object.entries(requestDict).map(async ([url, params]) => { const response = await cycleTLS( url, { body: params.body ?? "", ja3: params.ja3 ?? ja3, userAgent: params.userAgent ?? userAgent, headers: params.headers, cookies: params.cookies, }, params.method ?? "GET"); // Parse response based on content type const data = await response.json(); console.log(url, data); return { url, data }; }); // Wait for all requests to complete await Promise.all(promises); // Cleanly exit CycleTLS await cycleTLS.exit(); })(); ``` ## 多请求示例(Golang) Golang 包的通用预期是用户实现工作池或任何形式的 goroutine/异步处理。此包包含一个内置的队列方法,利用工作池/通道处理针对一组 URL 的长时间运行异步请求。 ``` package main import ( "log" cycletls "github.com/Danny-Dasilva/CycleTLS/cycletls" ) // Static variables var ( ja3 = "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0" userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36" ) // RequestConfig holds the configuration for each request. type RequestConfig struct { URL string Method string Options cycletls.Options } func main() { client := cycletls.Init(true) // Initialize with worker pool // Define the requests requests := []RequestConfig{ { URL: "http://httpbin.org/delay/4", Method: "GET", Options: cycletls.Options{ Ja3: ja3, UserAgent: userAgent, }, }, { URL: "http://httpbin.org/post", Method: "POST", Options: cycletls.Options{ Body: `{"field":"POST-VAL"}`, Ja3: ja3, UserAgent: userAgent, }, }, { URL: "http://httpbin.org/cookies", Method: "GET", Options: cycletls.Options{ Ja3: ja3, UserAgent: userAgent, Cookies: []cycletls.Cookie{ { Name: "example1", Value: "aaaaaaa", }, }, }, }, } // Queue the requests for _, req := range requests { client.Queue(req.URL, req.Options, req.Method) } // Asynchronously read responses as soon as they are available // They will return as soon as they are processed // e.g. Delay 3 will be returned last for i := 0; i < len(requests); i++ { response := <-client.RespChan log.Println("Response:", response) } // Close the client client.Close() } ``` # 开发设置 如果你想自行编译 CycleTLS,请使用以下命令: 设置模块感知模式 `go env -w GO111MODULE=off` 安装 Go 依赖 `go get github.com/Danny-Dasilva/CycleTLS/cycletls` 安装 npm 包(此命令会处理上述操作) `npm install` ### 重新编译 src 文件夹中的 index.ts `npm run build` ### 重新编译 golang 文件夹中的 Go 文件 全部 `npm run build:go` Windows `npm run build:go:windows:amd64` Linux `npm run build:go:linux:amd64` Mac `npm run build:go:mac:arm64` 你可以在 `package.json` 中查看可用的编译选项 ## 问题 ### 如何设置 Cookie
有两种简单的接口方式处理 Cookie: ### JavaScript 简单 Cookie 配置 ``` const initCycleTLS = require("cycletls"); (async () => { // Initiate cycleTLS const cycleTLS = await initCycleTLS(); const response = await cycleTLS("https://httpbin.org/cookies", { cookies: { cookie1: "value1", cookie2: "value2", }, }); const data = await response.json(); console.log(data); /* Expected { "cookies": { "cookie1": "value1", "cookie2": "value2" } } */ await cycleTLS.exit(); })(); ``` 在此示例中,你可以在对象中设置 Cookie 的 `name` 和 `value` ### JavaScript 复杂 Cookie 配置 如果你需要更精细地控制 Cookie 参数,可以访问完整的底层 Go 结构体: 以下是可设置的值: ``` export interface Cookie { name: string; value: string; path?: string; domain?: string; expires?: string; rawExpires?: string; maxAge?: number; secure?: boolean; httpOnly?: boolean; sameSite?: string; unparsed?: string; } ``` 你可以在请求中使用它们,如下所示: ``` const initCycleTLS = require("cycletls"); (async () => { // Initiate cycleTLS const cycleTLS = await initCycleTLS(); const complexCookies = [ { name: "cookie1", value: "value1", domain: "httpbin.org", }, { name: "cookie2", value: "value2", domain: "httpbin.org", }, ]; const response = await cycleTLS("https://httpbin.org/cookies", { cookies: complexCookies, }); const data = await response.json(); console.log(data); /* Expected { "cookies": { "cookie1": "value1", "cookie2": "value2" } } */ await cycleTLS.exit(); })(); ``` ### Golang 配置 Cookie ``` package main import ( "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { resp, err := client.Do("https://httpbin.org/cookies", cycletls.Options{ Body: "", Ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", Cookies: []cycletls.Cookie{{Name: "cookie1", Value: "value1"}, {Name: "cookie2", Value: "value2"}}, }, "GET") if err != nil { log.Print("Request Failed: " + err.Error()) } log.Println(resp.Body) /* Expected { "cookies": { "cookie1": "value1", "cookie2": "value2" } } */ // Alternatively if you want access to values within a map log.Println(resp.JSONBody()) /* Expected map[cookies:map[cookie1:value1 cookie2:value2]] */ } ``` 如果你有特定文件类型支持的功能请求,请自由打开一个 [问题](https://github.com/Danny-Dasilva/CycleTLS/issues/new/choose)。
### 如何在 CycleTLS 中使用 CookieJar?
### JavaScript 中的 CookieJar ``` const initCycleTLS = require("cycletls"); const tough = require("tough-cookie"); const Cookie = tough.Cookie; (async () => { // Initiate cycleTLS and CookieJar const cycleTLS = await initCycleTLS(); const cookieJar = new tough.CookieJar(); // Capture a set cookie const firstResponse = await cycleTLS.get( "https://httpbin.org/cookies/set?freeform=test", { disableRedirect: true, } ); // Now use the processCookies function to add the cookies from the response headers to the cookie jar await processCookies( firstResponse, "https://httpbin.org/cookies/set?freeform=test", cookieJar ); // Now send a second to verify we have our cookies const secondResponse = await cycleTLS.get("https://httpbin.org/cookies", { headers: { cookie: await cookieJar.getCookieString("https://httpbin.org/cookies"), }, }); // Verify cookies were set const data = await secondResponse.json(); console.log(data) /* Expected { "cookies": { "freeform": "test" } } */ await cycleTLS.exit(); })(); async function processCookies(response, url, cookieJar) { if (response.headers["Set-Cookie"] instanceof Array) { response.headers["Set-Cookie"].map( async (cookieString) => await cookieJar.setCookie(cookieString, url) ); } else { await cookieJar.setCookie(response.headers["Set-Cookie"], url); } } ``` ### Golang 中的 CookieJar ``` package main import ( "github.com/Danny-Dasilva/CycleTLS/cycletls" "log" "net/http/cookiejar" "net/url" "strings" ) func main() { client := cycletls.Init() jar, err := cookiejar.New(nil) if err != nil { log.Fatal(err) } // First request to set cookie firstResponse, err := client.Do("https://httpbin.org/cookies/set?a=1&b=2&c=3", cycletls.Options{ Body: "", Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0", UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36", DisableRedirect: true, }, "GET") if err != nil { log.Fatal(err) } firstURL, _ := url.Parse(firstResponse.FinalUrl) jar.SetCookies( firstURL, firstResponse.Cookies) // Second request to verify cookies, including the cookies from the first response secondResponse, err := client.Do("https://httpbin.org/cookies", cycletls.Options{ Body: "", Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0", UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36", Headers: map[string]string{ "Cookie": getHeadersFromJar(jar, firstURL), }, }, "GET") if err != nil { log.Fatal(err) } log.Println("Second Response body:", secondResponse.Body) } func getHeadersFromJar(jar *cookiejar.Jar, url *url.URL) string { cookies := jar.Cookies(url) var cookieStrs []string for _, cookie := range cookies { cookieStrs = append(cookieStrs, cookie.Name+"="+cookie.Value) } return strings.Join(cookieStrs, "; ") } ```
### 如何在 CycleTLS 中发送 multipart/form-data?
### JavaScript 文本 form-data ``` const initCycleTLS = require("cycletls"); const FormData = require('form-data'); (async () => { const cycleTLS = await initCycleTLS(); const formData = new FormData(); formData.append("key1", "value1"); formData.append("key2", "value2"); const response = await cycleTLS('http://httpbin.org/post', { body: formData, headers: formData.getHeaders(), // Use formData.getHeaders() for proper content-type }, 'post'); const data = await response.json(); console.log(data); await cycleTLS.exit(); })(); ``` ### JavaScript 文件 form-data ``` const initCycleTLS = require("cycletls"); const FormData = require('form-data'); const fs = require('fs'); (async () => { const cycleTLS = await initCycleTLS(); const formData = new FormData(); const fileStream = fs.createReadStream("../go.mod"); formData.append('file', fileStream); const response = await cycleTLS('http://httpbin.org/post', { body: formData, headers: formData.getHeaders(), // Use formData.getHeaders() for proper content-type }, 'post'); const data = await response.json(); console.log(data); await cycleTLS.exit(); })(); ``` ### Golang 文本 form-data ``` package main import ( "bytes" "github.com/Danny-Dasilva/CycleTLS/cycletls" "log" "mime/multipart" ) func main() { client := cycletls.Init() // Prepare a buffer to write our multipart form var requestBody bytes.Buffer multipartWriter := multipart.NewWriter(&requestBody) // Add form fields multipartWriter.WriteField("key1", "value1") multipartWriter.WriteField("key2", "value2") contentType := multipartWriter.FormDataContentType() // Close the writer before making the request multipartWriter.Close() response, err := client.Do("http://httpbin.org/post", cycletls.Options{ Body: requestBody.String(), Headers: map[string]string{ "Content-Type": contentType, }, }, "POST") if err != nil { log.Print("Request Failed: " + err.Error()) } log.Println(response.Body) } ``` ### Golang 文件上传 form-data ``` package main import ( "github.com/Danny-Dasilva/CycleTLS/cycletls" "bytes" "io" "log" "mime/multipart" "os" ) func main() { client := cycletls.Init() // Prepare a buffer to write our multipart form var requestBody bytes.Buffer multipartWriter := multipart.NewWriter(&requestBody) // Add a file fileWriter, err := multipartWriter.CreateFormFile("fieldname", "filename") if err != nil { log.Fatal("CreateFormFile Error: ", err) } // Open the file that you want to upload file, err := os.Open("path/to/your/file") if err != nil { log.Fatal("File Open Error: ", err) } defer file.Close() // Copy the file to the multipart writer _, err = io.Copy(fileWriter, file) if err != nil { log.Fatal("File Copy Error: ", err) } // Close the writer before making the request contentType := multipartWriter.FormDataContentType() multipartWriter.Close() response, err := client.Do("http://httpbin.org/post", cycletls.Options{ Body: requestBody.String(), Headers: map[string]string{ "Content-Type": contentType, }, }, "POST") if err != nil { log.Print("Request Failed: " + err.Error()) } log.Println(response.Body) } ``` 如果需要,可以为 Go 语言添加编码助手。
### 如何发送 application/x-www-form-urlencoded 的 POST 请求?
### JavaScript application/x-www-form-urlencoded 表单 ``` const initCycleTLS = require("cycletls"); (async () => { const cycleTLS = await initCycleTLS(); const urlEncodedData = new URLSearchParams(); urlEncodedData.append('key1', 'value1'); urlEncodedData.append('key2', 'value2'); const response = await cycleTLS('http://httpbin.org/post', { body: urlEncodedData, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }, 'post'); const data = await response.json(); console.log(data); await cycleTLS.exit(); })(); ``` ### Golang application/x-www-form-urlencoded 表单 ``` package main import ( "log" "net/url" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() // Prepare form data form := url.Values{} form.Add("key1", "value1") form.Add("key2", "value2") response, err := client.Do("http://httpbin.org/post", cycletls.Options{ Body: form.Encode(), Headers: map[string]string{ "Content-Type": "application/x-www-form-urlencoded", }, }, "POST") if err != nil { log.Print("Request Failed: " + err.Error()) } log.Println(response.Body) } ```
### 如何下载图片和视频?
图片和视频如果带有支持的 `Content-Type` 头部,会以字符串格式返回原始二进制数据。 **支持的媒体类型** * `image/svg+xml` * `image/webp` * `image/jpeg` * `image/png` * `image/gif` * `application/pdf` * `video/mp4` * `video/webm` * `video/avi` * `video/quicktime` **重要提示:** 媒体数据**不是** base64 编码的,而是原始二进制数据转换为字符串格式。 要写入文件,可以使用以下方法: ### JavaScript 媒体下载示例 ``` const initCycleTLS = require("cycletls"); var fs = require("fs"); (async () => { const cycleTLS = await initCycleTLS(); // Download image using arrayBuffer() - correct method const jpegImage = await cycleTLS("http://httpbin.org/image/jpeg", { ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", userAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }); const jpegBuffer = await jpegImage.arrayBuffer(); fs.writeFileSync('./images/output.jpeg', Buffer.from(jpegBuffer)); console.log('JPEG image downloaded'); // Download PNG const pngImage = await cycleTLS("http://httpbin.org/image/png", { ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", userAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }); const pngBuffer = await pngImage.arrayBuffer(); fs.writeFileSync('./images/output.png', Buffer.from(pngBuffer)); console.log('PNG image downloaded'); // Download WebP const webpImage = await cycleTLS("http://httpbin.org/image/webp", { ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", userAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }); const webpBuffer = await webpImage.arrayBuffer(); fs.writeFileSync('./images/output.webp', Buffer.from(webpBuffer)); console.log('WebP image downloaded'); // Download video const videoResponse = await cycleTLS("https://sample-videos.com/zip/10/mp4/SampleVideo_360x240_1mb.mp4", { ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", userAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }); const videoBuffer = await videoResponse.arrayBuffer(); fs.writeFileSync('./videos/sample_video.mp4', Buffer.from(videoBuffer)); console.log('Video downloaded'); await cycleTLS.exit(); })(); ``` ### 流式二进制数据示例 对于大文件或实时二进制流: ``` const initCycleTLS = require("cycletls"); var fs = require("fs"); (async () => { const cycleTLS = await initCycleTLS(); // Stream large video file const response = await cycleTLS("https://sample-videos.com/zip/25/mp4/SampleVideo_1280x720_5mb.mp4", { responseType: 'stream', ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0", userAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0", }); const stream = response.data; const writeStream = fs.createWriteStream('./videos/large_video.mp4'); let totalSize = 0; stream.on('data', (chunk) => { totalSize += chunk.length; console.log(`Downloaded ${totalSize} bytes`); writeStream.write(chunk); }); stream.on('end', () => { writeStream.end(); console.log(`Stream complete. Total size: ${totalSize} bytes`); await cycleTLS.exit(); }); stream.on('error', (error) => { console.error('Stream error:', error); writeStream.end(); await cycleTLS.exit(); }); })(); ``` ### Golang 媒体下载示例 ``` package main import ( "log" "os" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func writeMedia(filepath string, data string) error { // Convert string body to bytes (raw binary data) bodyBytes := []byte(data) f, err := os.Create(filepath) if err != nil { return err } defer f.Close() if _, err := f.Write(bodyBytes); err != nil { return err } return f.Sync() } func main() { client := cycletls.Init() defer client.Close() // Download image response, err := client.Do("http://httpbin.org/image/jpeg", cycletls.Options{ Body: "", Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0", UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", }, "GET") if err != nil { log.Fatal("Image download failed: ", err) } if err := writeMedia("test.jpeg", response.Body); err != nil { log.Fatal("Image write failed: ", err) } log.Println("Image downloaded successfully") // Download video videoResponse, err := client.Do("https://sample-videos.com/zip/10/mp4/SampleVideo_360x240_1mb.mp4", cycletls.Options{ Body: "", Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0", UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", }, "GET") if err != nil { log.Fatal("Video download failed: ", err) } if err := writeMedia("sample_video.mp4", videoResponse.Body); err != nil { log.Fatal("Video write failed: ", err) } log.Println("Video downloaded successfully") } ``` 计划添加更多文件类型支持。 如果你有特定文件类型支持的功能请求,请自由打开一个 [问题](https://github.com/Danny-Dasilva/CycleTLS/issues/new/choose)。
### 如何使用连接复用?
连接复用允许你在多个请求中复用 TLS 连接,减少握手开销并提升性能。 ### Golang 连接复用 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { // Initialize without worker pool for better connection management client := cycletls.Init(false) defer client.Close() // Enable connection reuse in the options options := cycletls.Options{ Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0", UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36", EnableConnectionReuse: true, // Enable connection reuse } // First request - establishes connection resp1, err := client.Do("https://httpbin.org/get", options, "GET") if err != nil { log.Fatal("First request failed: ", err) } log.Println("First request status:", resp1.Status) // Second request - reuses connection resp2, err := client.Do("https://httpbin.org/headers", options, "GET") if err != nil { log.Fatal("Second request failed: ", err) } log.Println("Second request status:", resp2.Status) // Connection is reused for requests to the same host } ```
### 如何使用 HTTP/3 和 QUIC?
CycleTLS 现在支持带有自定义 QUIC 指纹识别的 HTTP/3 协议。 ### Golang HTTP/3 基础用法 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() // Force HTTP/3 response, err := client.Do("https://cloudflare-quic.com/", cycletls.Options{ ForceHTTP3: true, UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", InsecureSkipVerify: true, }, "GET") if err != nil { log.Fatal("Request failed: ", err) } log.Println("Response over HTTP/3:", response.Status) } ``` ### Golang QUIC 指纹识别 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() // Custom QUIC fingerprint quicFingerprint := "16030106f2010006ee03039a2b98d81139db0e128ea09eff6874549c219b543fb6dbaa7e4dbfe9e31602c620ce04c4026f019442affade7fed8ba66e022e186f77f1c670fd992f33c0143f120020aaaa130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035010006851a1a00000010000e000c02683208687474702f312e31002b000706dada03040303..." response, err := client.Do("https://cloudflare-quic.com/", cycletls.Options{ QUICFingerprint: quicFingerprint, ForceHTTP3: true, UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", InsecureSkipVerify: true, }, "GET") if err != nil { log.Fatal("Request failed: ", err) } log.Println("Response with QUIC fingerprint:", response.Status) } ``` ### Golang HTTP/3 传输直接使用 ``` package main import ( "crypto/tls" "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" http "github.com/Danny-Dasilva/fhttp" ) func main() { // Create TLS config tlsConfig := &tls.Config{ InsecureSkipVerify: true, } // Create HTTP/3 transport transport := cycletls.NewHTTP3Transport(tlsConfig) // Create request req, err := http.NewRequest("GET", "https://cloudflare-quic.com/", nil) if err != nil { log.Fatal("Failed to create request: ", err) } // Send request resp, err := transport.RoundTrip(req) if err != nil { log.Fatal("Request failed: ", err) } defer resp.Body.Close() log.Println("Direct HTTP/3 response:", resp.Status) } ```
### 如何使用 WebSocket 支持?
CycleTLS 提供支持自定义 TLS 指纹识别的 WebSocket 客户端。 ### JavaScript WebSocket 示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // WebSocket connection with TLS fingerprinting const wsResponse = await cycleTLS.ws('wss://echo.websocket.org', { ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', headers: { 'Sec-WebSocket-Protocol': 'echo-protocol' } }); // Check connection status if (wsResponse.status === 101) { console.log('WebSocket upgrade successful'); console.log('Response headers:', wsResponse.headers); } await cycleTLS.exit(); })(); ``` ### Golang WebSocket 示例 ``` package main import ( "log" "net/http" "github.com/Danny-Dasilva/CycleTLS/cycletls" "github.com/gorilla/websocket" utls "github.com/refraction-networking/utls" ) func main() { // Create TLS config tlsConfig := &utls.Config{ InsecureSkipVerify: true, } // Create headers headers := make(http.Header) headers.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36") // Create WebSocket client wsClient := cycletls.NewWebSocketClient(tlsConfig, headers) // Connect to WebSocket server conn, resp, err := wsClient.Connect("wss://echo.websocket.org/") if err != nil { log.Fatal("WebSocket connection failed: ", err) } defer conn.Close() log.Println("WebSocket connected, status:", resp.StatusCode) // Send message testMessage := "Hello, WebSocket!" if err := conn.WriteMessage(websocket.TextMessage, []byte(testMessage)); err != nil { log.Fatal("Failed to send message: ", err) } // Read response messageType, message, err := conn.ReadMessage() if err != nil { log.Fatal("Failed to read message: ", err) } log.Printf("Received message type %d: %s\n", messageType, string(message)) } ``` ### Golang WebSocket 响应包装器 ``` package main import ( "log" "net/http" "github.com/Danny-Dasilva/CycleTLS/cycletls" "github.com/gorilla/websocket" utls "github.com/refraction-networking/utls" ) func main() { // Setup WebSocket client tlsConfig := &utls.Config{ InsecureSkipVerify: true, } headers := make(http.Header) headers.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") wsClient := cycletls.NewWebSocketClient(tlsConfig, headers) // Connect conn, _, err := wsClient.Connect("wss://echo.websocket.org/") if err != nil { log.Fatal("Connection failed: ", err) } // Create response wrapper wsResponse := &cycletls.WebSocketResponse{ Conn: conn, } defer wsResponse.Close() // Send message using wrapper if err := wsResponse.Send(websocket.TextMessage, []byte("Hello!")); err != nil { log.Fatal("Send failed: ", err) } // Receive message using wrapper messageType, message, err := wsResponse.Receive() if err != nil { log.Fatal("Receive failed: ", err) } log.Printf("Received: %s (type: %d) ", string(message), messageType) } ```
### 如何使用服务器发送事件 (SSE)?
CycleTLS 支持服务器发送事件,用于实时数据流: ### JavaScript SSE 示例 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // SSE connection with TLS fingerprinting const sseResponse = await cycleTLS.sse('https://example.com/events', { ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', headers: { 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache' } }); // Parse real-time events const eventData = await sseResponse.text(); console.log('SSE events:', eventData); await cycleTLS.exit(); })(); ``` ### JavaScript SSE 流式处理 ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); // SSE with streaming for real-time processing const response = await cycleTLS.get('https://example.com/events', { responseType: 'stream', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', headers: { 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache' } }); // Process SSE stream in real-time const stream = response.data; let buffer = ''; stream.on('data', (chunk) => { buffer += chunk.toString(); const lines = buffer.split(' '); // Process complete lines, keep incomplete line in buffer buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data:')) { const eventData = line.substring(5).trim(); console.log('Event data:', eventData); } else if (line.startsWith('event:')) { const eventType = line.substring(6).trim(); console.log('Event type:', eventType); } } }); stream.on('end', () => { console.log('SSE stream ended'); await cycleTLS.exit(); }); stream.on('error', (error) => { console.error('SSE stream error:', error); await cycleTLS.exit(); }); })(); ``` ### Golang SSE 客户端示例 ``` package main import ( "context" "fmt" "log" "time" "github.com/Danny-Dasilva/CycleTLS/cycletls" fhttp "github.com/Danny-Dasilva/fhttp" ) func main() { // Create HTTP client httpClient := &fhttp.Client{ Timeout: 30 * time.Second, } // Create headers headers := make(fhttp.Header) headers.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36") headers.Set("Accept", "text/event-stream") // Create SSE client sseClient := cycletls.NewSSEClient(httpClient, headers) // Connect to SSE server with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() sseResp, err := sseClient.Connect(ctx, "http://localhost:3333/events") if err != nil { log.Fatal("SSE connection failed: ", err) } defer sseResp.Close() // Read events eventCount := 0 for eventCount < 5 { event, err := sseResp.NextEvent() if err != nil { log.Printf("Error reading event: %v\n", err) break } if event != nil { eventCount++ fmt.Printf("Event #%d:\n", eventCount) fmt.Printf(" Type: %s\n", event.Event) fmt.Printf(" Data: %s\n", event.Data) fmt.Printf(" ID: %s\n", event.ID) } } } ``` ### Golang SSE 与浏览器配置 ``` package main import ( "context" "fmt" "log" "time" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { // Create browser configuration with TLS fingerprinting browser := cycletls.Browser{ UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", JA3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0", InsecureSkipVerify: true, } // Connect to SSE endpoint with timeout ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() response, err := browser.SSEConnect(ctx, "http://127.0.0.1:3333/events") if err != nil { log.Fatal("SSE connection failed: ", err) } defer response.Close() // Process events with detailed parsing for { event, err := response.NextEvent() if err != nil { log.Printf("Event stream ended: %v\n", err) break } if event != nil && event.Data != "" { fmt.Printf("Event Type: %s\n", event.Event) fmt.Printf("Event ID: %s\n", event.ID) fmt.Printf("Event Data: %s\n", event.Data) // Break after receiving specific event if event.Data == "done" { break } } } } ``` ### 浏览器 SSEConnect 方法 `Browser.SSEConnect` 方法提供带有 TLS 指纹识别支持的 SSE 连接: ``` type Browser struct { UserAgent string JA3 string JA4r string HTTP2Fingerprint string QUICFingerprint string InsecureSkipVerify bool ForceHTTP1 bool ForceHTTP3 bool } // SSEConnect establishes an SSE connection with browser fingerprinting func (b *Browser) SSEConnect(ctx context.Context, url string) (*SSEResponse, error) ``` ### SSE 事件结构 ``` type SSEEvent struct { ID string // Event ID from server Event string // Event type (custom event names) Data string // Event data payload Retry int64 // Reconnection time in milliseconds } ```
### 如何使用 JA4R 指纹识别?
JA4R 是用于配置 TLS 指纹的原始格式,支持显式指定密码套件和扩展。 ### Golang JA4R 指纹识别 ``` package main import ( "log" "github.com/Danny-Dasilva/CycleTLS/cycletls" ) func main() { client := cycletls.Init() defer client.Close() // Use both JA3 and JA4R fingerprints response, err := client.Do("https://tls.peet.ws/api/clean", cycletls.Options{ Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0", Ja4r: "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601", // JA4R fingerprint (raw format) UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36", }, "GET") if err != nil { log.Fatal("Request failed: ", err) } log.Println("Response with JA4R:", response.Status) } ```
### 如何设置自定义 SNI(域名前置)?
你可以独立于 HTTP `Host` 头覆盖 TLS 服务器名称指示 (SNI)。这使得域名前置场景成为可能,其中握手 SNI 与请求主机不同。 JavaScript/TypeScript: ``` const initCycleTLS = require('cycletls'); (async () => { const cycleTLS = await initCycleTLS(); const resp = await cycleTLS('https://127.0.0.1:8443', { serverName: 'front.example', // TLS SNI used in handshake headers: { Host: 'real.example' }, // HTTP Host header inside the request insecureSkipVerify: true, // for local/self-signed testing ja3: '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0', userAgent: 'Mozilla/5.0 ... Chrome/101.0.4951.54 Safari/537.36' }, 'GET'); console.log(await resp.text()); await cycleTLS.exit(); })(); ``` Golang: ``` client := cycletls.Init() response, err := client.Do("https://127.0.0.1:8443", cycletls.Options{ ServerName: "front.example", // TLS SNI Headers: map[string]string{"Host": "real.example"}, // HTTP Host InsecureSkipVerify: true, Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0", UserAgent: "Mozilla/5.0 ... Chrome/101.0.4951.54 Safari/537.36", }, "GET") ``` **注意:** - 当提供 `serverName` 时,它将被用于 TLS 握手;库不会覆盖你的 `Host` 头。 - 包含 SNI (扩展 0x0000) 的 JA4R 指纹将使用提供的 `serverName` 构造。 - 协议支持:`serverName` 适用于 HTTP/1.1、HTTP/2、HTTP/3、WebSocket (`wss://`) 和 SSE (`https://`)。 带有自定义 SNI 的 WebSocket (wss): ``` import initCycleTLS from 'cycletls'; (async () => { const cycleTLS = await initCycleTLS(); const ws = await cycleTLS.ws('wss://127.0.0.1:8443/socket', { serverName: 'front.example', headers: { Host: 'real.example' }, insecureSkipVerify: true, }); ws.onMessage(msg => console.log('message:', msg)); await ws.close(); await cycleTLS.exit(); })(); ``` 带有自定义 SNI 的 SSE: ``` import initCycleTLS from 'cycletls'; (async () => { const cycleTLS = await initCycleTLS(); const sse = await cycleTLS.sse('https://127.0.0.1:8443/events', { serverName: 'front.example', headers: { Host: 'real.example' }, insecureSkipVerify: true, }); sse.onEvent(ev => console.log('event:', ev)); await sse.close(); await cycleTLS.exit(); })(); ```
### 如何设置/强制使用 HTTP1 或 HTTP3?
在 Golang 中,在 Options 中设置 `ForceHTTP1` ``` package main import ( "github.com/Danny-Dasilva/CycleTLS/cycletls" "log" ) func main() { client := cycletls.Init() response, err := client.Do("https://tls.peet.ws/api/all", cycletls.Options{ ForceHTTP1: true, }, "GET") if err != nil { log.Print("Request Failed: " + err.Error()) } log.Println(response.Body,) //You can verify the HTTP_Version in the response } ``` 在 JavaScript/TypeScript 中,在 Options 中设置 `forceHTTP1` ``` const initCycleTLS = require('cycletls'); // Typescript: import initCycleTLS from 'cycletls'; (async () => { const cycleTLS = await initCycleTLS(); const response = await cycleTLS('https://ja3er.com/json', { body: '', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', forceHTTP1: true, // Set this field to force HTTP/1.1 }); const data = await response.json(); console.log(data); // You can verify the HTTP_Version in the response await cycleTLS.exit(); })(); ``` ### 强制使用 HTTP/3 类似地,你可以强制使用 HTTP/3 协议: 在 JavaScript/TypeScript 中设置 `forceHTTP3` ``` const initCycleTLS = require('cycletls'); // Typescript: import initCycleTLS from 'cycletls'; (async () => { const cycleTLS = await initCycleTLS(); const response = await cycleTLS('https://www.google.com/', { body: '', ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0', userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0', forceHTTP3: true, // Set this field to force HTTP/3 via QUIC }); const data = await response.json(); console.log(data); // HTTP/3 requests use QUIC protocol await cycleTLS.exit(); })(); ```
## 为其他平台交叉编译 details> 本地操作系统类型 `linux`、`darwin`、`windows` 应该覆盖大多数用例。 你可以使用内置的 Golang 交叉编译命令 `go build` 来编译到其他操作系统。 例如,要编译为 Linux ARM,你需要传递 `GOOS` 和 `GOARCH` 参数: ``` $ GOOS=linux GOARCH=arm go build -o ./dist/index ./golang && chmod +x ./dist/index ``` 运行上述命令后,你可以直接运行 `./index`,CycleTLS 应该按预期工作。 使用此 [gist](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) 获取支持交叉编译的操作系统列表,并随时打开一个 [问题](https://github.com/Danny-Dasilva/CycleTLS/issues/new/choose) 提交针对你特定操作系统使用场景的功能请求。 ## 许可证 ### GPL3 许可证摘要 **_TL;DR_**:以下是 GPL3 许可证的内容: ``` 1. Anyone can copy, modify and distribute this software. 2. You have to include the license and copyright notice with each and every distribution. 3. You can use this software privately. 4. You can use this software for commercial purposes. 5. Source code MUST be made available when the software is distributed. 6. Any modifications of this code base MUST be distributed with the same license, GPLv3. 7. This software is provided without warranty. 8. The software author or license can not be held liable for any damages inflicted by the software. ``` 有关许可证的更多信息,请访问 [许可证页面](http://choosealicense.com/licenses/gpl-3.0/)
标签:API客户端, CMS安全, CycleTLS, EVTX分析, Goroutine池, Go语言, HTTP/3, HTTP指纹, JA3指纹, JA4指纹, JavaScript, QUIC, SEO, Socks5, TLS指纹混淆, TypeScript, WebSocket客户端, 二进制发布, 代理支持, 反取证, 告警, 安全插件, 安全评估, 客户端指纹, 开源工具, 异步请求, 数据可视化, 日志审计, 服务器发送事件, 流量伪装, 程序破解, 网络安全, 网络混淆, 请求配置, 连接复用, 隐私保护