{"id":14990027,"url":"https://github.com/veliovgroup/request-extra","last_synced_at":"2025-09-08T01:32:23.249Z","repository":{"id":34849395,"uuid":"184476875","full_name":"veliovgroup/request-extra","owner":"veliovgroup","description":"⚡️ Extremely stable HTTP request module built on top of libcurl with retries, timeouts, async/await/promise and callback APIs","archived":false,"fork":false,"pushed_at":"2025-07-01T22:27:28.000Z","size":593,"stargazers_count":24,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-05T19:46:54.486Z","etag":null,"topics":["curl","http","http-client","javascript","libcurl","nodejs","npm-package","request"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/request-libcurl","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/veliovgroup.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"dr-dimitru","custom":"https://paypal.me/veliovgroup"}},"created_at":"2019-05-01T20:24:11.000Z","updated_at":"2025-07-02T13:59:11.000Z","dependencies_parsed_at":"2024-06-19T05:32:50.838Z","dependency_job_id":"487a85e5-713c-4aed-b157-11c381233aba","html_url":"https://github.com/veliovgroup/request-extra","commit_stats":{"total_commits":108,"total_committers":3,"mean_commits":36.0,"dds":0.07407407407407407,"last_synced_commit":"dfbab610b9d89a5a6e8763fe60089f3aa8aeca5c"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/veliovgroup/request-extra","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Frequest-extra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Frequest-extra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Frequest-extra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Frequest-extra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/veliovgroup","download_url":"https://codeload.github.com/veliovgroup/request-extra/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Frequest-extra/sbom","scorecard":{"id":147549,"data":{"date":"2022-08-15","repo":{"name":"github.com/veliovgroup/request-extra","commit":"183e1927c281d3811f8c550cd6b00afe59afe79d"},"scorecard":{"version":"v4.5.0-28-g6dcfde9","commit":"6dcfde9299033d3b16d81985a9634092411e0cfc"},"score":5.8,"checks":[{"name":"Code-Review","score":0,"reason":"GitHub code reviews found for 2 commits out of the last 30 -- score normalized to 0","details":["Warn: no reviews found for commit: 183e1927c281d3811f8c550cd6b00afe59afe79d","Warn: no reviews found for commit: 2b18a80a11b1d39ad5a6eb39c53266dc722026f4","Warn: no reviews found for commit: e09d42048e13f7a6dec90cdc65506aceca43028c","Warn: no reviews found for commit: cc54596e7fc1797d8a7e3a3668f8c6776672e2f2","Warn: no reviews found for commit: 96e30d371420c39e7d9173c58dc74c5938d3efd8","Warn: no reviews found for commit: 320e793f2f508b3a6abd4830f49bec1f8c22b499","Warn: no reviews found for commit: 7c35579f780f6e963c736007fae0b9020198ff9c","Warn: no reviews found for commit: ad59b68855ac64ae9134f664c4a9449d8ae1840b","Warn: no reviews found for commit: f75e075ff3ccc05112e71e3d41a95bca43c543f6","Warn: no reviews found for commit: 7ea4e72529ffaea60629e2c109dac21a895dbdf8","Warn: no reviews found for commit: 4cc9a431099c43639d504f632952f95bf904b0da","Warn: no reviews found for commit: dc9d991254bcab2dda40d81625d93a95e29090ec","Warn: no reviews found for commit: c4d52d5e5173861c37378624acebbcc53cedd08b","Warn: no reviews found for commit: 45fd24f9726efc3557296a528009a6a505cd2fe1","Warn: no reviews found for commit: a4d4bc2e9fa1fb7db44f55674a99924d5ea223f6","Warn: no reviews found for commit: 5c8a5f786d1792fd908fec3d8ee972c1d889ecd4","Warn: no reviews found for commit: 4897b38abfb48ab97c9d8054c5eb42911af52d12","Warn: no reviews found for commit: 31beed0961a8913ab884c6784eceef028b2e9c36","Warn: no reviews found for commit: e648402aabe2b99e9de547cf4d7becec5a876923","Warn: no reviews found for commit: 9254912d1e8e10114fb926d05569516f021878e4","Warn: no reviews found for commit: 141ddc8cc6df6ea199245224780dd89324e9268f","Warn: no reviews found for commit: 83e2f0580c04ca18b9faf8f3f8bc56bb99bda948","Warn: no reviews found for commit: c81db9e723b4ceda1b809d7d84f6f75709053f81","Warn: no reviews found for commit: b8aecf4f3e046f56b77b43758892d3fe5e9c70a0","Warn: no reviews found for commit: 7cb8fc34715821ca51edbe138b2e826aacad791f","Warn: no reviews found for commit: 37045093511482cef9778aa32a1897a82f74545d","Warn: no reviews found for commit: d43a223160794e4d180ced9916dd9edd9ff42362","Warn: no reviews found for commit: 00eee203f801d467e426e783c8f0fd712777f20d"],"documentation":{"short":"Determines if the project requires code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#code-review"}},{"name":"Maintained","score":1,"reason":"2 commit(s) out of 30 and 0 issue activity out of 2 found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no badge detected","details":null,"documentation":{"short":"Determines if the project has a CII Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"no vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#vulnerabilities"}},{"name":"Token-Permissions","score":10,"reason":"tokens are read-only in GitHub workflows","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"no published package detected","details":["Warn: no GitHub publishing workflow detected"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":10,"reason":"all dependencies are pinned","details":["Info: GitHub-owned GitHubActions are pinned","Info: Third-party GitHubActions are pinned","Info: Dockerfile dependencies are pinned","Info: no insecure (not pinned by hash) dependency downloads found in Dockerfiles","Info: no insecure (not pinned by hash) dependency downloads found in shell scripts"],"documentation":{"short":"Determines if the project has declared and pinned its dependencies.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: : LICENSE:1"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#license"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#binary-artifacts"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":["Warn: no GitHub releases found"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":null,"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#security-policy"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: Dependabot detected"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#dependency-update-tool"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":null,"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/6dcfde9299033d3b16d81985a9634092411e0cfc/docs/checks.md#fuzzing"}}]},"last_synced_at":"2025-08-16T09:41:54.789Z","repository_id":34849395,"created_at":"2025-08-16T09:41:54.789Z","updated_at":"2025-08-16T09:41:54.789Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274121922,"owners_count":25225801,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-07T02:00:09.463Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["curl","http","http-client","javascript","libcurl","nodejs","npm-package","request"],"created_at":"2024-09-24T14:19:21.265Z","updated_at":"2025-09-08T01:32:23.238Z","avatar_url":"https://github.com/veliovgroup.png","language":"JavaScript","readme":"[![support](https://img.shields.io/badge/support-GitHub-white)](https://github.com/sponsors/dr-dimitru)\n[![support](https://img.shields.io/badge/support-PayPal-white)](https://paypal.me/veliovgroup)\n\u003ca href=\"https://ostr.io/info/built-by-developers-for-developers?ref=github-request-extra-repo-top\"\u003e\u003cimg src=\"https://ostr.io/apple-touch-icon-60x60.png\" height=\"20\"\u003e\u003c/a\u003e\n\u003ca href=\"https://meteor-files.com/?ref=github-request-extra-repo-top\"\u003e\u003cimg src=\"https://meteor-files.com/apple-touch-icon-60x60.png\" height=\"20\"\u003e\u003c/a\u003e\n\n# Request-libcurl\n\n```shell\nnpm install --save request-libcurl\n```\n\n__This is a server-only package.__ `request-libcurl` package was created due to a lack of stability in the Node's `http`/`https` *ClientRequest* and simplicity of `fetch` modules. Since we've been looking for something tested by decades and generations, — our choice stopped on `libcurl`. Hence `request-libcurl` package incorporates stability and performance of `libcurl` with simplicity of `request` API\n\n## Main features\n\n- 👨‍💻 98% tests coverage + TDD (*only for http(s)*);\n- 👷‍♂️ Follow `request` API (*simplified*);\n- 🚀 Async/Await API + callback API\n- 📦 The single dependency on `node-libcurl` package;\n- ㊗️ IDNs support (*internationalized domain names*);\n- 🛡 Repeat (*built-in retries*) request on failed or broken connection;\n- 😎 HTTP/2 support;\n- 🤘 HTTP/3 support!\n- 🎒 Send GET/POST with custom `body` and headers;\n- 🗂 Pipe to the file;\n- 🚦 Follow or deny redirects;\n- 📤 [Upload files with a single line](https://github.com/veliovgroup/request-extra#file-upload);\n- 🔐 Ignore or deny \"broken\" SSL/TLS certificates;\n- 💪 Bulletproof design, during development we avoid complex solutions.\n\n## ToC:\n\n- [Installation](https://github.com/veliovgroup/request-extra#install)\n- [API](https://github.com/veliovgroup/request-extra#api)\n- [Request options *detailed* description](https://github.com/veliovgroup/request-extra#request-options):\n  - [response description (*success*)](https://github.com/veliovgroup/request-extra#response)\n  - [error description (*fail*)](https://github.com/veliovgroup/request-extra#error)\n  - [*LibCurlRequest* API](https://github.com/veliovgroup/request-extra#returns-req-object)\n- [List of default request options](https://github.com/veliovgroup/request-extra#request-default-options)\n- [Examples](https://github.com/veliovgroup/request-extra#examples):\n  - [GET](https://github.com/veliovgroup/request-extra#get-request)\n  - [POST](https://github.com/veliovgroup/request-extra#post-request)\n  - [POST (*advanced*)](https://github.com/veliovgroup/request-extra#post-request-with-extra-options)\n  - [File download via `.pipe()`](https://github.com/veliovgroup/request-extra#file-download)\n  - [File upload](https://github.com/veliovgroup/request-extra#file-upload)\n  - [File upload (*multipart*)](https://github.com/veliovgroup/request-extra#file-upload-multipartform-data)\n- [Known Issues](https://github.com/veliovgroup/request-extra#known-issues)\n- [Running tests](https://github.com/veliovgroup/request-extra#running-tests)\n  - [Tests](https://github.com/veliovgroup/request-extra/blob/master/test/npm.js)\n- [Support](https://github.com/veliovgroup/request-extra#support-our-open-source-contribution)\n\n## Install\n\n```shell\n# ONLY for node@\u003e=18\nnpm install request-libcurl --save\n\n# for node@\u003e=16.14 || \u003e=18 || \u003e=20 || \u003e=21 (no async API)\nnpm install request-libcurl@4.0.0 --save\n\n# for node@^14.14 || \u003e=16 || \u003c16.14 (no async API)\nnpm install request-libcurl@3.0.0 --save\n\n# for node@\u003e=9.0.0 (no async API)\nnpm install request-libcurl@2.3.4 --save\n```\n\n```js\n// Import\nimport request, { requestAsync } from 'request-libcurl';\n\n// CommonJS\nconst { default: request, requestAsync } = require('request-libcurl');\n// OR\nconst request = require('request-libcurl').default;\nconst requestAsync = require('request-libcurl').requestAsync;\n```\n\n## Basic usage\n\n```js\nconst { status, headers, body } = await requestAsync({ url: 'https://api.example.com/data.json' });\n```\n\n## Note\n\nWe build this package to serve our needs and solve our issues with Node's native API. It may have a lack of compatibility with `request()` module API, or be compatible only partially. [PRs, suggestions, and discussions](https://github.com/veliovgroup/request-extra/issues) are always welcome.\n\n## API\n\n```js\nimport request, { requestAsync } from 'request-libcurl';\n\nconst opts = {\n  method: 'GET', // POST, GET\n  url: 'https://example.com', // String\n  auth: 'username:password', // String\n  form: '{\"ops\": \"value\"}', // String, can be JSON or any other type of payload\n  headers: { // Object\n    Accept: '*/*',\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'\n  },\n  debug: false, // Boolean\n  retry: true, // Boolean\n  retries: 3, // Number\n  timeout: 6144, // Number\n  keepAlive: false, // Boolean\n  retryDelay: 256, // Number\n  followRedirect: true, // Boolean\n  maxRedirects: 4, // Number\n  rejectUnauthorized: false // Boolean\n};\n\n// Async API\ntry {\n  const { statusCode, body, headers } = await requestAsync(opts);\n} catch (error) {\n  // Houston we got a problem! 😱\n  const { errorCode, code, statusCode, message } = error;\n}\n\n// Callback API\nrequest(opts, (error, resp) =\u003e {\n  if (error) {\n    // Houston we got a problem! 😱\n    const { errorCode, code, statusCode, message } = error;\n  } else {\n    // We've got successful response! 🥳\n    const { statusCode, body, headers } = resp;\n  }\n});\n```\n\n### Request default options\n\n```js\nimport request from 'request-libcurl';\n\n// Default \"defaultOptions\" Object:\nrequest.defaultOptions = {\n  wait: false,\n  proxy: false,\n  retry: true,\n  debug: false,\n  method: 'GET',\n  timeout: 6144,\n  retries: 3,\n  rawBody: false,\n  keepAlive: false,\n  noStorage: false,\n  retryDelay: 256,\n  maxRedirects: 4,\n  followRedirect: true,\n  rejectUnauthorized: false,\n  rejectUnauthorizedProxy: false,\n  badStatuses: [ 300, 303, 305, 400, 407, 408, 409, 410, 500, 502, 503, 504, 510 ],\n  isBadStatus(statusCode, badStatuses = request.defaultOptions.badStatuses) {\n    return badStatuses.includes(statusCode) || statusCode \u003e= 500;\n  },\n  headers: {\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',\n    Accept: '*/*'\n  }\n};\n\n// Override default settings:\nrequest.defaultOptions.timeout = 7000;\nrequest.defaultOptions.retries = 12;\nrequest.defaultOptions.retryDelay = 5000;\nrequest.defaultOptions.followRedirect = false;\n\n// Override bad statuses codes (used to trigger retries)\nrequest.defaultOptions.badStatuses = [300, 303, 305, 400, 407, 408, 409, 410];\n\n// Override function used to trigger retries based on status code\nrequest.defaultOptions.isBadStatus = (statusCode, badStatuses = request.defaultOptions.badStatuses) =\u003e {\n  return badStatuses.includes(statusCode) || statusCode \u003e= 500;\n};\n```\n\n### Request options\n\n- `opts.url` or `opts.uri` {*String*} - [__Required__] Fully qualified URI with protocol `http`/`https`;\n- `opts.method` {*String*} - [Optional] HTTP Method name, you can use any valid method name from HTTP specs, tested with GET/POST, default: `GET`;\n- `opts.auth` {*String*} - [Optional] value for HTTP Authorization header as plain string in a form of `username:password`;\n- `opts.form` {*String*|*Object*} - [Optional] Custom request body for POST request. If {*String*} is passed `Content-Type` will be set to `application/x-www-form-urlencoded`, by passing plain {*Object*} `Content-Type` will be set to `application/json`. To set custom `Content-Type` — pass it to `opts.headers` *Object*;\n- `opts.upload` {*Integer*} - [Optional] To upload a file pass an *Integer* representing the *file descriptor*. See [this example](https://github.com/veliovgroup/request-extra#file-upload) for reference;\n- `opts.pipeTo` {*stream.Writable*} - [Optional] Pass response data to *writableStream*, for example download a file to FS via `{pipeTo: fs.createWriteStream('/path/to/file.pdf')}`;\n- `opts.headers` {*Object*} - [Optional] Custom request headers, default: `{ Accept: '*/*', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36' }`. Note: setting custom request headers will replace default ones;\n- `opts.debug` {*Boolean*} - [Optional] Enable debug and extra logging, default: `false`;\n- `opts.retry` {*Boolean*} - [Optional] Retry request if connection is broken? Default: `true`;\n- `opts.retries` {*Number*} - [Optional] How many times retry request if connection is broken, default: `3`;\n- `opts.retryDelay` {*Number*} - [Optional] How long to wait between request retries (*ms*), default: `256`;\n- `opts.timeout` {*Number*} - [Optional] How long to wait for response (*ms*), default: `6144`;\n- `opts.followRedirect` {*Boolean*} - [Optional] Shall request follow redirects? Default: `true`;\n- `opts.keepAlive` {*Boolean*} - [Optional] Turn on TCP keepalive probes, default: `false`;\n- `opts.maxRedirects` {*Number*} - [Optional] How many redirects are supported during single request, default: `4`;\n- `opts.badStatuses` {*[Number]*} - [Optional] Array of \"bad\" status codes responsible for triggering request retries, default: `[300, 303, 305, 400, 407, 408, 409, 410, 500, 502, 503, 504, 510]`;\n- `opts.isBadStatus` {*Function*} - [Optional] Function responsible for triggering request retries, [default (*at the bottom of code-block*)](https://github.com/veliovgroup/request-extra#request-default-options);\n- `opts.rawBody` {*Boolean*} - Disable all data processing (`body` will be passed as *Buffer*, `headers` will be empty, use `.onHeader()` callback to get headers with `rawBody` option), great option for *piping*, default: `false`;\n- `opts.noStorage` {*Boolean*} - Disable all data processing and data concatenation (`headers` and `body` won't be passed to response), great option for *piping*, default: `false`;\n- `opts.wait` {*Boolean*} - Do not send request immediately and wait until `.send()` method is called, set this option to `true` to register `.onHeader()` and `.onBody()` callbacks, default: `false`;\n- `opts.proxy` {*String*} - Fully qualified URL to HTTP proxy, when this feature is enabled connections are going to start with `CONNECT` request, default: no proxy or system proxy is used;\n- `opts.rejectUnauthorized` {*Boolean*} - [Optional] Shall request be rejected if SSL/TLS certificate can't be validated? Default: `false`;\n- `opts.rejectUnauthorizedProxy` {*Boolean*} - [Optional] Shall request be rejected if SSL/TLS certificate of a __proxy host__ can't be validated? Default: `false`;\n- `opts.curlOptions` {*Object*} - [Optional] Explicitly set `libcurl` options, full list of options available [here](https://curl.haxx.se/libcurl/c/curl_easy_setopt.html) and [here](https://github.com/JCMais/node-libcurl/blob/32647acc28b026bbd03aa1e3abea9cbbc8f7d43b/lib/generated/CurlOption.ts#L3286);\n- `opts.curlFeatures` {*Object*} - [Optional] Explicitly __enable__ or __disable__ `libcurl` features. To __enable__ a feature pass `true` as a value, example: `{NoDataParsing: true}`. To __disable__ pass `false` as a value, example: `{NoDataParsing: false}`. Full list of available features is available [here](https://github.com/JCMais/node-libcurl/blob/249323f0f3883f8cbf6d0e91a89bfecb5862da53/lib/enum/CurlFeature.ts#L7).\n\n__Notes__:\n\n- When using `opts.rawBody` callback won't return `headers`, to get headers use `onHeader` callback;\n- When using `opts.noStorage` callback won't return `headers` and `body`, to get headers and body use `onData` and `onHeader` callbacks;\n- `opts.upload` and `opts.form` __can not be used together__, there won't be exception thrown, if both presented — `opts.form` will be used instead;\n- When using `opts.upload` or __any other request where server returns__ `expect: '100-continue'` HTTP header — callback won't return `headers`, to get headers use `onHeader` callback;\n- This package is build on top of [`libcurl`](https://curl.haxx.se/libcurl/) and [`node-libcurl`](https://github.com/JCMais/node-libcurl) it's the way more powerful than just sending requests via `http` and `https` protocol. Libcurl can work with IMAP/SMTP protocols getting/sending emails. Libcurl can serve as fully-featured FTP-client. Here's full list of supported protocols: `DICT`, `FILE`, `FTP`, `FTPS`, `Gopher`, `HTTP`, `HTTPS`, `IMAP`, `IMAPS`, `LDAP`, `LDAPS`, `POP3`, `POP3S`, `RTMP`, `RTSP`, `SCP`, `SFTP`, `SMTP`, `SMTPS`, `Telnet` and `TFTP`. To learn more on how to utilize all available power and features see docs of [`node-libcurl`](https://github.com/JCMais/node-libcurl#node-libcurl) and [`libcurl`](https://curl.haxx.se/libcurl/) itself.\n\n### Response\n\n- `resp.statusCode` {*Number*} - HTTP response/status code;\n- `resp.body` {*String*} - Body of HTTP response, not modified or processed, as it is — plain text;\n- `resp.headers` {*Object*} - HTTP response headers as plain *Object*, all headers names are lower-cased.\n\n### Error\n\n- `error.errorCode` {*Number*} - `libcurl` internal error code;\n- `error.code` {*Number*} - `libcurl` internal error code, same as `errorCode`;\n- `error.statusCode` {*Number*} - HTTP error code, if any;\n- `error.message` {*String*} - Human-readable error.\n\n### Returns `req` *Object*\n\n- `request()` and `requestAsync({ wait: true })` return `req` instance of {*LibCurlRequest*}\n- `requestAsync()` (*without wait*) and `req.sendAsync()` return {*Response*} upon resolve\n\n#### Using async API\n\n```js\nimport { requestAsync } from 'request-libcurl';\n\n// WITH { wait: true }\nconst req = await requestAsync({ url: 'https://example.com', wait: true });\ntry {\n  const { statusCode, body, headers } = await req.sendAsync();\n} catch (error) {\n  const { errorCode, code, statusCode, message } = error;\n}\n\n// GET RESPONSE RIGHT AWAY\ntry {\n  const resp = await requestAsync({ url: 'https://example.com' });\n  const { statusCode, body, headers } = resp;\n} catch (error) {\n  const { errorCode, code, statusCode, message } = error;\n}\n```\n\n- `req` {*LibCurlRequest*} - The *LibCurlRequest* instance returned only with `{ wait: true }` option, otherwise it returns *Response* (`resp` in the docs) right away\n- `req.abortAsync()` {*Promise*} - Abort current request, request will throw `499: Client Closed Request` HTTP error\n- `req.sendAsync()` {*Promise*} - Send request, use it with `{ wait: true }`. For example with `rawBody`/`noStorage`, when you need to delay sending request, for example to set event listeners and/or callbacks\n- `req.pipe(stream.Writable)` {*Function*} - Pipe response to a *WritableStream*, for example download a file to FS. Use with `{wait: true, retry: false}` options, and `.sendAsync()` method\n- `req.onData(callback)` {*Function*} - Hook, called right after data is received, called for each data-chunk. Useful with `.pipe()`, `rawBody`/`noStorage` and callbacks/events\n- `req.onHeader(callback)` {*Function*} - Hook, called right after header is received, called for each header. Useful with `.pipe()`, `rawBody`/`noStorage` and callbacks/events\n- `resp` {*Response*} - Successful response\n  - `error` {*undefined*};\n  - `resp.statusCode` {*Number*} - HTTP status code;\n  - `resp.body` {*String*} - Body of HTTP response, not modified or processed, as it is — plain text;\n  - `resp.headers` {*Object*} - Key-value plain *Object* with pairs of response headers;\n- `error` {*ResponseError*} - Failed response\n  - `error.errorCode` {*Number*} - `libcurl` internal error code;\n  - `error.code` {*Number*} - `libcurl` internal error code, same as `errorCode`;\n  - `error.statusCode` {*Number*} - HTTP error code, if any;\n  - `error.message` {*String*} - Human-readable error.\n\n#### Using callback API\n\n```js\nimport request from 'request-libcurl';\nconst req = request({ url: 'https://example.com' }, callback);\n```\n\n- `req.abort()` - Abort current request, request will return `499: Client Closed Request` HTTP error\n- `req.send()` - Send request, use it with `{ wait: true }`. For example with `rawBody`/`noStorage`, when you need to delay sending request, for example to set event listeners and/or callbacks\n- `callback(error, resp)` - Callback triggered on successful response\n  - `error` {*undefined*};\n  - `resp` {*Response*}\n- `callback(error)` - Callback triggered on failed request\n  - `error` {*ResponseError*};\n- The rest of methods match async API\n\n## Examples\n\nSend GET and POST requests, download and upload files — all just in few lines of code.\n\n### GET request\n\nBy default `request-libcurl` will take care of chunked responses and encoding:\n\n```js\nimport { requestAsync } from 'request-libcurl';\n\n// GET request:\nconst { body } = await requestAsync({ url: 'https://example.com' });\n```\n\nFor full control over request/response streams, chunks, and encoding use `{rawBody: true, wait: true, retry: false}` options with `.onData()` and `.onHeader()` callbacks:\n\n```js\nlet responseBody = Buffer.from('');\nlet responseHeaders = Buffer.from('');\nconst headersObj = {};\n\nconst req = await requestAsync({\n  url: 'https://example.com',\n  retry: false, // Do not retry with rawBody/noStorage, as it may mess up with headers and body inside `.onData()` and `.onHeader()` callbacks\n  rawBody: true,\n  wait: true // Using 'wait' option to set `.onData()` and `.onHeader()` callbacks\n});\n\nreq.onData((chunkAsBuffer) =\u003e {\n  // Do something with a body\n  // .pipe() for example\n  responseBody = Buffer.concat([responseBody, chunkAsBuffer]);\n});\n\nreq.onHeader((chunkAsBuffer) =\u003e {\n  responseHeaders = Buffer.concat([responseHeaders, chunkAsBuffer]);\n\n  // Or convert it to headers Object\n  // Headers received one-by-one, line-by-line\n  const header = chunkAsBuffer.toString('utf8');\n  if (header.includes(':')) {\n    const splitHeader = header.split(':');\n    headersObj[splitHeader[0].toLowerCase().trim()] = splitHeader[1].trim();\n  }\n});\n\nawait req.sendAsync();\n// Body as plain-string\nconst body = responseBody.toString('utf8');\n// All headers as plain multi-line string\nconst headers = responseHeaders.toString('utf8');\n// All headers as plain-Object\nconsole.log(headersObj);\n```\n\n### POST request\n\n```js\nimport request, { requestAsync } from 'request-libcurl';\nimport querystring from 'querystring';\n\n// POST (Content-Type: application/x-www-form-urlencoded):\n// by passing a String or formatted \"Query String\" to `form`\nconst resp = await requestAsync({\n  method: 'POST',\n  url: 'https://example.com',\n  form: querystring.stringify({ myForm: 'data' })\n};\n\n// POST with Authorization (Content-Type: application/x-www-form-urlencoded):\n// by passing a String or formatted \"Query String\" to `form`\nconst resp = await requestAsync({\n  method: 'POST',\n  url: 'https://example.com',\n  auth: 'username:passwd',\n  form: querystring.stringify({ myForm: 'data' })\n});\n\n// POST (Content-Type: application/json):\n// by passing plain Object to `form`\n// calling via callback API\nrequest({\n  method: 'POST',\n  url: 'https://example.com',\n  form: { myForm: 'data' }\n}, (error, resp) =\u003e {\n  /* ... */\n});\n```\n\n### POST request with extra options\n\n```js\nimport { requestAsync } from 'request-libcurl';\n\n// POST with Authorization (Content-Type: application/json):\n// by passing plain Object to `form`\nconst resp = await requestAsync({\n  method: 'POST',\n  url: 'https://example.com',\n  auth: 'username:passwd',\n  form: {\n    myJSON: 'data'\n  }\n});\n\n// Custom POST (Content-Type: text/plain):\n// by passing custom Headers\nconst resp = await requestAsync({\n  method: 'POST',\n  url: 'https://example.com',\n  form: 'Plain String or Base64 String or any other String',\n  headers: {\n    'Content-Type': 'text/plain'\n  }\n});\n```\n\n### File download\n\nDownload a file to the FileSystem by passing {*stream.Writable*}\n\n#### File download using `pipeTo` option\n\nDownload a file to the FileSystem by passing {*stream.Writable*} to `pipeTo` option:\n\n```js\nimport fs from 'fs';\nimport request from 'request-libcurl';\n\nconst req = request({\n  url: 'https://example.com/file.pdf',\n  pipeTo: fs.createWriteStream('/path/to/file.pdf', {flags: 'w'}),\n  retry: false // Do not retry when download!\n}, (error, resp) =\u003e {\n  if (error) {\n    throw error;\n  } else {\n    // File successfully downloaded\n    fs.stat('/path/to/file.pdf', (error, stats) =\u003e {\n      // do something with downloaded file\n    });\n  }\n});\n```\n\n#### File download via `.pipe()` method\n\nDownload a file to the FileSystem using `.pipe()` method:\n\n```js\nimport fs from 'node:fs/promises';\nimport { requestAsync } from 'request-libcurl';\n\nconst req = await requestAsync({\n  url: 'https://example.com/file.pdf',\n  wait: true,\n  retry: false // Do not retry when download!\n});\n\nreq.pipe(fs.createWriteStream('/path/to/file.pdf', { flags: 'w' }));\n// .pipe() method can be used to pass download to a multiple WritableStream(s):\n// req.pipe(fs.createWriteStream('/backup/downloads/file.pdf', {flags: 'w'}));\n\nawait req.sendAsync();\n// File successfully downloaded\n\nconst stats = await fs.stat('/path/to/file.pdf');\n// do something with downloaded file\n```\n\n### File upload\n\n```js\nimport fs from 'node:fs/promises';\nimport { requestAsync } from 'request-libcurl';\n\nawait requestAsync({\n  method: 'POST',\n  url: 'https://example.com/upload',\n  upload: await fs.open('/path/to/a/file', 'r'),\n  retry: false,\n});\n// File successfully uploaded\n```\n\n### File upload (`multipart/form-data`)\n\nIn this example we are going to use [`HTTPPOST` libcurl option](https://curl.haxx.se/libcurl/c/CURLOPT_HTTPPOST.html) passing `Object[]` (*array of Objects* representing files, note: multiple files can be passed in a single request) via `curlOptions`\n\n```js\nimport { requestAsync } from 'request-libcurl';\nconst fileLocation = '/full/absolute/path/to/a/file.ext';\n\nawait requestAsync({\n  method: 'POST', // Can be used with PUT\n  url: 'https://example.com/upload.php',\n  retry: false,\n  curlOptions: {\n    HTTPPOST: [{\n      name: 'file.ext', // File's name\n      file: fileLocation, // Full absolute path to a file on FS\n      type: 'application/ext' // File's mime-type\n    } /*, {...multiple files here...} */]\n  }\n});\n// File(s) successfully uploaded\n```\n\n## Known Issues\n\nGot an issue? Start with checking \"Known Issues\" section below.\n\n### 1. SSL connect error code: 35\n\nTo address most common issue with SSL certificates and speed up response time — SSL/TLS certificates validation is disabled in this package by default. But on edge cases this may return error-code `35` on SNI-enabled hosts. To solve this issue add `{ rejectUnauthorized: true }` to request object.\n\nTo change `rejectUnauthorized` option globally use:\n\n```js\nrequest.defaultOptions.rejectUnauthorized = true;\n```\n\n### 2. Compiled against Different Node.js version\n\nDue to single dependency on `node-libcurl` which shipped with statically built binaries, you may encounter `This module was compiled against a different Node.js version using NODE_MODULE_VERSION` error. This may happen on edge cases, like running the very latest release of node.js (*while bundled builds aren't shipped yet*), then you may want to build this package locally, use one of next commands:\n\n```shell\n# First: ensure node.js version is matching supported request-libcurl version\n# See \"Install\" section at the top of this document to check supported versions\n# package might require downgrade installing explicit version\n\n# Please see options below, in dependence from version of NPM and Node.js\n# one of this options should solve this issue\n\n# Option 1: Update and rebuild locally installed binaries\nnpm rebuild --update-binary --build-from-source\n\n# Option 2: Build library\nnpm install --save request-libcurl --build-from-source\n\n# Option 3: Build library and curl executables:\nnpm install --save request-libcurl --build-from-source --curl_static_build=true\n\n# In case if you encounter errors during building package locally:\n# 1. Execute same command as \"sudo\" (e.g. administrator), and try again\n# 2. Install globally node-gyp and node-pre-gyp NPM packages, and try again\n```\n\n### 3. Missing libraries\n\nSome of indirect dependencies of `libcurl` might be missing. Errors related to missing dependencies include `Library not loaded`, and `image not found` in error's description/output.\n\n#### zstd\n\nOne of `curl` dependency libraries is [`zstd`](https://github.com/facebook/zstd). See installation instruction below or [build from source](https://github.com/facebook/zstd#build-instructions)\n\n```shell\n# Error: Library not loaded: /usr/local/opt/zstd/lib/libzstd.1.dylib\n# Reason: image not found\n\n# Solution 1: macOS Install via brew\nbrew install zstd\n\n# Solution 2: Linux/Debian/Ubuntu Install via apt-get\napt-get update\napt-get install zstd\n\n# Solution 3: Linux/CentOS/RHEL Install via yum\nyum install zstd\n\n# Solution 4: Unix build from source\n# Up to date docs — https://github.com/facebook/zstd#build-instructions\n# Download latest release from here — https://github.com/facebook/zstd/releases\n# For example as of 2020-06-02 — zstd-1.4.8.tar.gz\ncurl https://github.com/facebook/zstd/releases/download/v1.4.8/zstd-1.4.8.tar.gz -O\nmake\nmake install # might require sudo/root permissions\n# Optionally test compiled binary with:\nmake check\n```\n\nFor more details and instructions for different platforms read `node-libcurl` [official docs](https://github.com/JCMais/node-libcurl#important-notes-on-prebuilt-binaries--direct-installation). __Note__: It's highly recommended to [run tests](https://github.com/veliovgroup/request-extra#running-tests) after building package locally.\n\n## Running Tests\n\n1. Clone this package\n2. In Terminal (*Console*) go to directory where package is cloned\n3. Then run:\n\n```shell\n# Install development NPM dependencies:\nnpm install --save-dev\n# Install NPM dependencies:\nnpm install --save\n# Run tests:\nPORT=3003 npm test\n# Run tests and output debugging details:\nDEBUG=true PORT=3003 npm test\n# PORT env.var is required! And can be changed to any open port!\n# Note: The Internet connection is required to perform tests\n# Note: Test-suite includes \"no response\" and \"timing out responses\"\n# if a test looks stuck — give it another minute before interrupting it\n```\n\n## Support our open source contribution\n\n- Upload and share files using [☄️ meteor-files.com](https://meteor-files.com/?ref=github-request-extra-repo-footer) — Continue interrupted file uploads without losing any progress. There is nothing that will stop Meteor from delivering your file to the desired destination\n- Use [▲ ostr.io](https://ostr.io?ref=github-request-extra-repo-footer) for [Server Monitoring](https://snmp-monitoring.com), [Web Analytics](https://ostr.io/info/web-analytics?ref=github-request-extra-repo-footer), [WebSec](https://domain-protection.info), [Web-CRON](https://web-cron.info) and [SEO Pre-rendering](https://prerendering.com) of a website\n- Star on [GitHub](https://github.com/veliovgroup/request-extra)\n- Star on [NPM](https://www.npmjs.com/package/request-libcurl)\n- [Sponsor maintainer via GitHub](https://github.com/sponsors/dr-dimitru) — support open source with one-time contribution or on a regular basis\n- [Sponsor veliovgroup via GitHub](https://github.com/sponsors/veliovgroup) — support company behind this package\n- [Support via PayPal](https://paypal.me/veliovgroup) — support our open source contributions\n","funding_links":["https://github.com/sponsors/dr-dimitru","https://paypal.me/veliovgroup","https://github.com/sponsors/veliovgroup"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveliovgroup%2Frequest-extra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fveliovgroup%2Frequest-extra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveliovgroup%2Frequest-extra/lists"}