Skip to content

Commit fb3e2c5

Browse files
committed
Update readme and timeout test
1 parent 2ef165c commit fb3e2c5

File tree

2 files changed

+72
-70
lines changed

2 files changed

+72
-70
lines changed

README.md

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -851,25 +851,19 @@ client := &http.Client{Transport: transport}
851851
```js
852852
{
853853
// URL for the request (required if not specified as an argument)
854-
url: "https://example.com"
854+
url: "https://example.com",
855855
// Method for the request ("head" | "get" | "post" | "put" | "delete" | "trace" | "options" | "connect" | "patch")
856-
method: "get" // Default method
856+
method: "get", // Default method
857857
// Custom headers to send
858-
headers: { "Authorization": "Bearer someexampletoken" }
859-
// Custom cookies to send
860-
Cookies: [{
861-
"name": "key",
862-
"value": "val",
863-
"path": "/docs",
864-
"domain": "google.com",
865-
"expires": "Mon, 02-Jan-2022 15:04:05 EST"
866-
"maxAge": 90,
867-
"secure": false,
868-
"httpOnly": true,
869-
"sameSite": "Lax"
870-
}],
871-
// Body to send with request (must be a string - cannot pass an object)
872-
body: '',
858+
headers: { "Authorization": "Bearer someexampletoken" },
859+
// Cookies to send
860+
cookies: [
861+
{ name: "key", value: "val", path: "/docs", domain: "google.com" }
862+
],
863+
// Body to send with request (string)
864+
body: "",
865+
// Binary body for non-UTF8 payloads
866+
bodyBytes: new Uint8Array([0x00, 0x01]),
873867
// JA3 token to send with request
874868
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',
875869
// JA4R token for enhanced fingerprinting (raw format)
@@ -878,26 +872,34 @@ client := &http.Client{Transport: transport}
878872
userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
879873
// Proxy to send request through (supports http, socks4, socks5, socks5h)
880874
proxy: 'http://username:[email protected]:443',
881-
// Amount of seconds before request timeout (default: 7)
882-
timeout: 2,
875+
// Timeout before headers arrive (ms)
876+
timeout: 2000,
877+
// Body stream idle timeout (ms)
878+
readTimeout: 500,
883879
// Toggle if CycleTLS should follow redirects
884880
disableRedirect: true,
885881
// Custom header order to send with request (This value will overwrite default header order)
886882
headerOrder: ["cache-control", "connection", "host"],
883+
// Preserve header insertion order exactly as provided
884+
orderAsProvided: true,
887885
// 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.)
888-
insecureSkipVerify: false
886+
insecureSkipVerify: false,
889887
// Forces CycleTLS to do a http1 handshake
890-
forceHTTP1: false
888+
forceHTTP1: false,
891889
// Forces HTTP/3 protocol
892-
forceHTTP3: false
890+
forceHTTP3: false,
893891
// Enable connection reuse across requests
894-
enableConnectionReuse: true
892+
enableConnectionReuse: true,
895893
// HTTP/2 fingerprint
896-
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'
894+
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',
897895
// QUIC fingerprint for HTTP/3
898-
quicFingerprint: '16030106f2010006ee03039a2b98d81139db0e128ea09eff...'
899-
// JA4H HTTP client fingerprint
900-
ja4h: 'ge11_73a4f1e_8b3fce7'
896+
quicFingerprint: '16030106f2010006ee03039a2b98d81139db0e128ea09eff...',
897+
// Disable GREASE for exact JA4 matching
898+
disableGrease: false,
899+
// Override TLS SNI
900+
serverName: "example.com",
901+
// Auto retry TLS 1.3 handshake failures
902+
tls13AutoRetry: true
901903
}
902904

903905
```
@@ -987,7 +989,7 @@ const client = new CycleTLS();
987989

