Libcurl.js
A port of libcurl to WebAssembly, for proxying HTTPS requests from the browser with full TLS encryption
Install / Use
/learn @ading2210/Libcurl.jsREADME
libcurl.js
This is a port of libcurl to WebAssembly for use in the browser. It provides an interface compatible with the Fetch API, allowing you to proxy HTTPS requests from the browser with full TLS encryption. Unlike a traditional CORS proxy, the proxy server cannot read the contents of your requests.
Table of Contents:
- Features
- Building
- Javascript API
- Importing the Library
- Making HTTP Requests
- Creating New HTTP Sessions
- Creating WebSocket Connections
- Using TLS Sockets
- Changing the Network Transport
- Changing the Websocket Proxy URL
- Getting Libcurl's Output
- Changing the HTTP Version:
- Getting Error Strings
- Getting Version Info
- Getting the CA Certificates Bundle
- Using the Wisp Client
- Proxy Server
- Project Structure
- Copyright
<small>Table of contents generated with markdown-toc.</small>
Features:
- Fetch compatible API
- End to end encryption between the browser and the destination server
- Support for up to TLS 1.3
- Support for proxying HTTP/2 connections and WebSockets
- Bypass CORS restrictions without compromising on privacy
- Low latency via multiplexing and reusing open connections
- Use raw TLS sockets in the browser
- Custom network transport support
- Works inside web workers without needing special permissions or headers
- Works in all major browsers (Chromium >= 64, Firefox >= 65, Safari >= 14)
- Has the ability to create multiple independent sessions
- Small footprint size (552KB after compression) and low runtime memory usage
- Support for Brotli and gzip compressed responses
Building:
You can build this project by running the following commands:
git clone https://github.com/ading2210/libcurl.js --recursive
cd libcurl.js/client
./build.sh
Make sure you have emscripten, git, and the various C build tools installed. The only OS supported for building libcurl.js is Linux. On Debian-based systems, you can run the following command to install all the dependencies:
sudo apt install python3 make cmake emscripten autoconf automake libtool pkg-config wget xxd jq
Emscripten versions 3.1.6 and 3.1.72 have been tested and known to work. If you are using Debian 12 or Ubuntu 24.04, Emscripten 3.1.6 is what is provided in the distro's repository.
The build script will generate client/out/libcurl.js as well as client/out/libcurl.mjs, which is an ES6 module. You can supply the following arguments to the build script to control the build:
release- Use all optimizations.single_file- Include the WASM binary in the outputted JS using base64.asan- Use the Clang AddressSanitizer to catch possible memory bugs during runtime.all- Build twice, once normally, and once as a single file. This enables the release mode.
Note: non-release builds will have the -dev version suffix and ASan builds will have the -asan suffix.
Javascript API:
Importing the Library:
To import the library, follow the build instructions in the previous section, and copy client/out/libcurl.js and client/out/libcurl.wasm to a directory of your choice. After the script is loaded, call libcurl.load_wasm, specifying the url of the libcurl.wasm file. You do not need to call libcurl.load_wasm if you use the libcurl_full.js file, as the WASM will be bundled into the JS file.
<script defer src="./out/libcurl.js" onload="libcurl.load_wasm('/out/libcurl.wasm');"></script>
You may also call and await libcurl.load_wasm in your own async code. It returns a promise which resolves once libcurl.js is fully ready. If the WASM is already loaded, the function will return immediately. If the WASM loading fails, the promise will be rejected with an error message.
async function main() {
await libcurl.load_wasm("/out/libcurl.wasm");
console.log(await libcurl.fetch("https://ading.dev/"));
}
main();
If you are using the single file version (libcurl_full.js), the libcurl.load_wasm function can still be used to wait for the WASM to load, although the url provided to it has no effect.
Alternatively, prebuilt versions can be found on NPM and jsDelivr. You can use the following URLs to load libcurl.js from a third party CDN.
https://cdn.jsdelivr.net/npm/libcurl.js@0.7.1/libcurl.js
https://cdn.jsdelivr.net/npm/libcurl.js@0.7.1/libcurl.wasm
To know when libcurl.js has finished loading, you can use the libcurl_load DOM event. The libcurl_abort event will trigger if the Emscripten runtime gets aborted due to a critical error. The libcurl.events object contains an EventTarget where these events will also be emitted.
document.addEventListener("libcurl_load", ()=>{
libcurl.set_websocket(`wss://${location.hostname}/ws/`);
console.log("libcurl.js ready!");
});
You may also use the libcurl.onload callback, which can be useful for running libcurl.js inside a web worker.
libcurl.onload = () => {
console.log("libcurl.js ready!");
}
Once loaded, there will be a window.libcurl object which includes all the API functions. The libcurl.ready property can also be used to know if the WASM has loaded.
There are also ES6 modules available if you are using a bundler. The libcurl.mjs and libcurl_full.mjs files provide this functionality, with the former being set as the entry point for the NPM package.
//import the regular version
import { libcurl } from "libcurl.js";
//import the version with the wasm included in the js
import { libcurl } from "libcurl.js/bundled";
Examples of running libcurl.js on the main thread and in a web worker are available at client/index.html and client/worker.html respectively.
Making HTTP Requests:
To perform HTTP requests, use libcurl.fetch, which takes the same arguments as the browser's regular fetch function. Like the standard Fetch API, libcurl.fetch will also return a Response object.
let r = await libcurl.fetch("https://ading.dev");
console.log(await r.text());
Most of the standard Fetch API's features are supported, with the exception of:
- CORS enforcement
- Caching
Sending cookies is supported, but they will not be automatically sent unless you create a new HTTP session, which is covered in the next section.
The response may contain multiple HTTP headers with the same name, which the Headers object isn't able to properly represent. If this matters to you, use response.raw_headers, which is an array of key value pairs, instead of response.headers. There is support for streaming the response body using a ReadableStream, as well as canceling requests using an AbortSignal. All requests made using this method share the same connection pool, which has a limit of 50 active TCP connections (configurable if you use a separate HTTP session).
The proxy option may be used to specify the URL of a socks5h, socks4a, or http proxy server. For example proxy: "socks5h://127.0.0.0:1080" will set the proxy server for just the current request. This proxy option is separate from the global websocket proxy.
Creating New HTTP Sessions:
To create new sessions for HTTP requests, use the libcurl.HTTPSession class. The constructor for this class takes the following arguments:
options- An optional object with various settings.
The valid HTTP session settings are:
enable_cookies- A boolean which indicate whether or not cookies should be persisted within the session.cookie_jar- A string containing the data in the cookie jar file. This should have been exported from a previous session. For more information on the format for this file, see the curl documentation.proxy- A URL for asocks5h,socks4a, orhttpproxy server.
Each HTTP session has the following methods available:
fetch- Identical to thelibcurl.fetchfunction but only creates connections in this session.set_connections- Set the connection limits. This takes three arguments: the first is the hard limit of active connections (default 60), the second is limit for the connection cache (default 50), and the third is the max connections per host (default 6).export_cookies- Export any cookies which were recorded in the session. This will return an empty string if cookies are disabled or no cookies have been set yet.close- Close all connections and clean up the session. You must call this after you are done using the session, otherwise it will leak memory.
The following attributes are supported:
base_url- Set the base URL used to resolve relative URLs. If this is not defined then an error will be thrown when attempting to fetch a relative URL.
let session = new libcurl.HTTPSession();
session.base_url = "https://ading.dev";
let r = await session.fetch("/projects/");
console.log(await r.text());
session.
