Skip to content

andrewmackrodt/node-libcurl-ja3

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

node-libcurl-ja3

NPM version license

Disclaimer

This is a fork of node-libcurl using patches from lexiforest/curl-impersonate to impersonate the four major browsers: Chrome, Edge, Safari and Firefox. node-libcurl-ja3 is able to perform TLS and HTTP handshakes that are identical to that of a real browser.

Only the following platforms are supported:

  • Linux 64-bit (glibc based)
  • macOS Apple Silicon (M1+)

Prebuilt binaries are provided for Node.js 20 and 22. Any other version has not been tested and will need an environment capable of building the native module. Refer to Important Notes on Prebuilt Binaries / Direct Installation for a list of required system packages.

Although the library is named node-libcurl-ja3, it also supports http2 and ja4 impersonation (e.g. Akamai).

Table of Contents

Quick Start

  • This library cannot be used in a browser, it depends on native code.
  • There is no worker threads support at the moment. See #169

Install

npm i node-libcurl-ja3 --save

or

yarn add node-libcurl-ja3

Impersonate Usage

The following browser fingerprints are pre-configured:

  • Chrome 134
  • Edge 134
  • Firefox 136.0
  • Safari 18.3

To learn how to configure custom impersonation options, refer to the folder lib/impersonate/browser.

For brevity, this section covers a single example for creating impersonate instances using the curly API and Curl class. To use impersonation with the examples following this section, adapt them to use either:

  • impersonate in place of curly
  • Curl.impersonate in place of new Curl

Simple Impersonate Request - Async / Await using curly

const { Browser, impersonate } = require('node-libcurl-ja3');

const curly = impersonate(Browser.Chrome);
const { data } = await curly.get('https://tls.browserleaks.com/json');

console.log(data.ja3n_hash); // 8e19337e7524d2573be54efb2b0784c9

Simple Impersonate Request - Using Curl class

const { Browser, Curl } = require('node-libcurl-ja3');

const curl = Curl.impersonate(Browser.Chrome);

curl.setOpt('URL', 'tls.browserleaks.com/json');
curl.setOpt('FOLLOWLOCATION', true);

curl.on('end', function (statusCode, data, headers) {
  console.info(statusCode);
  console.info('---');
  console.info(data.length);
  console.info('---');
  console.info(this.getInfo('TOTAL_TIME'));

  this.close();
});

curl.on('error', curl.close.bind(curl));
curl.perform();

Simple Request - Async / Await using curly

this API is experimental and is subject to changes without a major version bump

const { curly } = require('node-libcurl-ja3');

const { statusCode, data, headers } = await curly.get('http://www.google.com')

Any option can be passed using their FULLNAME or a lowerPascalCase format:

const querystring = require('querystring');
const { curly } = require('node-libcurl-ja3');

const { statusCode, data, headers } = await curly.post('http://httpbin.com/post', {
  postFields: querystring.stringify({
    field: 'value',
  }),
  // can use `postFields` or `POSTFIELDS`
})

JSON POST example:

const { curly } = require('node-libcurl-ja3')
const { data } = await curly.post('http://httpbin.com/post', {
  postFields: JSON.stringify({ field: 'value' }),
  httpHeader: [
    'Content-Type: application/json',
    'Accept: application/json'
  ],
})

console.log(data)

Simple Request - Using Curl class

const { Curl } = require('node-libcurl-ja3');

const curl = new Curl();

curl.setOpt('URL', 'www.google.com');
curl.setOpt('FOLLOWLOCATION', true);

curl.on('end', function (statusCode, data, headers) {
  console.info(statusCode);
  console.info('---');
  console.info(data.length);
  console.info('---');
  console.info(this.getInfo( 'TOTAL_TIME'));
  
  this.close();
});

curl.on('error', curl.close.bind(curl));
curl.perform();

Setting HTTP headers

Pass an array of strings specifying headers

curl.setOpt(Curl.option.HTTPHEADER,
  ['Content-Type: application/x-amz-json-1.1'])

Form Submission (Content-Type: application/x-www-form-urlencoded)

const querystring = require('querystring');
const { Curl } = require('node-libcurl-ja3');

const curl = new Curl();
const close = curl.close.bind(curl);

curl.setOpt(Curl.option.URL, '127.0.0.1/upload');
curl.setOpt(Curl.option.POST, true)
curl.setOpt(Curl.option.POSTFIELDS, querystring.stringify({
  field: 'value',
}));

curl.on('end', close);
curl.on('error', close);