988990
// JavaScript timeout example
989991
const response = await client.get('https://httpbin.org/delay/10', {
990-
timeout: 5, // 5 seconds timeout
992+
timeout: 5000, // 5 seconds timeout (ms)
991993
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',
992994
userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0'
993995
});
@@ -1005,10 +1007,10 @@ response, err := client.Do("https://httpbin.org/delay/10", cycletls.Options{
10051007

10061008
### Timeout Error Response
10071009

1008-
When a request times out, CycleTLS returns a response with:
1010+
When a request times out, the streaming client rejects with `CycleTLSError` (status code `408`). The legacy client returns a response with:
10091011
- **Status Code**: `408` (Request Timeout)
10101012
- **Body**: Contains error message describing the timeout
1011-
- **Error**: JavaScript will have the response object, Go will have `err != nil`
1013+
- **Error**: Go will have `err != nil`
10121014

10131015
### JavaScript Timeout Error Handling
10141016

@@ -1021,25 +1023,22 @@ const CycleTLS = require('cycletls').default;
10211023

10221024
try {
10231025
const response = await client.get('https://httpbin.org/delay/10', {
1024-
timeout: 2, // Will timeout after 2 seconds
1026+
timeout: 2000, // Will timeout after 2 seconds (ms)
10251027
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',
10261028
userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0'
10271029
});
10281030

1029-
if (response.statusCode === 408) {
1030-
// Consume body to get error message
1031-
const chunks = [];
1032-
for await (const chunk of response.body) chunks.push(chunk);
1033-
console.log('Request timed out:', Buffer.concat(chunks).toString());
1031+
// Consume body and parse JSON
1032+
const chunks = [];
1033+
for await (const chunk of response.body) chunks.push(chunk);
1034+
const data = JSON.parse(Buffer.concat(chunks).toString());
1035+
console.log('Success:', data);
1036+
} catch (error) {
1037+
if (error && error.statusCode === 408) {
1038+
console.error('Request timed out:', error.message);
10341039
} else {
1035-
// Consume body and parse JSON
1036-
const chunks = [];
1037-
for await (const chunk of response.body) chunks.push(chunk);
1038-
const data = JSON.parse(Buffer.concat(chunks).toString());
1039-
console.log('Success:', data);
1040+
console.error('Request failed:', error);
10401041
}
1041-
} catch (error) {
1042-
console.error('Request failed:', error);
10431042
} finally {
10441043
await client.close();
10451044
}
@@ -1236,12 +1235,16 @@ interface Response {
12361235
requestId: string;
12371236
// Status code returned from server (Number)
12381237
statusCode: number;
1238+
// Alias for statusCode (Number)
1239+
status: number;
12391240
// Final URL after redirects (String)
12401241
finalUrl: string;
12411242
// Headers returned from the server (Object)
12421243
headers: Record<string, string[]>;
12431244
// Body as a readable stream (for large responses)
12441245
body: Readable;
1246+
// Alias for body (Readable)
1247+
data: Readable;
12451248

12461249
// Helper methods (buffer entire response)
12471250
json<T>(): Promise<T>; // Parse as JSON
@@ -2408,7 +2411,7 @@ const CycleTLS = require('cycletls').default;
24082411

24092412
// Send messages (text or binary)
24102413
ws.send('text message');
2411-
ws.send(Buffer.from([0x01, 0x02, 0x03]), { binary: true });
2414+
ws.send(Buffer.from([0x01, 0x02, 0x03]));
24122415

24132416
// Close connection gracefully
24142417
ws.close(1000, 'Normal closure');
@@ -2418,19 +2421,19 @@ const CycleTLS = require('cycletls').default;
24182421
### WebSocket Properties and Methods
24192422
24202423
```typescript
2421-
interface CycleTLSWebSocket {
2424+
interface CycleTLSWebSocketV2 {
24222425
// Properties
2423-
url: string; // WebSocket URL
2426+
URL: string; // WebSocket URL
24242427
readyState: number; // 0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED
24252428
protocol: string; // Negotiated subprotocol
24262429
extensions: string; // Negotiated extensions
24272430
binaryType: 'nodebuffer' | 'arraybuffer'; // Binary data format
24282431

24292432
// Methods
2430-
send(data: string | Buffer | ArrayBuffer, options?: { binary?: boolean }, callback?: (err?: Error) => void): void;
2433+
send(data: string | Buffer, callback?: (err?: Error) => void): void;
24312434
close(code?: number, reason?: string): void;
2432-
ping(data?: Buffer | string, mask?: boolean, callback?: (err?: Error) => void): void;
2433-
pong(data?: Buffer | string, mask?: boolean, callback?: (err?: Error) => void): void;
2435+
ping(data?: Buffer | string): void;
2436+
pong(data?: Buffer | string): void;
24342437
terminate(): void; // Immediate close without handshake
24352438

24362439
// Events

tests/timeout.test.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
1-
import { initCycleTLS } from "../dist/index.js";
1+
import CycleTLS from "../dist/index.js";
22
import { withCycleTLS } from "./test-utils.js";
33
jest.setTimeout(30000);
44

55
test("Should return a timeout error", async () => {
6-
await withCycleTLS({ port: 9116 }, async (cycleTLS) => {
6+
// V2 API: timeout is set at client level, not per-request
7+
// Use a 2-second client timeout to trigger timeout on /delay/4 (4 second delay)
8+
await withCycleTLS({ port: 9116, timeout: 2000 }, async (client) => {
79
const ja3 =
810
"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";
911
const userAgent =
1012
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36";
1113

12-
const timeoutResponse = await cycleTLS(
13-
"https://httpbin.org/delay/4",
14-
{
15-
body: "",
14+
// V2 API throws CycleTLSError on timeout
15+
await expect(
16+
client.get("https://httpbin.org/delay/4", {
1617
ja3: ja3,
1718
userAgent: userAgent,
18-
timeout: 1,
19-
},
20-
"get"
21-
);
19+
})
20+
).rejects.toThrow(/timeout/i);
21+
});
2222

23-
expect(timeoutResponse.status).toBe(408);
23+
// Separate client with longer timeout for the success test
24+
await withCycleTLS({ port: 9116, timeout: 30000 }, async (client) => {
25+
const ja3 =
26+
"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";
27+
const userAgent =
28+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36";
2429

25-
const normalResponse = await cycleTLS(
26-
"https://httpbin.org/delay/1",
27-
{
28-
body: "",
29-
ja3: ja3,
30-
userAgent: userAgent,
31-
timeout: 30,
32-
},
33-
"get"
34-
);
30+
const normalResponse = await client.get("https://httpbin.org/delay/1", {
31+
ja3: ja3,
32+
userAgent: userAgent,
33+
});
3534

3635
expect(normalResponse.status).toBe(200);
3736
});

0 commit comments

Comments
 (0)