@galihrivanto/node-libcurli
Version:
Node.js bindings for curl-impersonate library
137 lines • 5.04 kB
JavaScript
;
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