Merge pull request #3 from ading2210/v0.4

merge v0.4
This commit is contained in:
ading2210 2024-03-07 09:34:52 -08:00 committed by GitHub
commit 327fd60161
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 528 additions and 224 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!");
@ -73,8 +73,38 @@ Most of the standard Fetch API's features are supported, with the exception of:
- Sending credentials/cookies automatically
- Caching
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", () => {
@ -86,7 +116,38 @@ 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 Network Transport:
You can change the underlying network transport by setting `libcurl.transport`. The following values are accepted:
- `"wisp"` - Use the [Wisp protocol](https://github.com/MercuryWorkshop/wisp-protocol).
- `"wsproxy"` - Use the wsproxy protocol, where a new websocket is created for each TCP connection.
- Any custom class - Use a custom network protocol. If you pass in custom code here, it must be roughly conformant with the standard `WebSocket` API. The URL that is passed into this fake websocket always looks like `"wss://example.com/ws/ading.dev:443"`, where `wss://example.com/ws/` is the proxy server URL, and `ading.dev:443` is the destination server.
### 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/");
@ -94,18 +155,26 @@ 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});
```
By default this will print the output to the browser console, but you can set `libcurl.stdout` and `libcurl.stderr` to intercept these messages. This callback will be executed on every line of text that libcurl outputs.
```js
libcurl.stderr = (text) => {document.body.innerHTML += text};
```
Libcurl.js will also output some error messages to the browser console. You can intercept these messages by setting the `libcurl.logger` callback, which takes two arguments:
- `type` - The type of message. This will be one of the following: `"log"`, `"warn"`, `"error"`
- `text` - The text that is to be logged.
### Getting Version Info:
You can get version information from the `libcurl.version` object. This object will also contain the versions of all the C libraries that libcurl.js uses. `libcurl.version.lib` returns the version of libcurl.js itself.
### Getting the CA Certificates Bundle:
You can get the CA cert bundle that libcurl uses by calling `libcurl.get_cacert()`. The function will return a string with the certificates in PEM format. The cert bundle comes from the [official curl website](https://curl.se/docs/caextract.html), which is extracted from the Mozilla Firefox source code.
## Proxy Server:
The proxy server consists of a standard [Wisp](https://github.com/MercuryWorkshop/wisp-protocol) server, allowing multiple TCP connections to share the same websocket.

View file

@ -80,11 +80,15 @@ VERSION=$(cat package.json | jq -r '.version')
sed -i "s/__library_version__/$VERSION/" $OUT_FILE
#add extra libraries
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/copyright.js" $OUT_FILE
sed -i "/__extra_libraries__/r $WISP_CLIENT/polyfill.js" $OUT_FILE
sed -i "/__extra_libraries__/r $WISP_CLIENT/wisp.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/messages.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/tls_socket.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/websocket.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/copyright.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/ws_polyfill.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/util.js" $OUT_FILE
sed -i "/__extra_libraries__/r $JAVSCRIPT_DIR/logger.js" $OUT_FILE
#apply patches
python3 tools/patch_js.py $FRAGMENTS_DIR $OUT_FILE

View file

@ -4,11 +4,13 @@ tick_request
active_requests
get_version
get_cacert
get_error_str
recv_from_websocket
send_to_websocket
close_websocket
cleanup_websocket
cleanup_handle
get_result_size
get_result_buffer
get_result_code
@ -16,4 +18,7 @@ get_result_closed
get_result_bytes_left
get_result_is_text
recv_from_socket
send_to_socket
free

View file

@ -1,4 +1,14 @@
/* REPLACE
new WebSocketConstructor
*/
new WispWebSocket
new ((() => {
if (api.transport === "wisp") {
return WispWebSocket;
}
else if (api.transport === "wsproxy") {
return WebSocket;
}
else { //custom transports
return api.transport;
}
})())

View file

@ -0,0 +1,18 @@
function logger(type, text) {
if (type === "log")
console.log(text);
else if (type === "warn")
console.warn(text);
else if (type === "error")
console.error(text);
}
function log_msg(text) {
logger("log", text);
}
function warn_msg(text) {
logger("warn", text);
}
function error_msg(text) {
logger("error", text);
}

View file

@ -42,42 +42,6 @@ function check_loaded(check_websocket) {
}
}
//a case insensitive dictionary for request headers
class HeadersDict {
constructor(obj) {
for (let key in obj) {
this[key] = obj[key];
}
return new Proxy(this, this);
}
get(target, prop) {
let keys = Object.keys(this);
for (let key of keys) {
if (key.toLowerCase() === prop.toLowerCase()) {
return this[key];
}
}
}
set(target, prop, value) {
let keys = Object.keys(this);
for (let key of keys) {
if (key.toLowerCase() === prop.toLowerCase()) {
this[key] = value;
}
}
this[prop] = value;
return true;
}
}
function allocate_str(str) {
return allocate(intArrayFromString(str), ALLOC_NORMAL);
}
function allocate_array(array) {
return allocate(array, ALLOC_NORMAL);
}
//low level interface with c code
function perform_request(url, params, js_data_callback, js_end_callback, body=null) {
let params_str = JSON.stringify(params);
@ -103,7 +67,6 @@ function perform_request(url, params, js_data_callback, js_end_callback, body=nu
_free(url_ptr);
_free(response_json_ptr);
if (error != 0) console.error("request failed with error code " + error);
active_requests --;
js_end_callback(error, response_info);
}
@ -177,51 +140,11 @@ function create_response(response_data, response_info) {
return response_obj;
}
async function parse_body(data) {
let data_array = null;
if (typeof data === "string") {
data_array = new TextEncoder().encode(data);
}
else if (data instanceof Blob) {
let array_buffer = await data.arrayBuffer();
data_array = new Uint8Array(array_buffer);
}
//any typedarray
else if (data instanceof ArrayBuffer) {
//dataview objects
if (ArrayBuffer.isView(data) && data instanceof DataView) {
data_array = new Uint8Array(data.buffer);
}
//regular typed arrays
else if (ArrayBuffer.isView(data)) {
data_array = Uint8Array.from(data);
}
//regular arraybuffers
else {
data_array = new Uint8Array(data);
}
}
else if (data instanceof ReadableStream) {
let chunks = [];
for await (let chunk of data) {
chunks.push(chunk);
}
data_array = merge_arrays(chunks);
}
else {
throw "invalid data type to be sent";
}
return data_array;
}
async function create_options(params) {
let body = null;
if (params.body) {
body = await parse_body(params.body);
body = await data_to_array(params.body);
params.body = true;
}
@ -248,7 +171,9 @@ function perform_request_async(url, params, body) {
let finish_callback = (error, response_info) => {
if (error != 0) {
reject("libcurl.js encountered an error: " + error);
let error_str = `Request failed with error code ${error}: ${get_error_str(error)}`;
if (error != 0) error_msg(error_str);
reject(error_str);
return;
}
let response_data = merge_arrays(chunks);
@ -268,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() {
@ -288,6 +210,10 @@ function get_version() {
return version_dict;
}
function get_cacert() {
return UTF8ToString(_get_cacert());
}
function main() {
wasm_ready = true;
_init_curl();
@ -311,19 +237,26 @@ api = {
fetch: libcurl_fetch,
set_websocket: set_websocket_url,
load_wasm: load_wasm,
WebSocket: CurlWebSocket,
WebSocket: FakeWebSocket,
CurlWebSocket: CurlWebSocket,
TLSSocket: TLSSocket,
get_cacert: get_cacert,
wisp_connections: _wisp_connections,
WispConnection: WispConnection,
transport: "wisp",
get copyright() {return copyright_notice},
get version() {return get_version()},
get ready() {return wasm_ready},
get websocket_url() {return websocket_url},
get stdout() {return out},
set stdout(callback) {out = callback},
get stderr() {return err},
set stderr(callback) {err = callback},
get logger() {return logger},
set logger(func) {logger = func},
onload() {}
};

View file

@ -0,0 +1,100 @@
//currently broken
class TLSSocket {
constructor(hostname, port, options={}) {
check_loaded(true);
this.hostname = hostname;
this.port = port;
this.url = `https://${hostname}:${port}`;
this.options = options;
this.onopen = () => {};
this.onerror = () => {};
this.onmessage = () => {};
this.onclose = () => {};
this.connected = false;
this.event_loop = null;
this.connect();
}
connect() {
let data_callback = () => {};
let finish_callback = (error, response_info) => {
if (error === 0) {
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 request_options = {
_connect_only: 1,
}
if (this.options.verbose) {
request_options._libcurl_verbose = 1;
}
this.http_handle = perform_request(this.url, request_options, data_callback, finish_callback, null);
}
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 && !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();
}
else if (result_code != 81) {
this.cleanup(result_code);
}
_free(data_ptr);
_free(result_ptr);
}
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);
_free(data_ptr);
}
cleanup(error=false) {
if (this.http_handle) _cleanup_handle(this.http_handle);
else return;
clearInterval(this.event_loop);
this.connected = false;
if (error) {
this.onerror(error);
}
else {
this.onclose();
}
}
close() {
this.cleanup();
}
}