MultiPart Upload / HttpPost libcurl Option (Content-Type: multipart/form-data)

const { Curl } = require('node-libcurl-ja3');

const curl = new Curl();
const close = curl.close.bind(curl);

curl.setOpt(Curl.option.URL, '127.0.0.1/upload.php');
curl.setOpt(Curl.option.HTTPPOST, [
    { name: 'input-name', file: '/file/path', type: 'text/html' },
    { name: 'input-name2', contents: 'field-contents' }
]);

curl.on('end', close);
curl.on('error', close);

Binary Data

When requesting binary data make sure to do one of these:

curl.setOpt('WRITEFUNCTION', (buffer, size, nmemb) => {
  // something
})
  • Enable one of the following flags:
curl.enable(CurlFeature.NoDataParsing)
// or
curl.enable(CurlFeature.Raw)

The reasoning behind this is that by default, the Curl instance will try to decode the received data and headers to utf8 strings, as can be seen here: https://github.com/JCMais/node-libcurl/blob/b55b13529c9d11fdcdd7959137d8030b39427800/lib/Curl.ts#L391

For more examples check the examples folder.

API

This library provides Typescript type definitions.

Almost all CURL options are supported, if you pass one that is not, an error will be thrown.

For more usage examples check the examples folder.

Special Notes

READFUNCTION option

The buffer passed as first parameter to the callback set with the READFUNCTION option is initialized with the size libcurl is using in their upload buffer (which can be set with UPLOAD_BUFFERSIZE), this is initialized using node::Buffer::Data(buf); which is basically the same than Buffer#allocUnsafe and therefore, it has all the implications as to its correct usage: https://nodejs.org/pt-br/docs/guides/buffer-constructor-deprecation/#regarding-buffer-allocunsafe

So, be careful, make sure to return exactly the amount of data you have written to the buffer on this callback. Only that specific amount is going to be copied and handed over to libcurl.

Common Issues

See COMMON_ISSUES.md

Benchmarks

See ./benchmark

Detailed Installation

The latest version of this package has prebuilt binaries (thanks to node-pre-gyp) available for:

And on the following platforms:

  • Linux 64-bit (glibc based)
  • macOS Apple Silicon (M1+)

Installing with yarn add node-libcurl-ja3 or npm install node-libcurl-ja3 should download a prebuilt binary and no compilation will be needed.

The prebuilt binary is statically built with the following library versions, features and protocols:

Versions: libcurl/8.7.0-DEV BoringSSL zlib/1.3 brotli/1.1.0 zstd/1.5.6 nghttp2/1.63.0
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS brotli HSTS HTTP2 HTTPS-proxy IPv6 Largefile libz NTLM SSL threadsafe UnixSockets zstd

If there is no prebuilt binary available that matches your system, or if the installation fails, then you will need an environment capable of compiling Node.js addons, which means:

  • python 3.x installed
  • updated C++ compiler able to compile C++17.

If you don't want to use the prebuilt binary even if it works on your system, you can pass a flag when installing:

With npm:

npm install node-libcurl-ja3 --build-from-source

With yarn:

npm_config_build_from_source=true yarn add node-libcurl-ja3

Important Notes on Prebuilt Binaries / Direct Installation

The built binaries are statically linked with BoringSSL, brotli, nghttp2, zlib and zstd.

Missing Packages

The built binaries do not have support for GSASL, GSS-API, HTTP3, IDN, LDAP, LDAPS, PSL, RTMP, SPNEGO, SSH, SSPI or TLS-SRP.

Building on Linux

If you are on a debian based system, install the required dependencies by running:

sudo apt install -qqy autoconf automake build-essential cmake curl libtool ninja-build pkg-config

Users for other distributions will need to find the equivalent packages and install via your package manager.

Building on macOS

On macOS you must have:

  • macOS >= 11.6 (Big Sur)
  • Xcode Command Line Tools
  • Homebrew
  • Bash >= 5.0 (unconfirmed)

You can check if you have Xcode Command Line Tools be running:

xcode-select -p

It should return their path, in case it returns nothing, you must install it by running:

xcode-select --install

Finally, install the remaining packages using homebrew:

brew install automake bash cmake libtool make meson ninja

Contributing

Read CONTRIBUTING.md

Acknowledgments

About

libcurl-impersonate bindings for Node.js

Resources

License

Stars

Watchers

Forks

Languages

  • TypeScript 75.1%
  • C++ 19.3%
  • JavaScript 4.2%
  • Shell 0.9%
  • Python 0.3%
  • C 0.1%
  • HTML 0.1%