UNPKG

sync-request-curl

Version:

Fast way to send synchronous web requests in NodeJS. API is a subset of sync-request. Leverages node-libcurl for high performance. Cannot be used in a browser.

191 lines (189 loc) 7.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const node_libcurl_1 = require("node-libcurl"); const utils_1 = require("./utils"); /** * Create a libcurl Easy object with default configurations * * @param {HttpVerb} method - The HTTP method (e.g., 'GET', 'POST', 'PUT') * @param {Options} options - configuration options for the request. * @returns {Easy} an initialized libcurl Easy object with default options */ const createCurlObjectWithDefaults = (method, options) => { var _a, _b; const curl = new node_libcurl_1.Easy(); curl.setOpt(node_libcurl_1.Curl.option.CUSTOMREQUEST, method); curl.setOpt(node_libcurl_1.Curl.option.TIMEOUT_MS, (_a = options.timeout) !== null && _a !== void 0 ? _a : 0); curl.setOpt(node_libcurl_1.Curl.option.FOLLOWLOCATION, options.followRedirects === undefined || options.followRedirects); curl.setOpt(node_libcurl_1.Curl.option.MAXREDIRS, (_b = options.maxRedirects) !== null && _b !== void 0 ? _b : -1); curl.setOpt(node_libcurl_1.Curl.option.SSL_VERIFYPEER, !options.insecure); curl.setOpt(node_libcurl_1.Curl.option.NOBODY, method === 'HEAD'); return curl; }; /** * Handles query string parameters in a URL, modifies the URL if necessary, * and sets it as the CURLOPT_URL option in the given cURL Easy object. * * @param {Easy} curl - The cURL easy handle * @param {string} url - The URL to handle query string parameters for * @param {Object.<string, any>} qs - query string parameters for the request */ const handleQueryString = (curl, url, qs) => { url = qs && Object.keys(qs).length ? (0, utils_1.handleQs)(url, qs) : url; curl.setOpt(node_libcurl_1.Curl.option.URL, url); }; /** * Sets up a callback function for the cURL Easy object to handle returned * headers and populate the input array with header lines. * * @param {Easy} curl - The cURL easy handle * @param {string[]} returnedHeaderArray - array for returned header lines */ const handleOutgoingHeaders = (curl, returnedHeaderArray) => { curl.setOpt(node_libcurl_1.Curl.option.HEADERFUNCTION, (headerLine) => { returnedHeaderArray.push(headerLine.toString('utf-8').trim()); return headerLine.length; }); }; /** * Sets the JSON payload for the curl request. * @param {Easy} curl - The curl object. * @param {any} json - The JSON body to be sent * @param {string[]} httpHeaders - HTTP headers for the request */ const setJSONPayload = (curl, json, httpHeaders) => { httpHeaders.push('Content-Type: application/json'); const payload = JSON.stringify(json); httpHeaders.push(`Content-Length: ${Buffer.byteLength(payload, 'utf-8')}`); curl.setOpt(node_libcurl_1.Curl.option.POSTFIELDS, payload); }; /** * Sets the buffer payload for the curl request. * @param {Easy} curl - The curl object. * @param {string | Buffer} body - The body to be sent in the request. * @param {string[]} httpHeaders - HTTP headers for the request */ const setBodyPayload = (curl, body, httpHeaders) => { if (Buffer.isBuffer(body)) { let position = 0; curl.setOpt(node_libcurl_1.Curl.option.POST, true); curl.setOpt(node_libcurl_1.Curl.option.POSTFIELDSIZE, -1); curl.setOpt(node_libcurl_1.Curl.option.READFUNCTION, (buffer, size, nmemb) => { const amountToRead = size * nmemb; if (position === body.length) { return 0; } const totalWritten = body.copy(buffer, 0, position, Math.min(amountToRead, body.length)); position += totalWritten; return totalWritten; }); } else { curl.setOpt(node_libcurl_1.Curl.option.POSTFIELDS, body); httpHeaders.push(`Content-Length: ${Buffer.byteLength(body, 'utf-8')}`); } }; /** * Sets the buffer payload for the curl request. * @param {Easy} curl - The curl object. * @param {Array<HttpPostField>} formData - list of files/contents to be sent */ const setFormPayload = (curl, formData) => { curl.setOpt(node_libcurl_1.Curl.option.HTTPPOST, formData); }; /** * Prepares the request body and headers for a cURL request based on provided * options. Also sets up a callback function for the cURL Easy object to handle * returned body and populates the input buffet. * * @param {Easy} curl - The cURL easy handle * @param {Options} options - Options for configuring the request * @param {{ body: Buffer }} buffer - wrapped buffer for the returned body * @param {string[]} httpHeaders - HTTP headers for the request */ const handleBodyAndRequestHeaders = (curl, options, buffer, httpHeaders) => { if (options.json) { setJSONPayload(curl, options.json, httpHeaders); } else if (options.body) { setBodyPayload(curl, options.body, httpHeaders); } else if (options.formData) { setFormPayload(curl, options.formData); } else { httpHeaders.push('Content-Length: 0'); } curl.setOpt(node_libcurl_1.Curl.option.WRITEFUNCTION, (buff, nmemb, size) => { buffer.body = Buffer.concat([buffer.body, buff.subarray(0, nmemb * size)]); return nmemb * size; }); curl.setOpt(node_libcurl_1.Curl.option.HTTPHEADER, httpHeaders); }; /** * Performs an HTTP request using cURL with the specified parameters. * * @param {HttpVerb} method - The HTTP method for the request (e.g., 'GET', 'POST') * @param {string} url - The URL to make the request to * @param {Options} [options={}] - An object to configure the request * @returns {Response} - HTTP response consisting of status code, headers, and body */ const request = (method, url, options = {}) => { const curl = createCurlObjectWithDefaults(method, options); handleQueryString(curl, url, options.qs); // Body/JSON and Headers (incoming) const bufferWrap = { body: Buffer.alloc(0) }; handleBodyAndRequestHeaders(curl, options, bufferWrap, (0, utils_1.parseIncomingHeaders)(options.headers)); // Headers (outgoing) const returnedHeaderArray = []; handleOutgoingHeaders(curl, returnedHeaderArray); if (options.setEasyOptions) { options.setEasyOptions(curl, node_libcurl_1.Curl.option); } // Execute request const code = curl.perform(); (0, utils_1.checkValidCurlCode)(code, { method, url, options }); // Creating return object const statusCode = curl.getInfo('RESPONSE_CODE').data; const headers = (0, utils_1.parseReturnedHeaders)(returnedHeaderArray); const { body } = bufferWrap; /** * Get the body of a response with an optional encoding. * * @throws {Error} if the status code is >= 300 * @returns {Buffer | string} buffer body by default, string body with encoding */ const getBody = (encoding) => { (0, utils_1.checkValidStatusCode)(statusCode, body); return typeof encoding === 'string' ? body.toString(encoding) : body; }; /** * Get the JSON-parsed body of a response. * * @throws {Error} if the body is nto a valid JSON * @returns {any} parsed JSON body */ const getJSON = (encoding) => { try { return JSON.parse(body.toString(encoding)); } catch (err) { throw new Error(` The server body response for - ${method} - ${url} cannot be parsed as JSON. Body: ${body.toString(encoding)} JSON-Parsing Error Message: ${err instanceof Error ? err.message : String(err)} `); } }; url = curl.getInfo('EFFECTIVE_URL').data; curl.close(); return { statusCode, headers, url, body, getBody, getJSON }; }; exports.default = request; //# sourceMappingURL=request.js.map