82
client/javascript/util.js Normal file
View file

@ -0,0 +1,82 @@
//a case insensitive dictionary for request headers
class HeadersDict {
constructor(obj) {
for (let key in obj) {
this[key] = obj[key];
}
return new Proxy(this, this);
}
get(target, prop) {
let keys = Object.keys(this);
for (let key of keys) {
if (key.toLowerCase() === prop.toLowerCase()) {
return this[key];
}
}
}
set(target, prop, value) {
let keys = Object.keys(this);
for (let key of keys) {
if (key.toLowerCase() === prop.toLowerCase()) {
this[key] = value;
}
}
this[prop] = value;
return true;
}
}
function allocate_str(str) {
return allocate(intArrayFromString(str), ALLOC_NORMAL);
}
function allocate_array(array) {
return allocate(array, ALLOC_NORMAL);
}
function get_error_str(error_code) {
let error_ptr = _get_error_str(error_code);
return UTF8ToString(error_ptr);
}
//convert any data to a uint8array
async function data_to_array(data) {
let data_array = null;
if (typeof data === "string") {
data_array = new TextEncoder().encode(data);
}
else if (data instanceof Blob) {
let array_buffer = await data.arrayBuffer();
data_array = new Uint8Array(array_buffer);
}
//any typedarray
else if (data instanceof ArrayBuffer) {
//dataview objects
if (ArrayBuffer.isView(data) && data instanceof DataView) {
data_array = new Uint8Array(data.buffer);
}
//regular typed arrays
else if (ArrayBuffer.isView(data)) {
data_array = Uint8Array.from(data);
}
//regular arraybuffers
else {
data_array = new Uint8Array(data);
}
}
else if (data instanceof ReadableStream) {
let chunks = [];
for await (let chunk of data) {
chunks.push(chunk);
}
data_array = merge_arrays(chunks);
}
else {
throw "invalid data type to be sent";
}
return data_array;
}

