libcurl.js/client/javascript/main.js

199 lines
No EOL
5.3 KiB
JavaScript

/*
ading2210/libcurl.js - A port of libcurl to WASM for the browser.
Copyright (C) 2023 ading2210
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//everything is wrapped in a function to prevent emscripten from polluting the global scope
const libcurl = (function() {
//emscripten compiled code is inserted here
/* __emscripten_output__ */
//extra client code goes here
/* __extra_libraries__ */
var websocket_url = null;
var wasm_ready = false;
var version_dict = null;
var api = null;
var main_session = null;
const libcurl_version = "__library_version__";
const wisp_version = "__wisp_version__";
function check_loaded(check_websocket) {
if (!wasm_ready) {
throw new Error("wasm not loaded yet, please call libcurl.load_wasm first");
}
if (!websocket_url && check_websocket) {
throw new Error("websocket proxy url not set, please call libcurl.set_websocket");
}
}
//wrap perform_request in a promise
function perform_request_async(url, params, body) {
return new Promise((resolve, reject) => {
let stream_controller;
let http_handle;
let response_obj;
let aborted = false;
//handle abort signals
if (params.signal instanceof AbortSignal) {
params.signal.addEventListener("abort", () => {
if (aborted) return;
aborted = true;
_cleanup_handle(http_handle);
if (!response_obj) {
reject(new DOMException("The operation was aborted."));
}
else {
stream_controller.error("The operation was aborted.");
}
});
}
let stream = new ReadableStream({
start(controller) {
stream_controller = controller;
}
});
function data_callback(new_data) {
try {
stream_controller.enqueue(new_data);
}
catch (e) {
//the readable stream has been closed elsewhere, so cancel the request
if (e instanceof TypeError) {
_cleanup_handle(http_handle);
}
else {
throw e;
}
}
}
function headers_callback() {
let response_json = c_func_str(_http_get_info, [http_handle]);
response_obj = create_response(stream, JSON.parse(response_json));
resolve(response_obj);
}
function finish_callback(error) {
if (error != 0) {
error_msg(`Request "${url}" failed with error code ${error}: ${get_error_str(error)}`);
reject(`Request failed with error code ${error}: ${get_error_str(error)}`);
return;
}
try {
stream_controller.close();
} //this will only fail if the stream is already errored or closed, which isn't a problem
catch {}
}
let body_length = body ? body.length : 0;
let params_json = JSON.stringify(params);
http_handle = create_request(url, data_callback, finish_callback, headers_callback);
c_func(_http_set_options, [http_handle, params_json, body, body_length]);
start_request(http_handle);
});
}
async function libcurl_fetch(url, params={}) {
check_loaded(true);
let body = await create_options(params);
return await perform_request_async(url, params, body);
}
function set_websocket_url(url) {
websocket_url = url;
if (Module.websocket) {
Module.websocket.url = url;
}
}
function get_version() {
if (!wasm_ready) return null;
if (version_dict) return version_dict;
let version_ptr = _get_version();
let version_str = UTF8ToString(version_ptr);
_free(version_ptr);
version_dict = JSON.parse(version_str);
version_dict.lib = libcurl_version;
version_dict.wisp = wisp_version;
return version_dict;
}
function get_cacert() {
return UTF8ToString(_get_cacert());
}
function main() {
wasm_ready = true;
_init_curl();
set_websocket_url(websocket_url);
if (ENVIRONMENT_IS_WEB) {
let load_event = new Event("libcurl_load");
document.dispatchEvent(load_event);
}
main_session = new HTTPSession();
api.fetch = main_session.fetch.bind(main_session);
api.onload();
}
function load_wasm(url) {
wasmBinaryFile = url;
createWasm();
run();
}
Module.onRuntimeInitialized = main;
api = {
set_websocket: set_websocket_url,
load_wasm: load_wasm,
get_cacert: get_cacert,
get_error_string: get_error_str,
wisp_connections: _wisp_connections,
WispConnection: WispConnection,
transport: "wisp",
WebSocket: WebSocket,
CurlWebSocket: CurlWebSocket,
TLSSocket: TLSSocket,
fetch: () => {throw "not ready"},
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() {}
};
return api;
})()