JavaScript "httplib"

Admin User, erstellt 08. März 2024
         
/**
* Modern Albufeira Prolog Interpreter
*
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies AG makes no warranties
* regarding the provided information. XLOG Technologies AG assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies AG.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies AG. If the company was not the originator of some
* excerpts, XLOG Technologies AG has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies AG.
*/
import {
exec_build, exec_unify, add, http, check_atom, make_error,
Compound, open_write, open_file_promise, launch_async, get_engine,
get_codebase, Sink, make_check, deref, check_integer, check_clause,
MASK_SRC_AREAD, http_read_promise, Source, map_http_result,
get_ctx, fs, register_signal, http_close_promise,
map_file_error, map_stream_error, Context, register_interrupt
} from "../../dogelog.mjs";
/*********************************************************************/
/* HTTP Client */
/*********************************************************************/
/**
* os_open_promise_opts(P, L, S, Q):
* The predicate succeeds in Q with a promise for open input S
* on path P and option list L.
*/
function test_os_open_promise_opts(args) {
let url = deref(exec_build(args[0]));
check_atom(url);
let opts = deref(exec_build(args[1]));
let stream = new Source();
if (!exec_unify(args[2], stream))
return false;
let buf = get_ctx();
let prom;
if (fs !== undefined) {
if (url.startsWith("http:") || url.startsWith("https:")) {
prom = open_http_promise_opts(buf, stream, url, opts);
} else {
prom = open_file_promise(buf, stream, url);
}
} else {
try {
url = new URL(url, get_codebase()).href;
} catch (err) {
throw make_error(
new Compound("resource_error", ["base_url"]));
}
prom = open_http_promise_opts(buf, stream, url, opts);
}
return exec_unify(args[3], prom);
}
export function open_http_promise_opts(buf, stream, url, opts) {
let paras = {};
if (opts.method !== undefined) {
paras.method = opts.method;
} else if (opts.body !== undefined) {
paras.method = "POST";
} else {
paras.method = "GET";
}
if (opts.headers !== undefined)
paras.headers = opts.headers;
if (opts.body !== undefined)
paras.body = opts.body;
let contr = new AbortController();
paras.signal = contr.signal;
register_interrupt(buf, () => contr.abort("ABORT"));
return fetch(url, paras).then(response => {
register_interrupt(buf, () => {});
if (response.status !== 200) {
register_signal(buf, map_http_result(response.status, url));
} else {
stream.data = response.body.getReader();
stream.receive = http_read_promise;
stream.release = http_close_promise;
stream.flags |= MASK_SRC_AREAD;
}
}, err => {
register_interrupt(buf, () => {});
if ("ABORT" !== err)
register_signal(buf, map_file_error(err, url));
});
}
/**
* os_open_sync_opts(P, M, L, S):
* The predicate succeeds. As a side effect the stream S is
* opened on the path P with the mode M and the option list L.
*/
function test_os_open_sync_opts(args) {
let url = deref(exec_build(args[0]));
check_atom(url);
let mode = deref(exec_build(args[1]));
check_atom(mode);
let opts = deref(exec_build(args[2]));
let stream;
if ("read" === mode) {
throw make_error(new Compound("resource_error",
["not_implemented"]));
} else if ("write" === mode) {
stream = open_write(url, "w");
} else if ("append" === mode) {
stream = open_write(url, "a");
} else {
throw make_error(new Compound("domain_error",
["io_mode", mode]));
}
return exec_unify(args[3], stream);
}
/*********************************************************************/
/* HTTP Server */
/*********************************************************************/
/**
* http_server_new(S):
* The predicate succeeds in S with a new http server.
*/
function test_http_server_new(args) {
let obj = http.createServer();
return exec_unify(args[0], obj);
}
/**
* sys_http_server_on(S, T, C):
* The predicate succeeds. As a side effect the stackfull handler C
* is registered as listener to event T from server S.
*/
function test_sys_http_server_on(args) {
let obj = deref(exec_build(args[0]));
let type = deref(exec_build(args[1]));
check_atom(type);
let clause = deref(exec_build(args[2]));
check_clause(clause);
let buf = new Context();
buf.engine.text_output = get_engine().text_output;
buf.engine.text_error = get_engine().text_error;
buf.engine.text_input = get_engine().text_input;
obj.on(type, async (...paras) => await launch_async(clause, buf, paras))
return true;
}
/**
* http_server_listen(S, P):
* The predicate succeeds. As a side effect the server S
* starts listening on port P.
*/
function test_http_server_listen(args) {
let obj = deref(exec_build(args[0]));
let port = deref(exec_build(args[1]));
check_integer(port);
obj.listen(port);
return true;
}
/*********************************************************************/
/* HTTP Request */
/*********************************************************************/
/**
* http_current_method(S, P):
* The predicate succeeds in M with the method of the HTTP request S.
*/
function test_http_current_method(args) {
let obj = deref(exec_build(args[0]));
return exec_unify(args[1], obj.method);
}
/**
* http_current_path(S, P):
* The predicate succeeds in P with the path of the HTTP request S.
*/
function test_http_current_path(args) {
let obj = deref(exec_build(args[0]));
return exec_unify(args[1], obj.url);
}
/**
* http_input_promise(S, R, Q):
* The predicate succeeds in Q with a promise for a new
* text reader R on HTTP request S.
*/
function test_http_input_promise(args) {
let obj = deref(exec_build(args[0]));
let stream = new Source();
if (!exec_unify(args[1], stream))
return false;
let buf = get_ctx();
let prom = http_input_promise(buf, stream, obj);
return exec_unify(args[2], prom);
}
function http_input_promise(buf, stream, obj) {
return new Promise(resolve => {
let res = [];
obj.setEncoding('utf8');
obj.on("data", chunk => {
res.push(chunk);
});
obj.on("end", () => {
stream.buf = res.join();
resolve();
});
obj.on("error", err => {
register_signal(buf, map_stream_error(err));
resolve();
});
});
}
/*********************************************************************/
/* HTTP Response */
/*********************************************************************/
/**
* sys_http_write_head(S, C, H):
* The predicate succeeds. As a side effect it writes the status
* code C and the headers map H to the HTTP response S.
*/
function test_sys_http_write_head(args) {
let obj = deref(exec_build(args[0]));
let code = deref(exec_build(args[1]));
check_integer(code);
let headers = deref(exec_build(args[2]));
obj.writeHead(code, headers);
return true;
}
/**
* http_output_new(S, W):
* The predicate succeeds in W with a new text writer for the HTTP response S.
*/
function test_http_output_new(args) {
let obj = deref(exec_build(args[0]));
let dst = new Sink();
dst.data = obj;
dst.send = http_send;
dst.release = http_release;
return exec_unify(args[1], dst);
}
function http_send(res, buf) {
res.write(buf);
}
function http_release(res) {
res.end();
}
/*********************************************************************/
/* HTTP Lib Init */
/*********************************************************************/
export function main() {
add("os_open_promise_opts", 4, make_check(test_os_open_promise_opts));
add("os_open_sync_opts", 4, make_check(test_os_open_sync_opts));
add("http_server_new", 1, make_check(test_http_server_new));
add("sys_http_server_on", 3, make_check(test_sys_http_server_on));
add("http_server_listen", 2, make_check(test_http_server_listen));
add("http_current_method", 2, make_check(test_http_current_method));
add("http_current_path", 2, make_check(test_http_current_path));
add("http_input_promise", 3, make_check(test_http_input_promise));
add("sys_http_write_head", 3, make_check(test_sys_http_write_head));
add("http_output_new", 2, make_check(test_http_output_new));
}