UNPKG

@galihrivanto/node-libcurli

Version:

Node.js bindings for curl-impersonate library

137 lines 5.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.create = create; const tslib_1 = require("tslib"); const curly_1 = require("../shared/curly"); const child_process_1 = require("child_process"); const path_1 = tslib_1.__importDefault(require("path")); const curlDir = 'deps/curl-impersonate/build/bin'; function parseCurlOutput(stderr, stdout) { const response = { StatusCode: 200, Headers: {}, Body: stdout.trim() }; // Extract status line (e.g., "HTTP/2 200" or "HTTP/1.1 404 Not Found") const statusLineMatch = stderr.match(/< HTTP\/\d(\.\d)? (\d{3})/); if (statusLineMatch) { response.StatusCode = parseInt(statusLineMatch[2], 10); } // Extract headers const headerLines = stderr.split("\n").filter((line) => line.startsWith("<") && !line.includes("HTTP/")); headerLines.forEach((line) => { const match = line.match(/< ([^:]+):\s*(.*)/); if (match) { response.Headers[match[1].trim()] = match[2].trim(); } }); return response; } function create(browser) { function runCommand(method, url, headers, body) { const args = [ '-v', '-X', method ]; if (headers) { for (const [key, value] of Object.entries(headers)) { args.push('-H', `"${key}: ${value}"`); } } if (method !== 'GET') { args.push('-d', body ? JSON.stringify(body) : ''); } args.push(url); const curlCommand = path_1.default.resolve(`${curlDir}/curl_${browser}`); const command = `${curlCommand} ${args.join(' ')}`; return new Promise((resolve, reject) => { const process = (0, child_process_1.exec)(command, { encoding: 'utf8', }, (error, stdout, stderr) => { if (error) { reject(error); return; } const response = parseCurlOutput(stderr, stdout); if (response.StatusCode >= 400) { reject(new Error(`request error, status code ${response.StatusCode}`)); } resolve(response); }); // Optionally, you can also handle the streams directly process.stdout?.setEncoding('utf8'); process.stderr?.setEncoding('utf8'); }); } function fn(url, options = {}) { return new Promise((resolve, reject) => { const method = (options.customRequest || 'GET'); // construct headers const headers = {}; if (options.httpHeader) { options.httpHeader.forEach((header) => { const parts = header.split(':'); if (parts.length == 2) { headers[parts[0].trim()] = parts[1].trim(); } }); } runCommand(method, url, headers) .then(response => { // get content type const contentTypeEntry = Object.entries(response.Headers) .find(([k]) => k.toLowerCase() === 'content-type'); const contentType = contentTypeEntry ? contentTypeEntry[1] : ''; let parser = curly_1.defaultResponseBodyParsers[contentType]; if (!parser) { parser = (data, _headers) => data.toString('utf8'); } const headers = [{ ...response.Headers }]; // Convert string to Buffer before parsing const bodyBuffer = Buffer.from(response.Body); const body = parser(bodyBuffer, headers); const result = { statusCode: response.StatusCode, headers: headers, data: body }; resolve(result); }) .catch(e => { reject(e); }); }); } fn.create = create; fn.defaultResponseBodyParsers = {}; const httpMethodOptionsMap = { get: null, post: (_m, o) => ({ post: true, ...o, }), head: (_m, o) => ({ nobody: true, ...o, }), _: (m, o) => ({ customRequest: m.toUpperCase(), ...o, }), }; for (const httpMethod of curly_1.methods) { const httpMethodOptionsKey = Object.prototype.hasOwnProperty.call(httpMethodOptionsMap, httpMethod) ? httpMethod : '_'; const httpMethodOptions = httpMethodOptionsMap[httpMethodOptionsKey]; // @ts-ignore fn[httpMethod] = httpMethodOptions === null ? fn : (url, options = {}) => fn(url, { ...httpMethodOptions(httpMethod, options), }); } return fn; } //# sourceMappingURL=binaryBrowser.js.map