diff --git a/CHANGELOG.md b/CHANGELOG.md index 4779c99..4b3eca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Libcurl.js Changelog: +## v0.7.0 (1/24/25): +- Switch to Mbed TLS for the TLS backend + - This fixes some TLS certificate errors that were occurring on some sites with WolfSSL +- Fix a null pointer dereference bug with websockets +- Store the CA certificates bundle more efficiently +- The WASM size has decreased from 1.4MB to 1.2MB + ## v0.6.22 (1/10/25): - Remove Emscripten's maximum socket limit of 64 - Fix handling of sub-protocols in Websockets diff --git a/website/favicon.ico b/website/favicon.ico new file mode 100644 index 0000000..a4e921b Binary files /dev/null and b/website/favicon.ico differ diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..baa6eb2 --- /dev/null +++ b/website/index.html @@ -0,0 +1,88 @@ + + + + + + libcurl.js - A port of libcurl to WASM for the web. + + + + + + + +
+

libcurl.js

+
+

libcurl.js is a port of the popular libcurl C library to WASM for use on the web. It allows you send encrypted HTTPS requests from the browser, without the proxy server being able to read the contents.

+

See the Github repository for more information.

+ + +

Features:

+ + +

Live Demo:

+
+ + + +
+


Waiting for libcurl.js to load...

+ + + + + + + + + + + + + + + +
Request Content:Network Traffic:
+

+        
+
+
Libcurl Output:
+

+        
+ +

Explanation:

+

libcurl.js works by proxying TCP sockets, rather than HTTP requests. The Wisp protocol is used to multiplex the TCP connections over a single websocket for minimal latency. The libcurl C library is used as an HTTP client, and Mbed TLS is used to facilitate encryption.

+

If the destination server supports HTTPS, the connection will be end-to-end encrypted using TLS. Only the encrypted TLS traffic is carried over the websocket, so the proxy server knows nothing about your requests except for the hostname.

+

This avoids the privacy issues of a traditional CORS proxy which can steal your credentials and private information. With libcurl.js and Wisp, you don't need to trust the proxy server to keep your requests secure.

+ +

Javascript API:

+

Including libcurl.js in your web pages is as simple as loading the JS bundle using a script tag.

+
<script src="https://cdn.jsdelivr.net/npm/libcurl.js@latest/libcurl_full.js" defer></script>
+

Then, after setting the Websocket proxy URL, you can use the API to its full potential.

+
document.addEventListener("libcurl_load", ()=>{
+  libcurl.set_websocket(`wss://wisp.mercurywork.shop/`);
+  console.log("libcurl.js ready!");
+});
+

More documentation and examples are available on the project README.

+ +

License:

+

This project was written by ading2210 and it is licensed under the GNU LGPL v3.

+
+ This license is mainly applied to libraries. You may copy, distribute and modify the software provided that modifications are described and licensed for free under LGPL. Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL, but applications that use the library don't have to be. +

