fix tls socket, add documentation

This commit is contained in:
ading2210 2024-03-07 12:16:28 -05:00
parent 155d5ea5b6
commit 53a474430a
4 changed files with 125 additions and 46 deletions

View file

@ -51,7 +51,7 @@ document.addEventListener("libcurl_load", ()=>{
});
```
Alternatively, the `libcurl.onload` callback can be used.
You may also use the, the `libcurl.onload` callback, which can be useful for running libcurl.js inside a web worker.
```js
libcurl.onload = () => {
console.log("libcurl.js ready!");
@ -76,7 +76,35 @@ Most of the standard Fetch API's features are supported, with the exception of:
Note that there is a hard limit of 50 active TCP connections due to emscripten limitations.
### Creating WebSocket Connections:
To use WebSockets, create a `libcurl.WebSocket` object, which works identically to the regular [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object.
To use WebSockets, create a `libcurl.CurlWebSocket` object, which takes the following arguments:
- `url` - The Websocket URL.
- `protocols` - A optional list of websocket subprotocols, as an array of strings.
- `options` - An optional object with extra settings to pass to curl.
The valid WebSocket options are:
- `headers` - HTTP request headers for the websocket handshake.
- `verbose` - A boolean flag that toggles the verbose libcurl output. This verbose output will be passed to the function defined in `libcurl.stderr`, which is `console.warn` by default.
The following callbacks are available:
- `CurlWebSocket.onopen` - Called when the websocket is successfully connected.
- `CurlWebSocket.message` - Called when a websocket message is received from the server. The data is passed to the first argument of the function, and it will be either a `Uint8Array` or a string, depending on the type of message.
- `CurlWebSocket.onclose` - Called when the websocket is cleanly closed with no error.
- `CurlWebSocket.onerror` - Called when the websocket encounters an unexpected error. The [error code](https://curl.se/libcurl/c/libcurl-errors.html) is passed to the first argument of the function.
The `CurlWebSocket.send` function can be used to send data to the websocket. The only argument is the data that is to be sent, which must be either a string or a `Uint8Array`.
```js
let ws = new libcurl.CurlWebSocket("wss://echo.websocket.org", [], {verbose: 1});
ws.onopen = () => {
console.log("ws connected!");
ws.send("hello".repeat(100));
};
ws.onmessage = (data) => {
console.log(data);
};
```
You can also use the `libcurl.WebSocket` object, which works identically to the regular [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object. It uses the same arguments as the simpler `CurlWebSocket` API.
```js
let ws = new libcurl.WebSocket("wss://echo.websocket.org");
ws.addEventListener("open", () => {
@ -88,7 +116,32 @@ ws.addEventListener("message", (event) => {
});
```
### Changing the Websocket URL:
### Using TLS Sockets:
Raw TLS sockets can be created with the `libcurl.TLSSocket` class, which takes the following arguments:
- `host` - The hostname to connect to.
- `port` - The TCP port to connect to.
- `options` - An optional object with extra settings to pass to curl.
The valid TLS socket options are:
- `verbose` - A boolean flag that toggles the verbose libcurl output.
The callbacks work similarly to the `libcurl.CurlWebSocket` object, with the main difference being that the `onmessage` callback always returns a `Uint8Array`.
The `TLSSocket.send` function can be used to send data to the socket. The only argument is the data that is to be sent, which must be a `Uint8Array`.
```js
let socket = new libcurl.TLSSocket("ading.dev", 443, {verbose: 1});
socket.onopen = () => {
console.log("socket connected!");
let str = "GET /all HTTP/1.1\r\nHost: ading.dev\r\nConnection: close\r\n\r\n";
socket.send(new TextEncoder().encode(str));
};
socket.onmessage = (data) => {
console.log(new TextDecoder().decode(data));
};
```
### Changing the Websocket Proxy URL:
You can change the URL of the websocket proxy by using `libcurl.set_websocket`.
```js
libcurl.set_websocket("ws://localhost:6001/");
@ -96,7 +149,7 @@ libcurl.set_websocket("ws://localhost:6001/");
If the websocket proxy URL is not set and one of the other API functions is called, an error will be thrown. Note that this URL must end with a trailing slash.
### Getting Libcurl's Output:
If you want more information about a connection, you can pass the `_libcurl_verbose` argument to the `libcurl.fetch` function.
If you want more information about a connection, you can pass the `_libcurl_verbose` argument to the `libcurl.fetch` function. These are the same messages that you would see if you ran `curl -v` on the command line.
```js
await libcurl.fetch("https://example.com", {_libcurl_verbose: 1});
```

View file

@ -193,12 +193,9 @@ async function libcurl_fetch(url, params={}) {
function set_websocket_url(url) {
websocket_url = url;
if (!Module.websocket && ENVIRONMENT_IS_WEB) {
document.addEventListener("libcurl_load", () => {
set_websocket_url(url);
});
if (Module.websocket) {
Module.websocket.url = url;
}
else Module.websocket.url = url;
}
function get_version() {

View file

@ -1,11 +1,21 @@
//currently broken
class TLSSocket {
constructor(hostname, port, debug) {
constructor(hostname, port, options={}) {
check_loaded(true);
this.hostname = hostname;
this.port = port;
this.url = `https://${hostname}:${port}`;
this.debug = debug;
this.options = options;
this.onopen = () => {};
this.onerror = () => {};
this.onmessage = () => {};
this.onclose = () => {};
this.connected = false;
this.event_loop = null;
this.connect();
}
@ -13,48 +23,56 @@ class TLSSocket {
connect() {
let data_callback = () => {};
let finish_callback = (error, response_info) => {
this.status = this.OPEN;
if (error === 0) {
this.open_callback();
this.recv_loop();
this.connected = true;
this.event_loop = setInterval(() => {
let data = this.recv();
if (data != null) this.onmessage(data);
}, 0);
this.onopen();
}
else {
this.cleanup(error);
}
}
let options = {
let request_options = {
_connect_only: 1,
}
if (this.debug) options._libcurl_verbose = 1;
if (this.options.verbose) {
request_options._libcurl_verbose = 1;
}
this.http_handle = perform_request(this.url, options, data_callback, finish_callback, null);
this.http_handle = perform_request(this.url, request_options, data_callback, finish_callback, null);
}
custom_recv() {
recv() {
let buffer_size = 64*1024;
let result_ptr = _recv_from_socket(this.http_handle, buffer_size);
let data_ptr = _get_result_buffer(result_ptr);
let result_code = _get_result_code(result_ptr);
let result_closed = _get_result_closed(result_ptr);
if (result_code == 0) { //CURLE_OK - data received
if (_get_result_closed(result_ptr)) {
this.close_callback();
return;
}
if (result_code === 0 && !result_closed) { //CURLE_OK - data received
let data_size = _get_result_size(result_ptr);
let data_heap = Module.HEAPU8.subarray(data_ptr, data_ptr + data_size);
let data = new Uint8Array(data_heap);
this.onmessage(data)
}
else if (result_code === 0 && result_closed) {
this.cleanup();
}
let message_event = new MessageEvent("message", {data: data});
this.dispatchEvent(message_event);
else if (result_code != 81) {
this.cleanup(result_code);
}
_free(data_ptr);
_free(result_ptr);
}
custom_send(data_array) {
send(data_array) {
if (!this.connected) return;
let data_ptr = allocate_array(data_array);
let data_len = data_array.length;
_send_to_socket(this.http_handle, data_ptr, data_len);
@ -63,12 +81,20 @@ class TLSSocket {
cleanup(error=false) {
if (this.http_handle) _cleanup_handle(this.http_handle);
else return;
clearInterval(this.event_loop);
this.close_callback(error);
this.connected = false;
if (error) {
this.onerror(error);
}
else {
this.onclose();
}
}
custom_close() {
close() {
this.cleanup();
this.status = this.CLOSED;
}
}

View file

@ -33,7 +33,6 @@ class CurlWebSocket {
this.onopen();
}
else {
this.status = this.CLOSED;
this.cleanup(error);
}
}
@ -54,16 +53,14 @@ class CurlWebSocket {
let result_ptr = _recv_from_websocket(this.http_handle, buffer_size);
let data_ptr = _get_result_buffer(result_ptr);
let result_code = _get_result_code(result_ptr);
let result_closed = _get_result_closed(result_ptr);
let returned_data = null;
function free_result() {
_free(data_ptr);
_free(result_ptr);
}
if (result_code === 0) { //CURLE_OK - data received
//CURLE_OK - data received
if (result_code === 0 && !result_closed) {
if (_get_result_closed(result_ptr)) {
free_result();
_free(data_ptr);
_free(result_ptr);
this.cleanup();
return returned_data;
}
@ -85,18 +82,26 @@ class CurlWebSocket {
}
}
}
//CURLE_GOT_NOTHING, CURLE_RECV_ERROR, CURLE_SEND_ERROR - socket closed
else if (result_code === 52 || result_code === 55 || result_code === 56) {
// websocket was cleanly closed by the server
else if (result_code === 0 && result_closed) {
this.cleanup();
}
//code is not CURLE_AGAIN - an error must have occurred
else if (result_code !== 81) {
this.cleanup(result_code);
}
free_result();
_free(data_ptr);
_free(result_ptr);
return returned_data;
}
cleanup(error=false) {
cleanup(error=0) {
if (this.http_handle) _cleanup_handle(this.http_handle);
else return;
clearInterval(this.event_loop);
this.connected = false;
@ -110,9 +115,7 @@ class CurlWebSocket {
send(data) {
let is_text = typeof data === "string";
if (!this.connected) {
throw new DOMException("websocket not connected");
}
if (!this.connected) return;
if (is_text) {
data = new TextEncoder().encode(data);