Danny-Dasilva/CycleTLS
GitHub: Danny-Dasilva/CycleTLS
一款用于伪造 TLS/JA3 指纹、支持多协议与代理的高性能 Go 工具,帮助隐蔽探测与绕过安全检测。
Stars: 1348 | Forks: 212
# CycleTLS
接受社区支持和 PR  [](http://godoc.org/github.com/Danny-Dasilva/CycleTLS/cycletls) [](https://github.com/Danny-Dasilva/CycleTLS/blob/main/LICENSE) [](https://goreportcard.com/report/github.com/Danny-Dasilva/CycleTLS/cycletls) [](https://www.npmjs.org/package/cycletls)
如果你有 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 示例
接受社区支持和 PR  [](http://godoc.org/github.com/Danny-Dasilva/CycleTLS/cycletls) [](https://github.com/Danny-Dasilva/CycleTLS/blob/main/LICENSE) [](https://goreportcard.com/report/github.com/Danny-Dasilva/CycleTLS/cycletls) [](https://www.npmjs.org/package/cycletls)
```
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客户端, 二进制发布, 代理支持, 反取证, 告警, 安全插件, 安全评估, 客户端指纹, 开源工具, 异步请求, 数据可视化, 日志审计, 服务器发送事件, 流量伪装, 程序破解, 网络安全, 网络混淆, 请求配置, 连接复用, 隐私保护