+ - From tldrlegal.com +
+ + \ No newline at end of file diff --git a/website/main.css b/website/main.css new file mode 100644 index 0000000..5e4144d --- /dev/null +++ b/website/main.css @@ -0,0 +1,112 @@ +:root { + --background-1: #0C0C0D; + --background-2: #18181A; + --background-3: #232327; + --button-color: #38383D; + --border-1: #3a344e; + --text-color: #cccccc; + --link-color: #3584E4; + --link-color-alt: #a64ede; +} + +* { + font-family: sans-serif; + color: var(--text-color); + border-color: var(--text-color); + box-sizing: border-box; +} + +a { + color: var(--link-color); +} +a:hover { + color: var(--link-color-alt); +} + +html { + background-color: var(--background-1); + height: 100%; + padding-left: 16px; + padding-right: 16px; +} + +body { + background-color: var(--background-2); + max-width: 800px; + min-height: 90%; + margin-top: 16px; + margin-left: auto; + margin-right: auto; + padding: 16px; +} + +h1, h2, h3 { + margin-bottom: 0px; +} + +pre, code { + font-family: monospace; + background-color: var(--background-1); + padding: 8px; + overflow-x: auto; +} + +table { + border-collapse: collapse; + table-layout: fixed; + width: 100%; +} +table, th, td { + border: 1px solid; + vertical-align: top; +} + +.center { + width: 100%; + text-align: center; +} +.button { + background-color: var(--button-color); +} + +#title { + margin-top: 0px; +} +#request_form { + display: flex; + gap: 4px; + width: 100%; + margin-bottom: 8px; + margin-top: 8px; +} +#url { + width: 100%; + background-color: var(--button-color); +} +#main_table { + visibility: hidden; + margin-bottom: 20px; +} +#network_data { + width: 100%; +} +#status_text { + height: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + + +.list_cell { + height: 200px; + overflow-y: auto; + background-color: var(--background-3); +} +.list_cell pre { + height: 100%; + white-space: pre-wrap; + line-break: anywhere; + margin: 0px; + padding: 4px; + background-color: var(--background-3); +} \ No newline at end of file diff --git a/website/main.js b/website/main.js new file mode 100644 index 0000000..9c5d28d --- /dev/null +++ b/website/main.js @@ -0,0 +1,91 @@ +const decoder = new TextDecoder(); + +function from_id(id) { + return document.getElementById(id); +} + +async function start_request() { + let request_output = from_id("request_info"); + let libcurl_output = from_id("libcurl_output"); + let network_data = from_id("network_data"); + let url_box = from_id("url"); + + request_output.innerText = ""; + libcurl_output.innerText = ""; + network_data.innerText = ""; + + let url = url_box.value; + if (!url.startsWith("http://") && !url.startsWith("https://")) + url = "https://" + url; + request_output.innerText += `GET ${url}\n\n`; + + try { + let r = await libcurl.fetch(url, {_libcurl_verbose: 1}); + request_output.innerText += await r.text(); + } + catch (e) { + request_output.innerText += e; + } +} + +function handle_output(text) { + let output_element = from_id("libcurl_output"); + output_element.innerText += text + "\n"; + output_element.scrollTop = output_element.scrollHeight; +} + +function handle_ws_traffic(data, direction) { + let network_data = from_id("network_data"); + let table_row = document.createElement("tr"); + let direction_cell = document.createElement("td"); + let data_cell = document.createElement("td"); + let data_pre = document.createElement("pre"); + + if (direction === "send") + direction_cell.style.backgroundColor = "rgba(255, 0, 0, 0.3)"; + else if (direction === "recv") + direction_cell.style.backgroundColor = "rgba(0, 255, 0, 0.3)"; + direction_cell.style.fontFamily = "monospace"; + direction_cell.style.width = "36px"; + data_pre.style.fontSize = "10px"; + network_data.parentNode.style.display = "block"; + network_data.parentNode.style.marginBottom = "-2px"; + + data_cell.append(data_pre); + table_row.append(direction_cell, data_cell); + + direction_cell.innerText = direction; + data_pre.innerText = String.fromCodePoint(...new Uint8Array(data)); + network_data.append(table_row); +} + +function proxy_websocket() { + WebSocket.prototype.send = new Proxy(WebSocket.prototype.send, { + apply(a, b, c) { + handle_ws_traffic(c[0], "send"); + Reflect.apply(a, b, c); + }} + ); + WebSocket = new Proxy(WebSocket, { + construct(target, args) { + let ws = new target(...args); + ws.addEventListener("message", (event) => { + handle_ws_traffic(event.data, "recv"); + }); + return ws; + }} + ); +} + +document.addEventListener("libcurl_load", () => { + console.log(`loaded libcurl.js ${libcurl.version.lib}`); + + libcurl.set_websocket("wss://wisp.mercurywork.shop/"); + libcurl.stdout = handle_output; + libcurl.stderr = handle_output; + proxy_websocket(); + + from_id("status_text").style.display = "none"; + from_id("main_table").style.visibility = "visible"; + start_request(); +}); \ No newline at end of file