httpcloak 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -0
- package/lib/index.d.ts +152 -0
- package/lib/index.js +551 -0
- package/npm/darwin-arm64/lib.js +3 -0
- package/npm/darwin-arm64/package.json +20 -0
- package/npm/darwin-x64/lib.js +3 -0
- package/npm/darwin-x64/package.json +20 -0
- package/npm/linux-arm64/lib.js +3 -0
- package/npm/linux-arm64/package.json +20 -0
- package/npm/linux-x64/lib.js +3 -0
- package/npm/linux-x64/package.json +20 -0
- package/npm/win32-arm64/lib.js +3 -0
- package/npm/win32-arm64/package.json +20 -0
- package/npm/win32-x64/lib.js +3 -0
- package/npm/win32-x64/package.json +20 -0
- package/package.json +45 -0
- package/scripts/setup-npm-packages.js +69 -0
- package/test.js +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# HTTPCloak Node.js
|
|
2
|
+
|
|
3
|
+
Browser fingerprint emulation HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install httpcloak
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Promise-based Usage (Recommended)
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const { Session } = require("httpcloak");
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
const session = new Session({ preset: "chrome-143" });
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// GET request
|
|
23
|
+
const response = await session.get("https://www.cloudflare.com/cdn-cgi/trace");
|
|
24
|
+
console.log(response.statusCode);
|
|
25
|
+
console.log(response.text);
|
|
26
|
+
|
|
27
|
+
// POST request with JSON body
|
|
28
|
+
const postResponse = await session.post("https://api.example.com/data", {
|
|
29
|
+
key: "value",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Custom headers
|
|
33
|
+
const customResponse = await session.get("https://example.com", {
|
|
34
|
+
"X-Custom": "value",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Concurrent requests
|
|
38
|
+
const responses = await Promise.all([
|
|
39
|
+
session.get("https://example.com/1"),
|
|
40
|
+
session.get("https://example.com/2"),
|
|
41
|
+
session.get("https://example.com/3"),
|
|
42
|
+
]);
|
|
43
|
+
} finally {
|
|
44
|
+
session.close();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
main();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Synchronous Usage
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
const { Session } = require("httpcloak");
|
|
55
|
+
|
|
56
|
+
const session = new Session({ preset: "chrome-143" });
|
|
57
|
+
|
|
58
|
+
// Sync GET
|
|
59
|
+
const response = session.getSync("https://example.com");
|
|
60
|
+
console.log(response.statusCode);
|
|
61
|
+
console.log(response.text);
|
|
62
|
+
|
|
63
|
+
// Sync POST
|
|
64
|
+
const postResponse = session.postSync("https://api.example.com/data", {
|
|
65
|
+
key: "value",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
session.close();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Callback-based Usage
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const { Session } = require("httpcloak");
|
|
75
|
+
|
|
76
|
+
const session = new Session({ preset: "chrome-143" });
|
|
77
|
+
|
|
78
|
+
// GET with callback
|
|
79
|
+
session.getCb("https://example.com", (err, response) => {
|
|
80
|
+
if (err) {
|
|
81
|
+
console.error("Error:", err.message);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log(response.statusCode);
|
|
85
|
+
console.log(response.text);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// POST with callback
|
|
89
|
+
session.postCb(
|
|
90
|
+
"https://api.example.com/data",
|
|
91
|
+
{ key: "value" },
|
|
92
|
+
(err, response) => {
|
|
93
|
+
if (err) {
|
|
94
|
+
console.error("Error:", err.message);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log(response.statusCode);
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### With Proxy
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
const session = new Session({
|
|
106
|
+
preset: "chrome-143",
|
|
107
|
+
proxy: "http://user:pass@host:port",
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Cookie Management
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const { Session } = require("httpcloak");
|
|
115
|
+
|
|
116
|
+
const session = new Session();
|
|
117
|
+
|
|
118
|
+
// Get all cookies
|
|
119
|
+
const cookies = session.getCookies();
|
|
120
|
+
console.log(cookies);
|
|
121
|
+
|
|
122
|
+
// Set a cookie
|
|
123
|
+
session.setCookie("session_id", "abc123");
|
|
124
|
+
|
|
125
|
+
// Access cookies as property
|
|
126
|
+
console.log(session.cookies);
|
|
127
|
+
|
|
128
|
+
session.close();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Available Presets
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const { availablePresets } = require("httpcloak");
|
|
135
|
+
|
|
136
|
+
console.log(availablePresets());
|
|
137
|
+
// ['chrome-143', 'chrome-143-windows', 'chrome-143-linux', 'chrome-143-macos',
|
|
138
|
+
// 'chrome-131', 'firefox-133', 'safari-18', ...]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Response Object
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
const response = await session.get("https://example.com");
|
|
145
|
+
|
|
146
|
+
response.statusCode; // number: HTTP status code
|
|
147
|
+
response.headers; // object: Response headers
|
|
148
|
+
response.body; // Buffer: Raw response body
|
|
149
|
+
response.text; // string: Response body as text
|
|
150
|
+
response.finalUrl; // string: Final URL after redirects
|
|
151
|
+
response.protocol; // string: Protocol used (http/1.1, h2, h3)
|
|
152
|
+
response.json(); // Parse response body as JSON
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Custom Requests
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
const response = await session.request({
|
|
159
|
+
method: "PUT",
|
|
160
|
+
url: "https://api.example.com/resource",
|
|
161
|
+
headers: { "X-Custom": "value" },
|
|
162
|
+
body: { data: "value" },
|
|
163
|
+
timeout: 60,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Error Handling
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
const { Session, HTTPCloakError } = require("httpcloak");
|
|
171
|
+
|
|
172
|
+
const session = new Session();
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const response = await session.get("https://example.com");
|
|
176
|
+
} catch (err) {
|
|
177
|
+
if (err instanceof HTTPCloakError) {
|
|
178
|
+
console.error("HTTPCloak error:", err.message);
|
|
179
|
+
} else {
|
|
180
|
+
console.error("Unknown error:", err);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
session.close();
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## TypeScript Support
|
|
188
|
+
|
|
189
|
+
HTTPCloak includes TypeScript definitions out of the box:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { Session, Response, HTTPCloakError } from "httpcloak";
|
|
193
|
+
|
|
194
|
+
const session = new Session({ preset: "chrome-143" });
|
|
195
|
+
|
|
196
|
+
async function fetchData(): Promise<Response> {
|
|
197
|
+
return session.get("https://example.com");
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Platform Support
|
|
202
|
+
|
|
203
|
+
- Linux (x64, arm64)
|
|
204
|
+
- macOS (x64, arm64)
|
|
205
|
+
- Windows (x64, arm64)
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTPCloak Node.js TypeScript Definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class HTTPCloakError extends Error {
|
|
6
|
+
name: "HTTPCloakError";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class Response {
|
|
10
|
+
/** HTTP status code */
|
|
11
|
+
statusCode: number;
|
|
12
|
+
/** Response headers */
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
/** Raw response body as Buffer */
|
|
15
|
+
body: Buffer;
|
|
16
|
+
/** Response body as string */
|
|
17
|
+
text: string;
|
|
18
|
+
/** Final URL after redirects */
|
|
19
|
+
finalUrl: string;
|
|
20
|
+
/** Protocol used (http/1.1, h2, h3) */
|
|
21
|
+
protocol: string;
|
|
22
|
+
|
|
23
|
+
/** Parse response body as JSON */
|
|
24
|
+
json<T = any>(): T;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SessionOptions {
|
|
28
|
+
/** Browser preset to use (default: "chrome-143") */
|
|
29
|
+
preset?: string;
|
|
30
|
+
/** Proxy URL (e.g., "http://user:pass@host:port") */
|
|
31
|
+
proxy?: string;
|
|
32
|
+
/** Request timeout in seconds (default: 30) */
|
|
33
|
+
timeout?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RequestOptions {
|
|
37
|
+
/** HTTP method */
|
|
38
|
+
method: string;
|
|
39
|
+
/** Request URL */
|
|
40
|
+
url: string;
|
|
41
|
+
/** Optional custom headers */
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
/** Optional request body */
|
|
44
|
+
body?: string | Buffer | Record<string, any>;
|
|
45
|
+
/** Optional request timeout in seconds */
|
|
46
|
+
timeout?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type RequestCallback = (
|
|
50
|
+
error: HTTPCloakError | null,
|
|
51
|
+
response: Response | null
|
|
52
|
+
) => void;
|
|
53
|
+
|
|
54
|
+
export class Session {
|
|
55
|
+
constructor(options?: SessionOptions);
|
|
56
|
+
|
|
57
|
+
/** Close the session and release resources */
|
|
58
|
+
close(): void;
|
|
59
|
+
|
|
60
|
+
// Synchronous methods
|
|
61
|
+
/** Perform a synchronous GET request */
|
|
62
|
+
getSync(url: string, headers?: Record<string, string>): Response;
|
|
63
|
+
|
|
64
|
+
/** Perform a synchronous POST request */
|
|
65
|
+
postSync(
|
|
66
|
+
url: string,
|
|
67
|
+
body?: string | Buffer | Record<string, any>,
|
|
68
|
+
headers?: Record<string, string>
|
|
69
|
+
): Response;
|
|
70
|
+
|
|
71
|
+
/** Perform a synchronous custom HTTP request */
|
|
72
|
+
requestSync(options: RequestOptions): Response;
|
|
73
|
+
|
|
74
|
+
// Promise-based methods
|
|
75
|
+
/** Perform an async GET request */
|
|
76
|
+
get(url: string, headers?: Record<string, string>): Promise<Response>;
|
|
77
|
+
|
|
78
|
+
/** Perform an async POST request */
|
|
79
|
+
post(
|
|
80
|
+
url: string,
|
|
81
|
+
body?: string | Buffer | Record<string, any>,
|
|
82
|
+
headers?: Record<string, string>
|
|
83
|
+
): Promise<Response>;
|
|
84
|
+
|
|
85
|
+
/** Perform an async custom HTTP request */
|
|
86
|
+
request(options: RequestOptions): Promise<Response>;
|
|
87
|
+
|
|
88
|
+
/** Perform an async PUT request */
|
|
89
|
+
put(
|
|
90
|
+
url: string,
|
|
91
|
+
body?: string | Buffer | Record<string, any>,
|
|
92
|
+
headers?: Record<string, string>
|
|
93
|
+
): Promise<Response>;
|
|
94
|
+
|
|
95
|
+
/** Perform an async DELETE request */
|
|
96
|
+
delete(url: string, headers?: Record<string, string>): Promise<Response>;
|
|
97
|
+
|
|
98
|
+
/** Perform an async PATCH request */
|
|
99
|
+
patch(
|
|
100
|
+
url: string,
|
|
101
|
+
body?: string | Buffer | Record<string, any>,
|
|
102
|
+
headers?: Record<string, string>
|
|
103
|
+
): Promise<Response>;
|
|
104
|
+
|
|
105
|
+
/** Perform an async HEAD request */
|
|
106
|
+
head(url: string, headers?: Record<string, string>): Promise<Response>;
|
|
107
|
+
|
|
108
|
+
/** Perform an async OPTIONS request */
|
|
109
|
+
options(url: string, headers?: Record<string, string>): Promise<Response>;
|
|
110
|
+
|
|
111
|
+
// Callback-based methods
|
|
112
|
+
/** Perform a GET request with callback */
|
|
113
|
+
getCb(url: string, callback: RequestCallback): void;
|
|
114
|
+
getCb(
|
|
115
|
+
url: string,
|
|
116
|
+
headers: Record<string, string>,
|
|
117
|
+
callback: RequestCallback
|
|
118
|
+
): void;
|
|
119
|
+
|
|
120
|
+
/** Perform a POST request with callback */
|
|
121
|
+
postCb(url: string, callback: RequestCallback): void;
|
|
122
|
+
postCb(
|
|
123
|
+
url: string,
|
|
124
|
+
body: string | Buffer | Record<string, any>,
|
|
125
|
+
callback: RequestCallback
|
|
126
|
+
): void;
|
|
127
|
+
postCb(
|
|
128
|
+
url: string,
|
|
129
|
+
body: string | Buffer | Record<string, any>,
|
|
130
|
+
headers: Record<string, string>,
|
|
131
|
+
callback: RequestCallback
|
|
132
|
+
): void;
|
|
133
|
+
|
|
134
|
+
/** Perform a custom request with callback */
|
|
135
|
+
requestCb(options: RequestOptions, callback: RequestCallback): void;
|
|
136
|
+
|
|
137
|
+
// Cookie management
|
|
138
|
+
/** Get all cookies from the session */
|
|
139
|
+
getCookies(): Record<string, string>;
|
|
140
|
+
|
|
141
|
+
/** Set a cookie in the session */
|
|
142
|
+
setCookie(name: string, value: string): void;
|
|
143
|
+
|
|
144
|
+
/** Get cookies as a property */
|
|
145
|
+
readonly cookies: Record<string, string>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Get the httpcloak library version */
|
|
149
|
+
export function version(): string;
|
|
150
|
+
|
|
151
|
+
/** Get list of available browser presets */
|
|
152
|
+
export function availablePresets(): string[];
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTPCloak Node.js Client
|
|
3
|
+
*
|
|
4
|
+
* Provides HTTP client with browser fingerprint emulation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const koffi = require("koffi");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Custom error class for HTTPCloak errors
|
|
14
|
+
*/
|
|
15
|
+
class HTTPCloakError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "HTTPCloakError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Response object returned from HTTP requests
|
|
24
|
+
*/
|
|
25
|
+
class Response {
|
|
26
|
+
constructor(data) {
|
|
27
|
+
this.statusCode = data.status_code || 0;
|
|
28
|
+
this.headers = data.headers || {};
|
|
29
|
+
this.body = Buffer.from(data.body || "", "utf8");
|
|
30
|
+
this.text = data.body || "";
|
|
31
|
+
this.finalUrl = data.final_url || "";
|
|
32
|
+
this.protocol = data.protocol || "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse response body as JSON
|
|
37
|
+
*/
|
|
38
|
+
json() {
|
|
39
|
+
return JSON.parse(this.text);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the platform package name for the current platform
|
|
45
|
+
*/
|
|
46
|
+
function getPlatformPackageName() {
|
|
47
|
+
const platform = os.platform();
|
|
48
|
+
const arch = os.arch();
|
|
49
|
+
|
|
50
|
+
// Map to npm platform names
|
|
51
|
+
let platName;
|
|
52
|
+
if (platform === "darwin") {
|
|
53
|
+
platName = "darwin";
|
|
54
|
+
} else if (platform === "win32") {
|
|
55
|
+
platName = "win32";
|
|
56
|
+
} else {
|
|
57
|
+
platName = "linux";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let archName;
|
|
61
|
+
if (arch === "x64" || arch === "amd64") {
|
|
62
|
+
archName = "x64";
|
|
63
|
+
} else if (arch === "arm64" || arch === "aarch64") {
|
|
64
|
+
archName = "arm64";
|
|
65
|
+
} else {
|
|
66
|
+
archName = arch;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return `@httpcloak/${platName}-${archName}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the path to the native library
|
|
74
|
+
*/
|
|
75
|
+
function getLibPath() {
|
|
76
|
+
const platform = os.platform();
|
|
77
|
+
const arch = os.arch();
|
|
78
|
+
|
|
79
|
+
// Check environment variable first
|
|
80
|
+
const envPath = process.env.HTTPCLOAK_LIB_PATH;
|
|
81
|
+
if (envPath && fs.existsSync(envPath)) {
|
|
82
|
+
return envPath;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Try to load from platform-specific optional dependency
|
|
86
|
+
const packageName = getPlatformPackageName();
|
|
87
|
+
try {
|
|
88
|
+
const libPath = require(packageName);
|
|
89
|
+
if (fs.existsSync(libPath)) {
|
|
90
|
+
return libPath;
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {
|
|
93
|
+
// Optional dependency not installed, fall back to local search
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Normalize architecture for library name
|
|
97
|
+
let archName;
|
|
98
|
+
if (arch === "x64" || arch === "amd64") {
|
|
99
|
+
archName = "amd64";
|
|
100
|
+
} else if (arch === "arm64" || arch === "aarch64") {
|
|
101
|
+
archName = "arm64";
|
|
102
|
+
} else {
|
|
103
|
+
archName = arch;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Determine OS name and extension
|
|
107
|
+
let osName, ext;
|
|
108
|
+
if (platform === "darwin") {
|
|
109
|
+
osName = "darwin";
|
|
110
|
+
ext = ".dylib";
|
|
111
|
+
} else if (platform === "win32") {
|
|
112
|
+
osName = "windows";
|
|
113
|
+
ext = ".dll";
|
|
114
|
+
} else {
|
|
115
|
+
osName = "linux";
|
|
116
|
+
ext = ".so";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const libName = `libhttpcloak-${osName}-${archName}${ext}`;
|
|
120
|
+
|
|
121
|
+
// Search paths (fallback for local development)
|
|
122
|
+
const searchPaths = [
|
|
123
|
+
path.join(__dirname, libName),
|
|
124
|
+
path.join(__dirname, "..", libName),
|
|
125
|
+
path.join(__dirname, "..", "lib", libName),
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
for (const searchPath of searchPaths) {
|
|
129
|
+
if (fs.existsSync(searchPath)) {
|
|
130
|
+
return searchPath;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw new HTTPCloakError(
|
|
135
|
+
`Could not find httpcloak library (${libName}). ` +
|
|
136
|
+
`Try: npm install ${packageName}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Load the native library
|
|
141
|
+
let lib = null;
|
|
142
|
+
|
|
143
|
+
function getLib() {
|
|
144
|
+
if (lib === null) {
|
|
145
|
+
const libPath = getLibPath();
|
|
146
|
+
const nativeLib = koffi.load(libPath);
|
|
147
|
+
|
|
148
|
+
lib = {
|
|
149
|
+
httpcloak_session_new: nativeLib.func("httpcloak_session_new", "int64", ["str"]),
|
|
150
|
+
httpcloak_session_free: nativeLib.func("httpcloak_session_free", "void", ["int64"]),
|
|
151
|
+
httpcloak_get: nativeLib.func("httpcloak_get", "str", ["int64", "str", "str"]),
|
|
152
|
+
httpcloak_post: nativeLib.func("httpcloak_post", "str", ["int64", "str", "str", "str"]),
|
|
153
|
+
httpcloak_request: nativeLib.func("httpcloak_request", "str", ["int64", "str"]),
|
|
154
|
+
httpcloak_get_cookies: nativeLib.func("httpcloak_get_cookies", "str", ["int64"]),
|
|
155
|
+
httpcloak_set_cookie: nativeLib.func("httpcloak_set_cookie", "void", ["int64", "str", "str"]),
|
|
156
|
+
httpcloak_free_string: nativeLib.func("httpcloak_free_string", "void", ["str"]),
|
|
157
|
+
httpcloak_version: nativeLib.func("httpcloak_version", "str", []),
|
|
158
|
+
httpcloak_available_presets: nativeLib.func("httpcloak_available_presets", "str", []),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return lib;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parse response from the native library
|
|
166
|
+
*/
|
|
167
|
+
function parseResponse(result) {
|
|
168
|
+
if (!result) {
|
|
169
|
+
throw new HTTPCloakError("No response received");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const data = JSON.parse(result);
|
|
173
|
+
|
|
174
|
+
if (data.error) {
|
|
175
|
+
throw new HTTPCloakError(data.error);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return new Response(data);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get the httpcloak library version
|
|
183
|
+
*/
|
|
184
|
+
function version() {
|
|
185
|
+
const nativeLib = getLib();
|
|
186
|
+
return nativeLib.httpcloak_version() || "unknown";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get list of available browser presets
|
|
191
|
+
*/
|
|
192
|
+
function availablePresets() {
|
|
193
|
+
const nativeLib = getLib();
|
|
194
|
+
const result = nativeLib.httpcloak_available_presets();
|
|
195
|
+
if (result) {
|
|
196
|
+
return JSON.parse(result);
|
|
197
|
+
}
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* HTTP Session with browser fingerprint emulation
|
|
203
|
+
*/
|
|
204
|
+
class Session {
|
|
205
|
+
/**
|
|
206
|
+
* Create a new session
|
|
207
|
+
* @param {Object} options - Session options
|
|
208
|
+
* @param {string} [options.preset="chrome-143"] - Browser preset to use
|
|
209
|
+
* @param {string} [options.proxy] - Proxy URL (e.g., "http://user:pass@host:port")
|
|
210
|
+
* @param {number} [options.timeout=30] - Request timeout in seconds
|
|
211
|
+
*/
|
|
212
|
+
constructor(options = {}) {
|
|
213
|
+
const { preset = "chrome-143", proxy = null, timeout = 30 } = options;
|
|
214
|
+
|
|
215
|
+
this._lib = getLib();
|
|
216
|
+
|
|
217
|
+
const config = {
|
|
218
|
+
preset,
|
|
219
|
+
timeout,
|
|
220
|
+
};
|
|
221
|
+
if (proxy) {
|
|
222
|
+
config.proxy = proxy;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
this._handle = this._lib.httpcloak_session_new(JSON.stringify(config));
|
|
226
|
+
|
|
227
|
+
if (this._handle === 0n || this._handle === 0) {
|
|
228
|
+
throw new HTTPCloakError("Failed to create session");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Close the session and release resources
|
|
234
|
+
*/
|
|
235
|
+
close() {
|
|
236
|
+
if (this._handle) {
|
|
237
|
+
this._lib.httpcloak_session_free(this._handle);
|
|
238
|
+
this._handle = 0n;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ===========================================================================
|
|
243
|
+
// Synchronous Methods
|
|
244
|
+
// ===========================================================================
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Perform a synchronous GET request
|
|
248
|
+
* @param {string} url - Request URL
|
|
249
|
+
* @param {Object} [headers] - Optional custom headers
|
|
250
|
+
* @returns {Response} Response object
|
|
251
|
+
*/
|
|
252
|
+
getSync(url, headers = null) {
|
|
253
|
+
const headersJson = headers ? JSON.stringify(headers) : null;
|
|
254
|
+
const result = this._lib.httpcloak_get(this._handle, url, headersJson);
|
|
255
|
+
return parseResponse(result);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Perform a synchronous POST request
|
|
260
|
+
* @param {string} url - Request URL
|
|
261
|
+
* @param {string|Buffer|Object} [body] - Request body
|
|
262
|
+
* @param {Object} [headers] - Optional custom headers
|
|
263
|
+
* @returns {Response} Response object
|
|
264
|
+
*/
|
|
265
|
+
postSync(url, body = null, headers = null) {
|
|
266
|
+
if (typeof body === "object" && body !== null && !Buffer.isBuffer(body)) {
|
|
267
|
+
body = JSON.stringify(body);
|
|
268
|
+
headers = headers || {};
|
|
269
|
+
if (!headers["Content-Type"]) {
|
|
270
|
+
headers["Content-Type"] = "application/json";
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (Buffer.isBuffer(body)) {
|
|
275
|
+
body = body.toString("utf8");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const headersJson = headers ? JSON.stringify(headers) : null;
|
|
279
|
+
const result = this._lib.httpcloak_post(this._handle, url, body, headersJson);
|
|
280
|
+
return parseResponse(result);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Perform a synchronous custom HTTP request
|
|
285
|
+
* @param {Object} options - Request options
|
|
286
|
+
* @param {string} options.method - HTTP method
|
|
287
|
+
* @param {string} options.url - Request URL
|
|
288
|
+
* @param {Object} [options.headers] - Optional custom headers
|
|
289
|
+
* @param {string|Buffer|Object} [options.body] - Optional request body
|
|
290
|
+
* @param {number} [options.timeout] - Optional request timeout
|
|
291
|
+
* @returns {Response} Response object
|
|
292
|
+
*/
|
|
293
|
+
requestSync(options) {
|
|
294
|
+
let { method, url, headers = null, body = null, timeout = null } = options;
|
|
295
|
+
|
|
296
|
+
if (typeof body === "object" && body !== null && !Buffer.isBuffer(body)) {
|
|
297
|
+
body = JSON.stringify(body);
|
|
298
|
+
headers = headers || {};
|
|
299
|
+
if (!headers["Content-Type"]) {
|
|
300
|
+
headers["Content-Type"] = "application/json";
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (Buffer.isBuffer(body)) {
|
|
305
|
+
body = body.toString("utf8");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const requestConfig = {
|
|
309
|
+
method: method.toUpperCase(),
|
|
310
|
+
url,
|
|
311
|
+
};
|
|
312
|
+
if (headers) requestConfig.headers = headers;
|
|
313
|
+
if (body) requestConfig.body = body;
|
|
314
|
+
if (timeout) requestConfig.timeout = timeout;
|
|
315
|
+
|
|
316
|
+
const result = this._lib.httpcloak_request(
|
|
317
|
+
this._handle,
|
|
318
|
+
JSON.stringify(requestConfig)
|
|
319
|
+
);
|
|
320
|
+
return parseResponse(result);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ===========================================================================
|
|
324
|
+
// Promise-based Methods
|
|
325
|
+
// ===========================================================================
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Perform an async GET request
|
|
329
|
+
* @param {string} url - Request URL
|
|
330
|
+
* @param {Object} [headers] - Optional custom headers
|
|
331
|
+
* @returns {Promise<Response>} Response object
|
|
332
|
+
*/
|
|
333
|
+
get(url, headers = null) {
|
|
334
|
+
return new Promise((resolve, reject) => {
|
|
335
|
+
setImmediate(() => {
|
|
336
|
+
try {
|
|
337
|
+
resolve(this.getSync(url, headers));
|
|
338
|
+
} catch (err) {
|
|
339
|
+
reject(err);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Perform an async POST request
|
|
347
|
+
* @param {string} url - Request URL
|
|
348
|
+
* @param {string|Buffer|Object} [body] - Request body
|
|
349
|
+
* @param {Object} [headers] - Optional custom headers
|
|
350
|
+
* @returns {Promise<Response>} Response object
|
|
351
|
+
*/
|
|
352
|
+
post(url, body = null, headers = null) {
|
|
353
|
+
return new Promise((resolve, reject) => {
|
|
354
|
+
setImmediate(() => {
|
|
355
|
+
try {
|
|
356
|
+
resolve(this.postSync(url, body, headers));
|
|
357
|
+
} catch (err) {
|
|
358
|
+
reject(err);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Perform an async custom HTTP request
|
|
366
|
+
* @param {Object} options - Request options
|
|
367
|
+
* @returns {Promise<Response>} Response object
|
|
368
|
+
*/
|
|
369
|
+
request(options) {
|
|
370
|
+
return new Promise((resolve, reject) => {
|
|
371
|
+
setImmediate(() => {
|
|
372
|
+
try {
|
|
373
|
+
resolve(this.requestSync(options));
|
|
374
|
+
} catch (err) {
|
|
375
|
+
reject(err);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Perform an async PUT request
|
|
383
|
+
* @param {string} url - Request URL
|
|
384
|
+
* @param {string|Buffer|Object} [body] - Request body
|
|
385
|
+
* @param {Object} [headers] - Optional custom headers
|
|
386
|
+
* @returns {Promise<Response>} Response object
|
|
387
|
+
*/
|
|
388
|
+
put(url, body = null, headers = null) {
|
|
389
|
+
return this.request({ method: "PUT", url, body, headers });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Perform an async DELETE request
|
|
394
|
+
* @param {string} url - Request URL
|
|
395
|
+
* @param {Object} [headers] - Optional custom headers
|
|
396
|
+
* @returns {Promise<Response>} Response object
|
|
397
|
+
*/
|
|
398
|
+
delete(url, headers = null) {
|
|
399
|
+
return this.request({ method: "DELETE", url, headers });
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Perform an async PATCH request
|
|
404
|
+
* @param {string} url - Request URL
|
|
405
|
+
* @param {string|Buffer|Object} [body] - Request body
|
|
406
|
+
* @param {Object} [headers] - Optional custom headers
|
|
407
|
+
* @returns {Promise<Response>} Response object
|
|
408
|
+
*/
|
|
409
|
+
patch(url, body = null, headers = null) {
|
|
410
|
+
return this.request({ method: "PATCH", url, body, headers });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Perform an async HEAD request
|
|
415
|
+
* @param {string} url - Request URL
|
|
416
|
+
* @param {Object} [headers] - Optional custom headers
|
|
417
|
+
* @returns {Promise<Response>} Response object
|
|
418
|
+
*/
|
|
419
|
+
head(url, headers = null) {
|
|
420
|
+
return this.request({ method: "HEAD", url, headers });
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Perform an async OPTIONS request
|
|
425
|
+
* @param {string} url - Request URL
|
|
426
|
+
* @param {Object} [headers] - Optional custom headers
|
|
427
|
+
* @returns {Promise<Response>} Response object
|
|
428
|
+
*/
|
|
429
|
+
options(url, headers = null) {
|
|
430
|
+
return this.request({ method: "OPTIONS", url, headers });
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ===========================================================================
|
|
434
|
+
// Callback-based Methods
|
|
435
|
+
// ===========================================================================
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Perform a GET request with callback
|
|
439
|
+
* @param {string} url - Request URL
|
|
440
|
+
* @param {Object|Function} [headersOrCallback] - Headers or callback
|
|
441
|
+
* @param {Function} [callback] - Callback function (err, response)
|
|
442
|
+
*/
|
|
443
|
+
getCb(url, headersOrCallback, callback) {
|
|
444
|
+
let headers = null;
|
|
445
|
+
let cb = callback;
|
|
446
|
+
|
|
447
|
+
if (typeof headersOrCallback === "function") {
|
|
448
|
+
cb = headersOrCallback;
|
|
449
|
+
} else {
|
|
450
|
+
headers = headersOrCallback;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
setImmediate(() => {
|
|
454
|
+
try {
|
|
455
|
+
const response = this.getSync(url, headers);
|
|
456
|
+
cb(null, response);
|
|
457
|
+
} catch (err) {
|
|
458
|
+
cb(err, null);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Perform a POST request with callback
|
|
465
|
+
* @param {string} url - Request URL
|
|
466
|
+
* @param {string|Buffer|Object} [body] - Request body
|
|
467
|
+
* @param {Object|Function} [headersOrCallback] - Headers or callback
|
|
468
|
+
* @param {Function} [callback] - Callback function (err, response)
|
|
469
|
+
*/
|
|
470
|
+
postCb(url, body, headersOrCallback, callback) {
|
|
471
|
+
let headers = null;
|
|
472
|
+
let cb = callback;
|
|
473
|
+
|
|
474
|
+
if (typeof headersOrCallback === "function") {
|
|
475
|
+
cb = headersOrCallback;
|
|
476
|
+
} else {
|
|
477
|
+
headers = headersOrCallback;
|
|
478
|
+
cb = callback;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (typeof body === "function") {
|
|
482
|
+
cb = body;
|
|
483
|
+
body = null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
setImmediate(() => {
|
|
487
|
+
try {
|
|
488
|
+
const response = this.postSync(url, body, headers);
|
|
489
|
+
cb(null, response);
|
|
490
|
+
} catch (err) {
|
|
491
|
+
cb(err, null);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Perform a custom request with callback
|
|
498
|
+
* @param {Object} options - Request options
|
|
499
|
+
* @param {Function} callback - Callback function (err, response)
|
|
500
|
+
*/
|
|
501
|
+
requestCb(options, callback) {
|
|
502
|
+
setImmediate(() => {
|
|
503
|
+
try {
|
|
504
|
+
const response = this.requestSync(options);
|
|
505
|
+
callback(null, response);
|
|
506
|
+
} catch (err) {
|
|
507
|
+
callback(err, null);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ===========================================================================
|
|
513
|
+
// Cookie Management
|
|
514
|
+
// ===========================================================================
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Get all cookies from the session
|
|
518
|
+
* @returns {Object} Cookies as key-value pairs
|
|
519
|
+
*/
|
|
520
|
+
getCookies() {
|
|
521
|
+
const result = this._lib.httpcloak_get_cookies(this._handle);
|
|
522
|
+
if (result) {
|
|
523
|
+
return JSON.parse(result);
|
|
524
|
+
}
|
|
525
|
+
return {};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Set a cookie in the session
|
|
530
|
+
* @param {string} name - Cookie name
|
|
531
|
+
* @param {string} value - Cookie value
|
|
532
|
+
*/
|
|
533
|
+
setCookie(name, value) {
|
|
534
|
+
this._lib.httpcloak_set_cookie(this._handle, name, value);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Get cookies as a property
|
|
539
|
+
*/
|
|
540
|
+
get cookies() {
|
|
541
|
+
return this.getCookies();
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
module.exports = {
|
|
546
|
+
Session,
|
|
547
|
+
Response,
|
|
548
|
+
HTTPCloakError,
|
|
549
|
+
version,
|
|
550
|
+
availablePresets,
|
|
551
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@httpcloak/darwin-arm64",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HTTPCloak native binary for darwin arm64",
|
|
5
|
+
"os": [
|
|
6
|
+
"darwin"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"arm64"
|
|
10
|
+
],
|
|
11
|
+
"main": "lib.js",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@httpcloak/darwin-x64",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HTTPCloak native binary for darwin x64",
|
|
5
|
+
"os": [
|
|
6
|
+
"darwin"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"x64"
|
|
10
|
+
],
|
|
11
|
+
"main": "lib.js",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@httpcloak/linux-arm64",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HTTPCloak native binary for linux arm64",
|
|
5
|
+
"os": [
|
|
6
|
+
"linux"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"arm64"
|
|
10
|
+
],
|
|
11
|
+
"main": "lib.js",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@httpcloak/linux-x64",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HTTPCloak native binary for linux x64",
|
|
5
|
+
"os": [
|
|
6
|
+
"linux"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"x64"
|
|
10
|
+
],
|
|
11
|
+
"main": "lib.js",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@httpcloak/win32-arm64",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HTTPCloak native binary for win32 arm64",
|
|
5
|
+
"os": [
|
|
6
|
+
"win32"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"arm64"
|
|
10
|
+
],
|
|
11
|
+
"main": "lib.js",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@httpcloak/win32-x64",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "HTTPCloak native binary for win32 x64",
|
|
5
|
+
"os": [
|
|
6
|
+
"win32"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"x64"
|
|
10
|
+
],
|
|
11
|
+
"main": "lib.js",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "httpcloak",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Browser fingerprint emulation HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node test.js",
|
|
9
|
+
"setup-packages": "node scripts/setup-npm-packages.js"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"http",
|
|
13
|
+
"http2",
|
|
14
|
+
"http3",
|
|
15
|
+
"quic",
|
|
16
|
+
"tls",
|
|
17
|
+
"fingerprint",
|
|
18
|
+
"browser",
|
|
19
|
+
"scraping",
|
|
20
|
+
"bot-detection"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/sardanioss/httpcloak"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/sardanioss/httpcloak/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/sardanioss/httpcloak#readme",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=14.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"koffi": "^2.9.0"
|
|
36
|
+
},
|
|
37
|
+
"optionalDependencies": {
|
|
38
|
+
"@httpcloak/linux-x64": "1.0.0",
|
|
39
|
+
"@httpcloak/linux-arm64": "1.0.0",
|
|
40
|
+
"@httpcloak/darwin-x64": "1.0.0",
|
|
41
|
+
"@httpcloak/darwin-arm64": "1.0.0",
|
|
42
|
+
"@httpcloak/win32-x64": "1.0.0",
|
|
43
|
+
"@httpcloak/win32-arm64": "1.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate platform-specific npm packages for httpcloak
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
const VERSION = "1.4.0";
|
|
10
|
+
|
|
11
|
+
const PLATFORMS = [
|
|
12
|
+
{ name: "linux-x64", os: "linux", cpu: "x64", libName: "libhttpcloak-linux-amd64.so" },
|
|
13
|
+
{ name: "linux-arm64", os: "linux", cpu: "arm64", libName: "libhttpcloak-linux-arm64.so" },
|
|
14
|
+
{ name: "darwin-x64", os: "darwin", cpu: "x64", libName: "libhttpcloak-darwin-amd64.dylib" },
|
|
15
|
+
{ name: "darwin-arm64", os: "darwin", cpu: "arm64", libName: "libhttpcloak-darwin-arm64.dylib" },
|
|
16
|
+
{ name: "win32-x64", os: "win32", cpu: "x64", libName: "libhttpcloak-windows-amd64.dll" },
|
|
17
|
+
{ name: "win32-arm64", os: "win32", cpu: "arm64", libName: "libhttpcloak-windows-arm64.dll" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const npmDir = path.join(__dirname, "..", "npm");
|
|
21
|
+
|
|
22
|
+
// Create each platform package
|
|
23
|
+
for (const platform of PLATFORMS) {
|
|
24
|
+
const pkgDir = path.join(npmDir, platform.name);
|
|
25
|
+
|
|
26
|
+
// Create directory
|
|
27
|
+
fs.mkdirSync(pkgDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
// Create package.json
|
|
30
|
+
const packageJson = {
|
|
31
|
+
name: `@httpcloak/${platform.name}`,
|
|
32
|
+
version: VERSION,
|
|
33
|
+
description: `HTTPCloak native binary for ${platform.os} ${platform.cpu}`,
|
|
34
|
+
os: [platform.os],
|
|
35
|
+
cpu: [platform.cpu],
|
|
36
|
+
main: "lib.js",
|
|
37
|
+
license: "MIT",
|
|
38
|
+
repository: {
|
|
39
|
+
type: "git",
|
|
40
|
+
url: "https://github.com/sardanioss/httpcloak",
|
|
41
|
+
},
|
|
42
|
+
publishConfig: {
|
|
43
|
+
access: "public",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
fs.writeFileSync(
|
|
48
|
+
path.join(pkgDir, "package.json"),
|
|
49
|
+
JSON.stringify(packageJson, null, 2) + "\n"
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Create lib.js that exports the library path
|
|
53
|
+
const libJs = `// Auto-generated - exports path to native library
|
|
54
|
+
const path = require("path");
|
|
55
|
+
module.exports = path.join(__dirname, "${platform.libName}");
|
|
56
|
+
`;
|
|
57
|
+
fs.writeFileSync(path.join(pkgDir, "lib.js"), libJs);
|
|
58
|
+
|
|
59
|
+
console.log(`Created: @httpcloak/${platform.name}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Create optionalDependencies for main package
|
|
63
|
+
const optionalDeps = {};
|
|
64
|
+
for (const platform of PLATFORMS) {
|
|
65
|
+
optionalDeps[`@httpcloak/${platform.name}`] = VERSION;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log("\nAdd to main package.json optionalDependencies:");
|
|
69
|
+
console.log(JSON.stringify(optionalDeps, null, 2));
|
package/test.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTPCloak Node.js Test
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Session, version, availablePresets, HTTPCloakError } = require("./lib");
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log("=== HTTPCloak Node.js Test ===\n");
|
|
9
|
+
|
|
10
|
+
// Test version
|
|
11
|
+
console.log("Version:", version());
|
|
12
|
+
|
|
13
|
+
// Test presets
|
|
14
|
+
const presets = availablePresets();
|
|
15
|
+
console.log("Presets:", presets.slice(0, 5).join(", "), "...");
|
|
16
|
+
console.log("");
|
|
17
|
+
|
|
18
|
+
// Create session
|
|
19
|
+
const session = new Session({ preset: "chrome-143" });
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Test sync GET
|
|
23
|
+
console.log("--- Sync GET ---");
|
|
24
|
+
const syncResponse = session.getSync(
|
|
25
|
+
"https://www.cloudflare.com/cdn-cgi/trace"
|
|
26
|
+
);
|
|
27
|
+
console.log("Status:", syncResponse.statusCode);
|
|
28
|
+
console.log("Protocol:", syncResponse.protocol);
|
|
29
|
+
console.log("");
|
|
30
|
+
|
|
31
|
+
// Test async GET
|
|
32
|
+
console.log("--- Async GET (Promise) ---");
|
|
33
|
+
const asyncResponse = await session.get(
|
|
34
|
+
"https://www.cloudflare.com/cdn-cgi/trace"
|
|
35
|
+
);
|
|
36
|
+
console.log("Status:", asyncResponse.statusCode);
|
|
37
|
+
console.log("Protocol:", asyncResponse.protocol);
|
|
38
|
+
console.log("");
|
|
39
|
+
|
|
40
|
+
// Test callback GET
|
|
41
|
+
console.log("--- Callback GET ---");
|
|
42
|
+
await new Promise((resolve, reject) => {
|
|
43
|
+
session.getCb(
|
|
44
|
+
"https://www.cloudflare.com/cdn-cgi/trace",
|
|
45
|
+
(err, response) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
console.error("Error:", err.message);
|
|
48
|
+
reject(err);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log("Status:", response.statusCode);
|
|
52
|
+
console.log("Protocol:", response.protocol);
|
|
53
|
+
resolve();
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
console.log("");
|
|
58
|
+
|
|
59
|
+
// Test concurrent requests
|
|
60
|
+
console.log("--- Concurrent Requests ---");
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
const responses = await Promise.all([
|
|
63
|
+
session.get("https://www.cloudflare.com/cdn-cgi/trace"),
|
|
64
|
+
session.get("https://www.cloudflare.com/cdn-cgi/trace"),
|
|
65
|
+
]);
|
|
66
|
+
const elapsed = Date.now() - startTime;
|
|
67
|
+
console.log("Concurrent requests:", responses.length);
|
|
68
|
+
console.log("Time:", elapsed, "ms");
|
|
69
|
+
console.log("");
|
|
70
|
+
|
|
71
|
+
// Test cookies
|
|
72
|
+
console.log("--- Cookies ---");
|
|
73
|
+
session.setCookie("test_cookie", "test_value");
|
|
74
|
+
const cookies = session.getCookies();
|
|
75
|
+
console.log("Cookies:", JSON.stringify(cookies));
|
|
76
|
+
console.log("");
|
|
77
|
+
|
|
78
|
+
console.log("=== All tests passed! ===");
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error("Test failed:", err);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
} finally {
|
|
83
|
+
session.close();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
main();
|