httpcloak 1.0.0 → 1.0.2
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/lib/index.js +594 -170
- package/npm/darwin-arm64/package.json +1 -1
- package/npm/darwin-x64/package.json +1 -1
- package/npm/linux-arm64/package.json +1 -1
- package/npm/linux-x64/package.json +1 -1
- package/npm/win32-arm64/package.json +1 -1
- package/npm/win32-x64/package.json +1 -1
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTTPCloak Node.js Client
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* A fetch/axios-compatible HTTP client with browser fingerprint emulation.
|
|
5
|
+
* Provides TLS fingerprinting for HTTP requests.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
const koffi = require("koffi");
|
|
@@ -19,6 +20,48 @@ class HTTPCloakError extends Error {
|
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Available browser presets for TLS fingerprinting.
|
|
25
|
+
*
|
|
26
|
+
* Use these constants instead of typing preset strings manually:
|
|
27
|
+
* const httpcloak = require("httpcloak");
|
|
28
|
+
* httpcloak.configure({ preset: httpcloak.Preset.CHROME_143 });
|
|
29
|
+
*
|
|
30
|
+
* // Or with Session
|
|
31
|
+
* const session = new httpcloak.Session({ preset: httpcloak.Preset.FIREFOX_133 });
|
|
32
|
+
*/
|
|
33
|
+
const Preset = {
|
|
34
|
+
// Chrome 143 (latest)
|
|
35
|
+
CHROME_143: "chrome-143",
|
|
36
|
+
CHROME_143_WINDOWS: "chrome-143-windows",
|
|
37
|
+
CHROME_143_LINUX: "chrome-143-linux",
|
|
38
|
+
CHROME_143_MACOS: "chrome-143-macos",
|
|
39
|
+
|
|
40
|
+
// Chrome 131
|
|
41
|
+
CHROME_131: "chrome-131",
|
|
42
|
+
CHROME_131_WINDOWS: "chrome-131-windows",
|
|
43
|
+
CHROME_131_LINUX: "chrome-131-linux",
|
|
44
|
+
CHROME_131_MACOS: "chrome-131-macos",
|
|
45
|
+
|
|
46
|
+
// Firefox
|
|
47
|
+
FIREFOX_133: "firefox-133",
|
|
48
|
+
|
|
49
|
+
// Safari
|
|
50
|
+
SAFARI_18: "safari-18",
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get all available preset names
|
|
54
|
+
* @returns {string[]} List of all preset names
|
|
55
|
+
*/
|
|
56
|
+
all() {
|
|
57
|
+
return [
|
|
58
|
+
this.CHROME_143, this.CHROME_143_WINDOWS, this.CHROME_143_LINUX, this.CHROME_143_MACOS,
|
|
59
|
+
this.CHROME_131, this.CHROME_131_WINDOWS, this.CHROME_131_LINUX, this.CHROME_131_MACOS,
|
|
60
|
+
this.FIREFOX_133, this.SAFARI_18,
|
|
61
|
+
];
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
22
65
|
/**
|
|
23
66
|
* Response object returned from HTTP requests
|
|
24
67
|
*/
|
|
@@ -26,17 +69,51 @@ class Response {
|
|
|
26
69
|
constructor(data) {
|
|
27
70
|
this.statusCode = data.status_code || 0;
|
|
28
71
|
this.headers = data.headers || {};
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
72
|
+
this._body = Buffer.from(data.body || "", "utf8");
|
|
73
|
+
this._text = data.body || "";
|
|
31
74
|
this.finalUrl = data.final_url || "";
|
|
32
75
|
this.protocol = data.protocol || "";
|
|
33
76
|
}
|
|
34
77
|
|
|
78
|
+
/** Response body as string */
|
|
79
|
+
get text() {
|
|
80
|
+
return this._text;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Response body as Buffer (requests compatibility) */
|
|
84
|
+
get body() {
|
|
85
|
+
return this._body;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Response body as Buffer (requests compatibility alias) */
|
|
89
|
+
get content() {
|
|
90
|
+
return this._body;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Final URL after redirects (requests compatibility alias) */
|
|
94
|
+
get url() {
|
|
95
|
+
return this.finalUrl;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** True if status code < 400 (requests compatibility) */
|
|
99
|
+
get ok() {
|
|
100
|
+
return this.statusCode < 400;
|
|
101
|
+
}
|
|
102
|
+
|
|
35
103
|
/**
|
|
36
104
|
* Parse response body as JSON
|
|
37
105
|
*/
|
|
38
106
|
json() {
|
|
39
|
-
return JSON.parse(this.
|
|
107
|
+
return JSON.parse(this._text);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Raise error if status >= 400 (requests compatibility)
|
|
112
|
+
*/
|
|
113
|
+
raiseForStatus() {
|
|
114
|
+
if (!this.ok) {
|
|
115
|
+
throw new HTTPCloakError(`HTTP ${this.statusCode}`);
|
|
116
|
+
}
|
|
40
117
|
}
|
|
41
118
|
}
|
|
42
119
|
|
|
@@ -47,7 +124,6 @@ function getPlatformPackageName() {
|
|
|
47
124
|
const platform = os.platform();
|
|
48
125
|
const arch = os.arch();
|
|
49
126
|
|
|
50
|
-
// Map to npm platform names
|
|
51
127
|
let platName;
|
|
52
128
|
if (platform === "darwin") {
|
|
53
129
|
platName = "darwin";
|
|
@@ -76,13 +152,11 @@ function getLibPath() {
|
|
|
76
152
|
const platform = os.platform();
|
|
77
153
|
const arch = os.arch();
|
|
78
154
|
|
|
79
|
-
// Check environment variable first
|
|
80
155
|
const envPath = process.env.HTTPCLOAK_LIB_PATH;
|
|
81
156
|
if (envPath && fs.existsSync(envPath)) {
|
|
82
157
|
return envPath;
|
|
83
158
|
}
|
|
84
159
|
|
|
85
|
-
// Try to load from platform-specific optional dependency
|
|
86
160
|
const packageName = getPlatformPackageName();
|
|
87
161
|
try {
|
|
88
162
|
const libPath = require(packageName);
|
|
@@ -90,10 +164,9 @@ function getLibPath() {
|
|
|
90
164
|
return libPath;
|
|
91
165
|
}
|
|
92
166
|
} catch (e) {
|
|
93
|
-
// Optional dependency not installed
|
|
167
|
+
// Optional dependency not installed
|
|
94
168
|
}
|
|
95
169
|
|
|
96
|
-
// Normalize architecture for library name
|
|
97
170
|
let archName;
|
|
98
171
|
if (arch === "x64" || arch === "amd64") {
|
|
99
172
|
archName = "amd64";
|
|
@@ -103,7 +176,6 @@ function getLibPath() {
|
|
|
103
176
|
archName = arch;
|
|
104
177
|
}
|
|
105
178
|
|
|
106
|
-
// Determine OS name and extension
|
|
107
179
|
let osName, ext;
|
|
108
180
|
if (platform === "darwin") {
|
|
109
181
|
osName = "darwin";
|
|
@@ -118,7 +190,6 @@ function getLibPath() {
|
|
|
118
190
|
|
|
119
191
|
const libName = `libhttpcloak-${osName}-${archName}${ext}`;
|
|
120
192
|
|
|
121
|
-
// Search paths (fallback for local development)
|
|
122
193
|
const searchPaths = [
|
|
123
194
|
path.join(__dirname, libName),
|
|
124
195
|
path.join(__dirname, "..", libName),
|
|
@@ -145,26 +216,45 @@ function getLib() {
|
|
|
145
216
|
const libPath = getLibPath();
|
|
146
217
|
const nativeLib = koffi.load(libPath);
|
|
147
218
|
|
|
219
|
+
// Use void* for string returns so we can free them properly
|
|
148
220
|
lib = {
|
|
149
221
|
httpcloak_session_new: nativeLib.func("httpcloak_session_new", "int64", ["str"]),
|
|
150
222
|
httpcloak_session_free: nativeLib.func("httpcloak_session_free", "void", ["int64"]),
|
|
151
|
-
httpcloak_get: nativeLib.func("httpcloak_get", "
|
|
152
|
-
httpcloak_post: nativeLib.func("httpcloak_post", "
|
|
153
|
-
httpcloak_request: nativeLib.func("httpcloak_request", "
|
|
154
|
-
httpcloak_get_cookies: nativeLib.func("httpcloak_get_cookies", "
|
|
223
|
+
httpcloak_get: nativeLib.func("httpcloak_get", "void*", ["int64", "str", "str"]),
|
|
224
|
+
httpcloak_post: nativeLib.func("httpcloak_post", "void*", ["int64", "str", "str", "str"]),
|
|
225
|
+
httpcloak_request: nativeLib.func("httpcloak_request", "void*", ["int64", "str"]),
|
|
226
|
+
httpcloak_get_cookies: nativeLib.func("httpcloak_get_cookies", "void*", ["int64"]),
|
|
155
227
|
httpcloak_set_cookie: nativeLib.func("httpcloak_set_cookie", "void", ["int64", "str", "str"]),
|
|
156
|
-
httpcloak_free_string: nativeLib.func("httpcloak_free_string", "void", ["
|
|
157
|
-
httpcloak_version: nativeLib.func("httpcloak_version", "
|
|
158
|
-
httpcloak_available_presets: nativeLib.func("httpcloak_available_presets", "
|
|
228
|
+
httpcloak_free_string: nativeLib.func("httpcloak_free_string", "void", ["void*"]),
|
|
229
|
+
httpcloak_version: nativeLib.func("httpcloak_version", "void*", []),
|
|
230
|
+
httpcloak_available_presets: nativeLib.func("httpcloak_available_presets", "void*", []),
|
|
159
231
|
};
|
|
160
232
|
}
|
|
161
233
|
return lib;
|
|
162
234
|
}
|
|
163
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Convert a C string pointer to JS string and free the memory
|
|
238
|
+
*/
|
|
239
|
+
function ptrToString(ptr) {
|
|
240
|
+
if (!ptr) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
// Decode the C string from the pointer
|
|
245
|
+
const str = koffi.decode(ptr, "str");
|
|
246
|
+
return str;
|
|
247
|
+
} finally {
|
|
248
|
+
// Always free the C string to prevent memory leaks
|
|
249
|
+
getLib().httpcloak_free_string(ptr);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
164
253
|
/**
|
|
165
254
|
* Parse response from the native library
|
|
166
255
|
*/
|
|
167
|
-
function parseResponse(
|
|
256
|
+
function parseResponse(resultPtr) {
|
|
257
|
+
const result = ptrToString(resultPtr);
|
|
168
258
|
if (!result) {
|
|
169
259
|
throw new HTTPCloakError("No response received");
|
|
170
260
|
}
|
|
@@ -178,12 +268,147 @@ function parseResponse(result) {
|
|
|
178
268
|
return new Response(data);
|
|
179
269
|
}
|
|
180
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Add query parameters to URL
|
|
273
|
+
*/
|
|
274
|
+
function addParamsToUrl(url, params) {
|
|
275
|
+
if (!params || Object.keys(params).length === 0) {
|
|
276
|
+
return url;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const urlObj = new URL(url);
|
|
280
|
+
for (const [key, value] of Object.entries(params)) {
|
|
281
|
+
urlObj.searchParams.append(key, String(value));
|
|
282
|
+
}
|
|
283
|
+
return urlObj.toString();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Apply basic auth to headers
|
|
288
|
+
*/
|
|
289
|
+
function applyAuth(headers, auth) {
|
|
290
|
+
if (!auth) {
|
|
291
|
+
return headers;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const [username, password] = auth;
|
|
295
|
+
const credentials = Buffer.from(`${username}:${password}`).toString("base64");
|
|
296
|
+
|
|
297
|
+
headers = headers ? { ...headers } : {};
|
|
298
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
299
|
+
return headers;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Detect MIME type from filename
|
|
304
|
+
*/
|
|
305
|
+
function detectMimeType(filename) {
|
|
306
|
+
const ext = path.extname(filename).toLowerCase();
|
|
307
|
+
const mimeTypes = {
|
|
308
|
+
".html": "text/html",
|
|
309
|
+
".htm": "text/html",
|
|
310
|
+
".css": "text/css",
|
|
311
|
+
".js": "application/javascript",
|
|
312
|
+
".json": "application/json",
|
|
313
|
+
".xml": "application/xml",
|
|
314
|
+
".txt": "text/plain",
|
|
315
|
+
".csv": "text/csv",
|
|
316
|
+
".jpg": "image/jpeg",
|
|
317
|
+
".jpeg": "image/jpeg",
|
|
318
|
+
".png": "image/png",
|
|
319
|
+
".gif": "image/gif",
|
|
320
|
+
".webp": "image/webp",
|
|
321
|
+
".svg": "image/svg+xml",
|
|
322
|
+
".ico": "image/x-icon",
|
|
323
|
+
".bmp": "image/bmp",
|
|
324
|
+
".mp3": "audio/mpeg",
|
|
325
|
+
".wav": "audio/wav",
|
|
326
|
+
".ogg": "audio/ogg",
|
|
327
|
+
".mp4": "video/mp4",
|
|
328
|
+
".webm": "video/webm",
|
|
329
|
+
".pdf": "application/pdf",
|
|
330
|
+
".zip": "application/zip",
|
|
331
|
+
".gz": "application/gzip",
|
|
332
|
+
".tar": "application/x-tar",
|
|
333
|
+
};
|
|
334
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Encode multipart form data
|
|
339
|
+
* @param {Object} data - Form fields (key-value pairs)
|
|
340
|
+
* @param {Object} files - Files to upload
|
|
341
|
+
* Each key is the field name, value can be:
|
|
342
|
+
* - Buffer: raw file content
|
|
343
|
+
* - { filename, content, contentType? }: file with metadata
|
|
344
|
+
* @returns {{ body: Buffer, contentType: string }}
|
|
345
|
+
*/
|
|
346
|
+
function encodeMultipart(data, files) {
|
|
347
|
+
const boundary = `----HTTPCloakBoundary${Date.now().toString(16)}${Math.random().toString(16).slice(2)}`;
|
|
348
|
+
const parts = [];
|
|
349
|
+
|
|
350
|
+
// Add form fields
|
|
351
|
+
if (data) {
|
|
352
|
+
for (const [key, value] of Object.entries(data)) {
|
|
353
|
+
parts.push(
|
|
354
|
+
`--${boundary}\r\n` +
|
|
355
|
+
`Content-Disposition: form-data; name="${key}"\r\n\r\n` +
|
|
356
|
+
`${value}\r\n`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Add files
|
|
362
|
+
if (files) {
|
|
363
|
+
for (const [fieldName, fileValue] of Object.entries(files)) {
|
|
364
|
+
let filename, content, contentType;
|
|
365
|
+
|
|
366
|
+
if (Buffer.isBuffer(fileValue)) {
|
|
367
|
+
filename = fieldName;
|
|
368
|
+
content = fileValue;
|
|
369
|
+
contentType = "application/octet-stream";
|
|
370
|
+
} else if (typeof fileValue === "object" && fileValue !== null) {
|
|
371
|
+
filename = fileValue.filename || fieldName;
|
|
372
|
+
content = fileValue.content;
|
|
373
|
+
contentType = fileValue.contentType || detectMimeType(filename);
|
|
374
|
+
|
|
375
|
+
if (!Buffer.isBuffer(content)) {
|
|
376
|
+
content = Buffer.from(content);
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
throw new HTTPCloakError(`Invalid file value for field '${fieldName}'`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
parts.push(Buffer.from(
|
|
383
|
+
`--${boundary}\r\n` +
|
|
384
|
+
`Content-Disposition: form-data; name="${fieldName}"; filename="${filename}"\r\n` +
|
|
385
|
+
`Content-Type: ${contentType}\r\n\r\n`
|
|
386
|
+
));
|
|
387
|
+
parts.push(content);
|
|
388
|
+
parts.push(Buffer.from("\r\n"));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
parts.push(Buffer.from(`--${boundary}--\r\n`));
|
|
393
|
+
|
|
394
|
+
// Combine all parts
|
|
395
|
+
const bodyParts = parts.map(p => Buffer.isBuffer(p) ? p : Buffer.from(p));
|
|
396
|
+
const body = Buffer.concat(bodyParts);
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
body,
|
|
400
|
+
contentType: `multipart/form-data; boundary=${boundary}`,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
181
404
|
/**
|
|
182
405
|
* Get the httpcloak library version
|
|
183
406
|
*/
|
|
184
407
|
function version() {
|
|
185
408
|
const nativeLib = getLib();
|
|
186
|
-
|
|
409
|
+
const resultPtr = nativeLib.httpcloak_version();
|
|
410
|
+
const result = ptrToString(resultPtr);
|
|
411
|
+
return result || "unknown";
|
|
187
412
|
}
|
|
188
413
|
|
|
189
414
|
/**
|
|
@@ -191,7 +416,8 @@ function version() {
|
|
|
191
416
|
*/
|
|
192
417
|
function availablePresets() {
|
|
193
418
|
const nativeLib = getLib();
|
|
194
|
-
const
|
|
419
|
+
const resultPtr = nativeLib.httpcloak_available_presets();
|
|
420
|
+
const result = ptrToString(resultPtr);
|
|
195
421
|
if (result) {
|
|
196
422
|
return JSON.parse(result);
|
|
197
423
|
}
|
|
@@ -208,19 +434,51 @@ class Session {
|
|
|
208
434
|
* @param {string} [options.preset="chrome-143"] - Browser preset to use
|
|
209
435
|
* @param {string} [options.proxy] - Proxy URL (e.g., "http://user:pass@host:port")
|
|
210
436
|
* @param {number} [options.timeout=30] - Request timeout in seconds
|
|
437
|
+
* @param {string} [options.httpVersion="auto"] - HTTP version: "auto", "h1", "h2", "h3"
|
|
438
|
+
* @param {boolean} [options.verify=true] - SSL certificate verification
|
|
439
|
+
* @param {boolean} [options.allowRedirects=true] - Follow redirects
|
|
440
|
+
* @param {number} [options.maxRedirects=10] - Maximum number of redirects to follow
|
|
441
|
+
* @param {number} [options.retry=0] - Number of retries on failure
|
|
442
|
+
* @param {number[]} [options.retryOnStatus] - Status codes to retry on
|
|
211
443
|
*/
|
|
212
444
|
constructor(options = {}) {
|
|
213
|
-
const {
|
|
445
|
+
const {
|
|
446
|
+
preset = "chrome-143",
|
|
447
|
+
proxy = null,
|
|
448
|
+
timeout = 30,
|
|
449
|
+
httpVersion = "auto",
|
|
450
|
+
verify = true,
|
|
451
|
+
allowRedirects = true,
|
|
452
|
+
maxRedirects = 10,
|
|
453
|
+
retry = 0,
|
|
454
|
+
retryOnStatus = null,
|
|
455
|
+
} = options;
|
|
214
456
|
|
|
215
457
|
this._lib = getLib();
|
|
458
|
+
this.headers = {}; // Default headers
|
|
216
459
|
|
|
217
460
|
const config = {
|
|
218
461
|
preset,
|
|
219
462
|
timeout,
|
|
463
|
+
http_version: httpVersion,
|
|
220
464
|
};
|
|
221
465
|
if (proxy) {
|
|
222
466
|
config.proxy = proxy;
|
|
223
467
|
}
|
|
468
|
+
if (!verify) {
|
|
469
|
+
config.verify = false;
|
|
470
|
+
}
|
|
471
|
+
if (!allowRedirects) {
|
|
472
|
+
config.allow_redirects = false;
|
|
473
|
+
} else if (maxRedirects !== 10) {
|
|
474
|
+
config.max_redirects = maxRedirects;
|
|
475
|
+
}
|
|
476
|
+
if (retry > 0) {
|
|
477
|
+
config.retry = retry;
|
|
478
|
+
if (retryOnStatus) {
|
|
479
|
+
config.retry_on_status = retryOnStatus;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
224
482
|
|
|
225
483
|
this._handle = this._lib.httpcloak_session_new(JSON.stringify(config));
|
|
226
484
|
|
|
@@ -239,6 +497,16 @@ class Session {
|
|
|
239
497
|
}
|
|
240
498
|
}
|
|
241
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Merge session headers with request headers
|
|
502
|
+
*/
|
|
503
|
+
_mergeHeaders(headers) {
|
|
504
|
+
if (!this.headers || Object.keys(this.headers).length === 0) {
|
|
505
|
+
return headers;
|
|
506
|
+
}
|
|
507
|
+
return { ...this.headers, ...headers };
|
|
508
|
+
}
|
|
509
|
+
|
|
242
510
|
// ===========================================================================
|
|
243
511
|
// Synchronous Methods
|
|
244
512
|
// ===========================================================================
|
|
@@ -246,11 +514,20 @@ class Session {
|
|
|
246
514
|
/**
|
|
247
515
|
* Perform a synchronous GET request
|
|
248
516
|
* @param {string} url - Request URL
|
|
249
|
-
* @param {Object} [
|
|
517
|
+
* @param {Object} [options] - Request options
|
|
518
|
+
* @param {Object} [options.headers] - Custom headers
|
|
519
|
+
* @param {Object} [options.params] - Query parameters
|
|
520
|
+
* @param {Array} [options.auth] - Basic auth [username, password]
|
|
250
521
|
* @returns {Response} Response object
|
|
251
522
|
*/
|
|
252
|
-
getSync(url,
|
|
253
|
-
const
|
|
523
|
+
getSync(url, options = {}) {
|
|
524
|
+
const { headers = null, params = null, auth = null } = options;
|
|
525
|
+
|
|
526
|
+
url = addParamsToUrl(url, params);
|
|
527
|
+
let mergedHeaders = this._mergeHeaders(headers);
|
|
528
|
+
mergedHeaders = applyAuth(mergedHeaders, auth);
|
|
529
|
+
|
|
530
|
+
const headersJson = mergedHeaders ? JSON.stringify(mergedHeaders) : null;
|
|
254
531
|
const result = this._lib.httpcloak_get(this._handle, url, headersJson);
|
|
255
532
|
return parseResponse(result);
|
|
256
533
|
}
|
|
@@ -258,58 +535,111 @@ class Session {
|
|
|
258
535
|
/**
|
|
259
536
|
* Perform a synchronous POST request
|
|
260
537
|
* @param {string} url - Request URL
|
|
261
|
-
* @param {
|
|
262
|
-
* @param {Object} [
|
|
538
|
+
* @param {Object} [options] - Request options
|
|
539
|
+
* @param {string|Buffer|Object} [options.body] - Request body
|
|
540
|
+
* @param {Object} [options.json] - JSON body (will be serialized)
|
|
541
|
+
* @param {Object} [options.data] - Form data (will be URL encoded)
|
|
542
|
+
* @param {Object} [options.files] - Files to upload as multipart/form-data
|
|
543
|
+
* Each key is the field name, value can be:
|
|
544
|
+
* - Buffer: raw file content
|
|
545
|
+
* - { filename, content, contentType? }: file with metadata
|
|
546
|
+
* @param {Object} [options.headers] - Custom headers
|
|
547
|
+
* @param {Object} [options.params] - Query parameters
|
|
548
|
+
* @param {Array} [options.auth] - Basic auth [username, password]
|
|
263
549
|
* @returns {Response} Response object
|
|
264
550
|
*/
|
|
265
|
-
postSync(url,
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
551
|
+
postSync(url, options = {}) {
|
|
552
|
+
let { body = null, json = null, data = null, files = null, headers = null, params = null, auth = null } = options;
|
|
553
|
+
|
|
554
|
+
url = addParamsToUrl(url, params);
|
|
555
|
+
let mergedHeaders = this._mergeHeaders(headers);
|
|
556
|
+
|
|
557
|
+
// Handle multipart file upload
|
|
558
|
+
if (files !== null) {
|
|
559
|
+
const formData = (data !== null && typeof data === "object") ? data : null;
|
|
560
|
+
const multipart = encodeMultipart(formData, files);
|
|
561
|
+
body = multipart.body.toString("latin1"); // Preserve binary data
|
|
562
|
+
mergedHeaders = mergedHeaders || {};
|
|
563
|
+
mergedHeaders["Content-Type"] = multipart.contentType;
|
|
564
|
+
}
|
|
565
|
+
// Handle JSON body
|
|
566
|
+
else if (json !== null) {
|
|
567
|
+
body = JSON.stringify(json);
|
|
568
|
+
mergedHeaders = mergedHeaders || {};
|
|
569
|
+
if (!mergedHeaders["Content-Type"]) {
|
|
570
|
+
mergedHeaders["Content-Type"] = "application/json";
|
|
271
571
|
}
|
|
272
572
|
}
|
|
273
|
-
|
|
274
|
-
if (
|
|
573
|
+
// Handle form data
|
|
574
|
+
else if (data !== null && typeof data === "object") {
|
|
575
|
+
body = new URLSearchParams(data).toString();
|
|
576
|
+
mergedHeaders = mergedHeaders || {};
|
|
577
|
+
if (!mergedHeaders["Content-Type"]) {
|
|
578
|
+
mergedHeaders["Content-Type"] = "application/x-www-form-urlencoded";
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// Handle Buffer body
|
|
582
|
+
else if (Buffer.isBuffer(body)) {
|
|
275
583
|
body = body.toString("utf8");
|
|
276
584
|
}
|
|
277
585
|
|
|
278
|
-
|
|
586
|
+
mergedHeaders = applyAuth(mergedHeaders, auth);
|
|
587
|
+
|
|
588
|
+
const headersJson = mergedHeaders ? JSON.stringify(mergedHeaders) : null;
|
|
279
589
|
const result = this._lib.httpcloak_post(this._handle, url, body, headersJson);
|
|
280
590
|
return parseResponse(result);
|
|
281
591
|
}
|
|
282
592
|
|
|
283
593
|
/**
|
|
284
594
|
* Perform a synchronous custom HTTP request
|
|
285
|
-
* @param {
|
|
286
|
-
* @param {string}
|
|
287
|
-
* @param {
|
|
288
|
-
* @param {Object} [options.
|
|
289
|
-
* @param {string|Buffer|Object} [options.body] - Optional request body
|
|
290
|
-
* @param {number} [options.timeout] - Optional request timeout
|
|
595
|
+
* @param {string} method - HTTP method
|
|
596
|
+
* @param {string} url - Request URL
|
|
597
|
+
* @param {Object} [options] - Request options
|
|
598
|
+
* @param {Object} [options.files] - Files to upload as multipart/form-data
|
|
291
599
|
* @returns {Response} Response object
|
|
292
600
|
*/
|
|
293
|
-
requestSync(options) {
|
|
294
|
-
let {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
601
|
+
requestSync(method, url, options = {}) {
|
|
602
|
+
let { body = null, json = null, data = null, files = null, headers = null, params = null, auth = null, timeout = null } = options;
|
|
603
|
+
|
|
604
|
+
url = addParamsToUrl(url, params);
|
|
605
|
+
let mergedHeaders = this._mergeHeaders(headers);
|
|
606
|
+
|
|
607
|
+
// Handle multipart file upload
|
|
608
|
+
if (files !== null) {
|
|
609
|
+
const formData = (data !== null && typeof data === "object") ? data : null;
|
|
610
|
+
const multipart = encodeMultipart(formData, files);
|
|
611
|
+
body = multipart.body.toString("latin1"); // Preserve binary data
|
|
612
|
+
mergedHeaders = mergedHeaders || {};
|
|
613
|
+
mergedHeaders["Content-Type"] = multipart.contentType;
|
|
614
|
+
}
|
|
615
|
+
// Handle JSON body
|
|
616
|
+
else if (json !== null) {
|
|
617
|
+
body = JSON.stringify(json);
|
|
618
|
+
mergedHeaders = mergedHeaders || {};
|
|
619
|
+
if (!mergedHeaders["Content-Type"]) {
|
|
620
|
+
mergedHeaders["Content-Type"] = "application/json";
|
|
301
621
|
}
|
|
302
622
|
}
|
|
303
|
-
|
|
304
|
-
if (
|
|
623
|
+
// Handle form data
|
|
624
|
+
else if (data !== null && typeof data === "object") {
|
|
625
|
+
body = new URLSearchParams(data).toString();
|
|
626
|
+
mergedHeaders = mergedHeaders || {};
|
|
627
|
+
if (!mergedHeaders["Content-Type"]) {
|
|
628
|
+
mergedHeaders["Content-Type"] = "application/x-www-form-urlencoded";
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
// Handle Buffer body
|
|
632
|
+
else if (Buffer.isBuffer(body)) {
|
|
305
633
|
body = body.toString("utf8");
|
|
306
634
|
}
|
|
307
635
|
|
|
636
|
+
mergedHeaders = applyAuth(mergedHeaders, auth);
|
|
637
|
+
|
|
308
638
|
const requestConfig = {
|
|
309
639
|
method: method.toUpperCase(),
|
|
310
640
|
url,
|
|
311
641
|
};
|
|
312
|
-
if (
|
|
642
|
+
if (mergedHeaders) requestConfig.headers = mergedHeaders;
|
|
313
643
|
if (body) requestConfig.body = body;
|
|
314
644
|
if (timeout) requestConfig.timeout = timeout;
|
|
315
645
|
|
|
@@ -327,14 +657,14 @@ class Session {
|
|
|
327
657
|
/**
|
|
328
658
|
* Perform an async GET request
|
|
329
659
|
* @param {string} url - Request URL
|
|
330
|
-
* @param {Object} [
|
|
660
|
+
* @param {Object} [options] - Request options
|
|
331
661
|
* @returns {Promise<Response>} Response object
|
|
332
662
|
*/
|
|
333
|
-
get(url,
|
|
663
|
+
get(url, options = {}) {
|
|
334
664
|
return new Promise((resolve, reject) => {
|
|
335
665
|
setImmediate(() => {
|
|
336
666
|
try {
|
|
337
|
-
resolve(this.getSync(url,
|
|
667
|
+
resolve(this.getSync(url, options));
|
|
338
668
|
} catch (err) {
|
|
339
669
|
reject(err);
|
|
340
670
|
}
|
|
@@ -345,15 +675,14 @@ class Session {
|
|
|
345
675
|
/**
|
|
346
676
|
* Perform an async POST request
|
|
347
677
|
* @param {string} url - Request URL
|
|
348
|
-
* @param {
|
|
349
|
-
* @param {Object} [headers] - Optional custom headers
|
|
678
|
+
* @param {Object} [options] - Request options
|
|
350
679
|
* @returns {Promise<Response>} Response object
|
|
351
680
|
*/
|
|
352
|
-
post(url,
|
|
681
|
+
post(url, options = {}) {
|
|
353
682
|
return new Promise((resolve, reject) => {
|
|
354
683
|
setImmediate(() => {
|
|
355
684
|
try {
|
|
356
|
-
resolve(this.postSync(url,
|
|
685
|
+
resolve(this.postSync(url, options));
|
|
357
686
|
} catch (err) {
|
|
358
687
|
reject(err);
|
|
359
688
|
}
|
|
@@ -363,14 +692,16 @@ class Session {
|
|
|
363
692
|
|
|
364
693
|
/**
|
|
365
694
|
* Perform an async custom HTTP request
|
|
366
|
-
* @param {
|
|
695
|
+
* @param {string} method - HTTP method
|
|
696
|
+
* @param {string} url - Request URL
|
|
697
|
+
* @param {Object} [options] - Request options
|
|
367
698
|
* @returns {Promise<Response>} Response object
|
|
368
699
|
*/
|
|
369
|
-
request(options) {
|
|
700
|
+
request(method, url, options = {}) {
|
|
370
701
|
return new Promise((resolve, reject) => {
|
|
371
702
|
setImmediate(() => {
|
|
372
703
|
try {
|
|
373
|
-
resolve(this.requestSync(options));
|
|
704
|
+
resolve(this.requestSync(method, url, options));
|
|
374
705
|
} catch (err) {
|
|
375
706
|
reject(err);
|
|
376
707
|
}
|
|
@@ -380,133 +711,37 @@ class Session {
|
|
|
380
711
|
|
|
381
712
|
/**
|
|
382
713
|
* 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
714
|
*/
|
|
388
|
-
put(url,
|
|
389
|
-
return this.request(
|
|
715
|
+
put(url, options = {}) {
|
|
716
|
+
return this.request("PUT", url, options);
|
|
390
717
|
}
|
|
391
718
|
|
|
392
719
|
/**
|
|
393
720
|
* 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
721
|
*/
|
|
398
|
-
delete(url,
|
|
399
|
-
return this.request(
|
|
722
|
+
delete(url, options = {}) {
|
|
723
|
+
return this.request("DELETE", url, options);
|
|
400
724
|
}
|
|
401
725
|
|
|
402
726
|
/**
|
|
403
727
|
* 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
728
|
*/
|
|
409
|
-
patch(url,
|
|
410
|
-
return this.request(
|
|
729
|
+
patch(url, options = {}) {
|
|
730
|
+
return this.request("PATCH", url, options);
|
|
411
731
|
}
|
|
412
732
|
|
|
413
733
|
/**
|
|
414
734
|
* 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
735
|
*/
|
|
419
|
-
head(url,
|
|
420
|
-
return this.request(
|
|
736
|
+
head(url, options = {}) {
|
|
737
|
+
return this.request("HEAD", url, options);
|
|
421
738
|
}
|
|
422
739
|
|
|
423
740
|
/**
|
|
424
741
|
* 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
742
|
*/
|
|
429
|
-
options(url,
|
|
430
|
-
return this.request(
|
|
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
|
-
});
|
|
743
|
+
options(url, options = {}) {
|
|
744
|
+
return this.request("OPTIONS", url, options);
|
|
510
745
|
}
|
|
511
746
|
|
|
512
747
|
// ===========================================================================
|
|
@@ -518,7 +753,8 @@ class Session {
|
|
|
518
753
|
* @returns {Object} Cookies as key-value pairs
|
|
519
754
|
*/
|
|
520
755
|
getCookies() {
|
|
521
|
-
const
|
|
756
|
+
const resultPtr = this._lib.httpcloak_get_cookies(this._handle);
|
|
757
|
+
const result = ptrToString(resultPtr);
|
|
522
758
|
if (result) {
|
|
523
759
|
return JSON.parse(result);
|
|
524
760
|
}
|
|
@@ -542,10 +778,198 @@ class Session {
|
|
|
542
778
|
}
|
|
543
779
|
}
|
|
544
780
|
|
|
781
|
+
// =============================================================================
|
|
782
|
+
// Module-level convenience functions
|
|
783
|
+
// =============================================================================
|
|
784
|
+
|
|
785
|
+
let _defaultSession = null;
|
|
786
|
+
let _defaultConfig = {};
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Configure defaults for module-level functions
|
|
790
|
+
* @param {Object} options - Configuration options
|
|
791
|
+
* @param {string} [options.preset="chrome-143"] - Browser preset
|
|
792
|
+
* @param {Object} [options.headers] - Default headers
|
|
793
|
+
* @param {Array} [options.auth] - Default basic auth [username, password]
|
|
794
|
+
* @param {string} [options.proxy] - Proxy URL
|
|
795
|
+
* @param {number} [options.timeout=30] - Default timeout in seconds
|
|
796
|
+
* @param {string} [options.httpVersion="auto"] - HTTP version: "auto", "h1", "h2", "h3"
|
|
797
|
+
* @param {boolean} [options.verify=true] - SSL certificate verification
|
|
798
|
+
* @param {boolean} [options.allowRedirects=true] - Follow redirects
|
|
799
|
+
* @param {number} [options.maxRedirects=10] - Maximum number of redirects to follow
|
|
800
|
+
* @param {number} [options.retry=0] - Number of retries on failure
|
|
801
|
+
* @param {number[]} [options.retryOnStatus] - Status codes to retry on
|
|
802
|
+
*/
|
|
803
|
+
function configure(options = {}) {
|
|
804
|
+
const {
|
|
805
|
+
preset = "chrome-143",
|
|
806
|
+
headers = null,
|
|
807
|
+
auth = null,
|
|
808
|
+
proxy = null,
|
|
809
|
+
timeout = 30,
|
|
810
|
+
httpVersion = "auto",
|
|
811
|
+
verify = true,
|
|
812
|
+
allowRedirects = true,
|
|
813
|
+
maxRedirects = 10,
|
|
814
|
+
retry = 0,
|
|
815
|
+
retryOnStatus = null,
|
|
816
|
+
} = options;
|
|
817
|
+
|
|
818
|
+
// Close existing session
|
|
819
|
+
if (_defaultSession) {
|
|
820
|
+
_defaultSession.close();
|
|
821
|
+
_defaultSession = null;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Apply auth to headers
|
|
825
|
+
let finalHeaders = applyAuth(headers, auth) || {};
|
|
826
|
+
|
|
827
|
+
// Store config
|
|
828
|
+
_defaultConfig = {
|
|
829
|
+
preset,
|
|
830
|
+
proxy,
|
|
831
|
+
timeout,
|
|
832
|
+
httpVersion,
|
|
833
|
+
verify,
|
|
834
|
+
allowRedirects,
|
|
835
|
+
maxRedirects,
|
|
836
|
+
retry,
|
|
837
|
+
retryOnStatus,
|
|
838
|
+
headers: finalHeaders,
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
// Create new session
|
|
842
|
+
_defaultSession = new Session({
|
|
843
|
+
preset,
|
|
844
|
+
proxy,
|
|
845
|
+
timeout,
|
|
846
|
+
httpVersion,
|
|
847
|
+
verify,
|
|
848
|
+
allowRedirects,
|
|
849
|
+
maxRedirects,
|
|
850
|
+
retry,
|
|
851
|
+
retryOnStatus,
|
|
852
|
+
});
|
|
853
|
+
if (Object.keys(finalHeaders).length > 0) {
|
|
854
|
+
Object.assign(_defaultSession.headers, finalHeaders);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Get or create the default session
|
|
860
|
+
*/
|
|
861
|
+
function _getDefaultSession() {
|
|
862
|
+
if (!_defaultSession) {
|
|
863
|
+
const preset = _defaultConfig.preset || "chrome-143";
|
|
864
|
+
const proxy = _defaultConfig.proxy || null;
|
|
865
|
+
const timeout = _defaultConfig.timeout || 30;
|
|
866
|
+
const httpVersion = _defaultConfig.httpVersion || "auto";
|
|
867
|
+
const verify = _defaultConfig.verify !== undefined ? _defaultConfig.verify : true;
|
|
868
|
+
const allowRedirects = _defaultConfig.allowRedirects !== undefined ? _defaultConfig.allowRedirects : true;
|
|
869
|
+
const maxRedirects = _defaultConfig.maxRedirects || 10;
|
|
870
|
+
const retry = _defaultConfig.retry || 0;
|
|
871
|
+
const retryOnStatus = _defaultConfig.retryOnStatus || null;
|
|
872
|
+
const headers = _defaultConfig.headers || {};
|
|
873
|
+
|
|
874
|
+
_defaultSession = new Session({
|
|
875
|
+
preset,
|
|
876
|
+
proxy,
|
|
877
|
+
timeout,
|
|
878
|
+
httpVersion,
|
|
879
|
+
verify,
|
|
880
|
+
allowRedirects,
|
|
881
|
+
maxRedirects,
|
|
882
|
+
retry,
|
|
883
|
+
retryOnStatus,
|
|
884
|
+
});
|
|
885
|
+
if (Object.keys(headers).length > 0) {
|
|
886
|
+
Object.assign(_defaultSession.headers, headers);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return _defaultSession;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Perform a GET request
|
|
894
|
+
* @param {string} url - Request URL
|
|
895
|
+
* @param {Object} [options] - Request options
|
|
896
|
+
* @returns {Promise<Response>}
|
|
897
|
+
*/
|
|
898
|
+
function get(url, options = {}) {
|
|
899
|
+
return _getDefaultSession().get(url, options);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Perform a POST request
|
|
904
|
+
* @param {string} url - Request URL
|
|
905
|
+
* @param {Object} [options] - Request options
|
|
906
|
+
* @returns {Promise<Response>}
|
|
907
|
+
*/
|
|
908
|
+
function post(url, options = {}) {
|
|
909
|
+
return _getDefaultSession().post(url, options);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Perform a PUT request
|
|
914
|
+
*/
|
|
915
|
+
function put(url, options = {}) {
|
|
916
|
+
return _getDefaultSession().put(url, options);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Perform a DELETE request
|
|
921
|
+
*/
|
|
922
|
+
function del(url, options = {}) {
|
|
923
|
+
return _getDefaultSession().delete(url, options);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Perform a PATCH request
|
|
928
|
+
*/
|
|
929
|
+
function patch(url, options = {}) {
|
|
930
|
+
return _getDefaultSession().patch(url, options);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Perform a HEAD request
|
|
935
|
+
*/
|
|
936
|
+
function head(url, options = {}) {
|
|
937
|
+
return _getDefaultSession().head(url, options);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Perform an OPTIONS request
|
|
942
|
+
*/
|
|
943
|
+
function options(url, opts = {}) {
|
|
944
|
+
return _getDefaultSession().options(url, opts);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Perform a custom HTTP request
|
|
949
|
+
*/
|
|
950
|
+
function request(method, url, options = {}) {
|
|
951
|
+
return _getDefaultSession().request(method, url, options);
|
|
952
|
+
}
|
|
953
|
+
|
|
545
954
|
module.exports = {
|
|
955
|
+
// Classes
|
|
546
956
|
Session,
|
|
547
957
|
Response,
|
|
548
958
|
HTTPCloakError,
|
|
959
|
+
// Presets
|
|
960
|
+
Preset,
|
|
961
|
+
// Configuration
|
|
962
|
+
configure,
|
|
963
|
+
// Module-level functions
|
|
964
|
+
get,
|
|
965
|
+
post,
|
|
966
|
+
put,
|
|
967
|
+
delete: del,
|
|
968
|
+
patch,
|
|
969
|
+
head,
|
|
970
|
+
options,
|
|
971
|
+
request,
|
|
972
|
+
// Utility
|
|
549
973
|
version,
|
|
550
974
|
availablePresets,
|
|
551
975
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "httpcloak",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Browser fingerprint emulation HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"koffi": "^2.9.0"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@httpcloak/linux-x64": "1.0.
|
|
39
|
-
"@httpcloak/linux-arm64": "1.0.
|
|
40
|
-
"@httpcloak/darwin-x64": "1.0.
|
|
41
|
-
"@httpcloak/darwin-arm64": "1.0.
|
|
42
|
-
"@httpcloak/win32-x64": "1.0.
|
|
43
|
-
"@httpcloak/win32-arm64": "1.0.
|
|
38
|
+
"@httpcloak/linux-x64": "1.0.2",
|
|
39
|
+
"@httpcloak/linux-arm64": "1.0.2",
|
|
40
|
+
"@httpcloak/darwin-x64": "1.0.2",
|
|
41
|
+
"@httpcloak/darwin-arm64": "1.0.2",
|
|
42
|
+
"@httpcloak/win32-x64": "1.0.2",
|
|
43
|
+
"@httpcloak/win32-arm64": "1.0.2"
|
|
44
44
|
}
|
|
45
45
|
}
|