View file

@ -1,50 +1,51 @@
//class for custom websocket
class CurlWebSocket extends EventTarget {
constructor(url, protocols=[], websocket_debug=false) {
super();
class CurlWebSocket {
constructor(url, protocols=[], options={}) {
check_loaded(true);
if (!url.startsWith("wss://") && !url.startsWith("ws://")) {
throw new SyntaxError("invalid url");
}
this.url = url;
this.protocols = protocols;
this.binaryType = "blob";
this.recv_buffer = [];
this.websocket_debug = websocket_debug;
this.options = options;
//legacy event handlers
this.onopen = () => {};
this.onerror = () => {};
this.onmessage = () => {};
this.onclose = () => {};
this.CONNECTING = 0;
this.OPEN = 1;
this.CLOSING = 2;
this.CLOSED = 3;
this.connected = false;
this.event_loop = null;
this.recv_buffer = [];
this.connect();
}
connect() {
this.status = this.CONNECTING;
let data_callback = () => {};
let finish_callback = (error, response_info) => {
this.finish_callback(error, response_info);
if (error === 0) {
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 = {
headers: this.options.headers || {}
};
if (this.protocols) {
options.headers = {
"Sec-Websocket-Protocol": this.protocols.join(", "),
};
request_options.headers["Sec-Websocket-Protocol"] = this.protocols.join(", ");
}
if (this.websocket_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.recv_loop();
this.http_handle = perform_request(this.url, request_options, data_callback, finish_callback, null);
}
recv() {
@ -52,12 +53,16 @@ class CurlWebSocket extends EventTarget {
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;
if (result_code == 0) { //CURLE_OK - data recieved
//CURLE_OK - data received
if (result_code === 0 && !result_closed) {
if (_get_result_closed(result_ptr)) {
//this.pass_buffer();
this.close_callback();
return;
_free(data_ptr);
_free(result_ptr);
this.cleanup();
return returned_data;
}
let data_size = _get_result_size(result_ptr);
@ -69,133 +74,59 @@ class CurlWebSocket extends EventTarget {
let full_data = merge_arrays(this.recv_buffer);
let is_text = _get_result_is_text(result_ptr)
this.recv_buffer = [];
this.recv_callback(full_data, is_text);
if (is_text) {
returned_data = new TextDecoder().decode(full_data);
}
else {
returned_data = full_data;
}
}
}
// websocket was cleanly closed by the server
else if (result_code === 0 && result_closed) {
this.cleanup();
}
if (result_code == 52) { //CURLE_GOT_NOTHING - socket closed
this.close_callback();
//code is not CURLE_AGAIN - an error must have occurred
else if (result_code !== 81) {
this.cleanup(result_code);
}
_free(data_ptr);
_free(result_ptr);
return returned_data;
}
recv_loop() {
this.event_loop = setInterval(() => {
this.recv();
}, 1);
}
recv_callback(data, is_text=false) {
let converted;
if (is_text) {
converted = new TextDecoder().decode(data);
}
else {
if (this.binaryType == "blob") {
converted = new Blob(data);
}
else if (this.binaryType == "arraybuffer") {
converted = data.buffer;
}
else {
throw "invalid binaryType string";
}
}
let msg_event = new MessageEvent("message", {data: converted});
this.onmessage(msg_event);
this.dispatchEvent(msg_event);
}
close_callback(error=false) {
if (this.status == this.CLOSED) return;
this.status = this.CLOSED;
cleanup(error=0) {
if (this.http_handle) _cleanup_handle(this.http_handle);
else return;
clearInterval(this.event_loop);
_cleanup_websocket();
this.connected = false;
if (error) {
let error_event = new Event("error");
this.dispatchEvent(error_event);
this.onerror(error_event);
this.onerror(error);
}
else {
let close_event = new CloseEvent("close");
this.dispatchEvent(close_event);
this.onclose(close_event);
this.onclose();
}
}
finish_callback(error, response_info) {
this.status = this.OPEN;
if (error != 0) this.close_callback(true);
let open_event = new Event("open");
this.onopen(open_event);
this.dispatchEvent(open_event);
}
send(data) {
let is_text = false;
if (this.status === this.CONNECTING) {
throw new DOMException("ws not ready yet");
}
if (this.status === this.CLOSED) {
return;
}
let is_text = typeof data === "string";
if (!this.connected) return;
let data_array;
if (typeof data === "string") {
data_array = new TextEncoder().encode(data);
is_text = true;
if (is_text) {
data = new TextEncoder().encode(data);
}
else if (data instanceof Blob) {
data.arrayBuffer().then(array_buffer => {
data_array = new Uint8Array(array_buffer);
this.send(data_array);
});
return;
}
//any typedarray
else if (data instanceof ArrayBuffer) {
//dataview objects
if (ArrayBuffer.isView(data) && data instanceof DataView) {
data_array = new Uint8Array(data.buffer);
}
//regular arraybuffers
else {
data_array = new Uint8Array(data);
}
}
//regular typed arrays
else if (ArrayBuffer.isView(data)) {
data_array = Uint8Array.from(data);
}
else {
throw "invalid data type to be sent";
}
let data_ptr = allocate_array(data_array);
let data_ptr = allocate_array(data);
let data_len = data.length;
_send_to_websocket(this.http_handle, data_ptr, data_len, is_text);
_free(data_ptr);
}
close() {
_close_websocket(this.http_handle);
}
get readyState() {
return this.status;
}
get bufferedAmount() {
return 0;
}
get protocol() {
return "";
}
get extensions() {
return "";
this.cleanup();
}
}

View file

@ -0,0 +1,113 @@
//class for websocket polyfill
class FakeWebSocket extends EventTarget {
constructor(url, protocols=[], options={}) {
super();
this.url = url;
this.protocols = protocols;
this.options = options;
this.binaryType = "blob";
//legacy event handlers
this.onopen = () => {};
this.onerror = () => {};
this.onmessage = () => {};
this.onclose = () => {};
this.CONNECTING = 0;
this.OPEN = 1;
this.CLOSING = 2;
this.CLOSED = 3;
this.status = this.CONNECTING;
this.socket = null;
this.connect();
}
connect() {
this.socket = new CurlWebSocket(this.url, this.protocols, this.options);
this.socket.onopen = () => {
this.status = this.OPEN;
let open_event = new Event("open");
this.onopen(open_event);
this.dispatchEvent(open_event);
}
this.socket.onclose = () => {
this.status = this.CLOSED;
let close_event = new CloseEvent("close");
this.dispatchEvent(close_event);
this.onclose(close_event);
};
this.socket.onerror = (error) => {
this.status = this.CLOSED;
error_msg(`websocket ${this.url} encountered an error (${error})`);
let error_event = new Event("error");
this.dispatchEvent(error_event);
this.onerror(error_event);
}
this.socket.onmessage = (data) => {
let converted;
if (typeof data === "string") {
converted = data;
}
else { //binary frame received as uint8array
if (this.binaryType == "blob") {
converted = new Blob(data);
}
else if (this.binaryType == "arraybuffer") {
converted = data.buffer;
}
else {
throw "invalid binaryType string";
}
}
let msg_event = new MessageEvent("message", {data: converted});
this.onmessage(msg_event);
this.dispatchEvent(msg_event);
}
}
send(data) {
let is_text = typeof data === "string";
if (this.status === this.CONNECTING) {
throw new DOMException("websocket not ready yet");
}
if (this.status === this.CLOSED) {
return;
}
(async () => {
if (is_text) {
this.socket.send(data);
}
else {
let data_array = await data_to_array(data);
this.send(data_array);
}
})();
}
close() {
this.status = this.CLOSING;
this.socket.close();
}
get readyState() {
return this.status;
}
get bufferedAmount() {
return 0;
}
get protocol() {
return this.protocols[0] || "";
}
get extensions() {
return "";
}
}

View file

@ -7,9 +7,9 @@
#include "curl/easy.h"
#include "curl/header.h"
#include "cjson/cJSON.h"
#include "cacert.h"
#include "curl/multi.h"
#include "cacert.h"
#include "util.h"
#include "types.h"
@ -83,6 +83,13 @@ CURL* start_request(const char* url, const char* json_params, DataCallback data_
curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1L);
}
if (strcmp(key, "_connect_only") == 0) {
curl_easy_setopt(http_handle, CURLOPT_CONNECT_ONLY, 1L);
curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(http_handle, CURLOPT_SSL_ENABLE_ALPN, 0L);
prevent_cleanup = 1;
}
if (strcmp(key, "method") == 0 && cJSON_IsString(item)) {
curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST, item->valuestring);
}
@ -189,6 +196,10 @@ void finish_request(CURLMsg *curl_msg) {
free(request_info);
}
unsigned char* get_cacert() {
return _cacert_pem;
}
void init_curl() {
curl_global_init(CURL_GLOBAL_DEFAULT);
multi_handle = curl_multi_init();

View file

@ -0,0 +1,24 @@
#include <stdlib.h>
#include "curl/curl.h"
#include "curl/easy.h"
#include "types.h"
struct WSResult* recv_from_socket(CURL* http_handle, int buffer_size) {
size_t nread;
char* buffer = malloc(buffer_size);
CURLcode res = curl_easy_recv(http_handle, buffer, buffer_size, &nread);
struct WSResult* result = malloc(sizeof(struct WSResult));
result->buffer_size = nread;
result->buffer = buffer;
result->res = (int) res;
result->closed = (nread == 0);
return result;
}
int send_to_socket(CURL* http_handle, const char* data, int data_len) {
size_t sent;
CURLcode res = curl_easy_send(http_handle, data, data_len, &sent);
return (int) res;
}

View file

@ -34,4 +34,8 @@ char* get_version() {
char* version_json_str = cJSON_Print(version_json);
cJSON_Delete(version_json);
return version_json_str;
}
const char* get_error_str(CURLcode error_code) {
return curl_easy_strerror(error_code);
}

View file

@ -37,7 +37,7 @@ void close_websocket(CURL* http_handle) {
}
//clean up the http handle associated with the websocket, since the main loop can't do this automatically
void cleanup_websocket(CURL* http_handle) {
void cleanup_handle(CURL* http_handle) {
struct RequestInfo *request_info;
curl_easy_getinfo(http_handle, CURLINFO_PRIVATE, &request_info);

View file

@ -1,6 +1,6 @@
{
"name": "libcurl.js",
"version": "0.3.9",
"version": "0.4.0",
"description": "An experimental port of libcurl to WebAssembly for use in the browser.",
"main": "libcurl.mjs",
"scripts": {