all repos — litestore @ a81a10a597f44a6d6ec1c19c470520b41f71092a

A minimalist nosql document store.

Added request and response to ctx object, enable console and print.
h3rald h3rald@h3rald.com
Fri, 31 Jan 2020 11:48:19 +0100
commit

a81a10a597f44a6d6ec1c19c470520b41f71092a

parent

c82bbb3d57f2fff05fdf8f10db798aa622a0c86c

A src/litestorepkg/examples/test.js

@@ -0,0 +1,1 @@

+ctx.response.content = JSON.stringify(ctx.request);
M src/litestorepkg/lib/api_v6.nimsrc/litestorepkg/lib/api_v6.nim

@@ -7,12 +7,12 @@ strtabs,

pegs, json, os, - times, - duktape + times import types, contenttypes, core, + custom, utils, logger

@@ -912,30 +912,6 @@

# Custom Resources support proc execute*(req: LSRequest, LS:LiteStore, id: string): LSResponse = - var ctx = duk_create_heap_default() - var ctx_idx = ctx.duk_push_object() - var req_idx = ctx.duk_push_object() if not LS.customResources.hasKey(id): return resError(Http404, "Custom resource '$1' not found." % id) - # Create execution context - ctx.duk_push_int(200) - discard ctx.duk_put_prop_string(req_idx, "code") - discard ctx.duk_push_string("") - discard ctx.duk_put_prop_string(req_idx, "content") - # Todo: Add default headers - discard ctx.duk_put_prop_string(ctx_idx, "response") - # Todo: Add request object - discard ctx.duk_put_global_string("ctx") - # Evaluate custom resource - try: - ctx.duk_eval_string(LS.customResources[id]) - except: - return resError(Http500, "An error occurred when executing custom resource code.") - # Retrieve response - ctx.duk_eval_string("JSON.stringify(ctx.response)") - echo "TEST!" - let jResponse = parseJson($(ctx.duk_get_string(-1))) - echo jResponse - ctx.duk_destroy_heap(); - result.code = HttpCode(jResponse["code"].getInt) - result.content = jResponse["content"].getStr + return custom.execute(req, LS, id)
A src/litestorepkg/lib/custom.nim

@@ -0,0 +1,78 @@

+import + json, + asynchttpserver, + strtabs, + duktape, + httpcore, + tables +import + types, + utils + +proc createRequest(LS: LiteStore, ctx: DTContext, obj: duk_idx_t, req: LSRequest) = + var req_idx = ctx.duk_push_object() + discard ctx.duk_push_string($req.reqMethod) + discard ctx.duk_put_prop_string(req_idx, "method") + # url + var uri_idx = ctx.duk_push_object() + discard ctx.duk_push_string(LS.address) + discard ctx.duk_put_prop_string(uri_idx, "hostname") + ctx.duk_push_int(cast[cint](LS.port)) + discard ctx.duk_put_prop_string(uri_idx, "port") + discard ctx.duk_push_string(req.url.query) + discard ctx.duk_put_prop_string(uri_idx, "search") + discard ctx.duk_push_string(req.url.path) + discard ctx.duk_put_prop_string(uri_idx, "path") + discard ctx.duk_put_prop_string(req_idx, "url") + discard ctx.duk_push_string(req.body) + discard ctx.duk_put_prop_string(req_idx, "body") + var hd_idx = ctx.duk_push_object() + for k, v in pairs(req.headers): + discard ctx.duk_push_string(v) + discard ctx.duk_put_prop_string(hd_idx, k) + discard ctx.duk_put_prop_string(req_idx, "headers") + discard ctx.duk_put_prop_string(obj, "request") + +proc createResponse(LS: LiteStore, ctx: DTContext, obj: duk_idx_t) = + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(200) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string("") + discard ctx.duk_put_prop_string(res_idx, "content") + var hd_idx = ctx.duk_push_object() + discard ctx.duk_push_string("*") + discard ctx.duk_put_prop_string(hd_idx, "Access-Control-Allow-Origin") + discard ctx.duk_push_string("Authorization, Content-Type") + discard ctx.duk_put_prop_string(hd_idx, "Access-Control-Allow-Headers") + discard ctx.duk_push_string(LS.appname & "/" & LS.appversion) + discard ctx.duk_put_prop_string(hd_idx, "Server") + discard ctx.duk_push_string("application/json") + discard ctx.duk_put_prop_string(hd_idx, "Content-Type") + discard ctx.duk_put_prop_string(res_idx, "headers") + discard ctx.duk_put_prop_string(obj, "response") + + +proc execute*(req: LSRequest, LS:LiteStore, id: string): LSResponse = + # Create execution context + var ctx = duk_create_heap_default() + duk_console_init(ctx) + duk_print_alert_init(ctx) + var ctx_idx = ctx.duk_push_object() + LS.createRequest(ctx, ctx_idx, req) + LS.createResponse(ctx, ctx_idx) + discard ctx.duk_put_global_string("ctx") + # Evaluate custom resource + try: + ctx.duk_eval_string(LS.customResources[id]) + except: + return resError(Http500, "An error occurred when executing custom resource code.") + # Retrieve response + ctx.duk_eval_string("JSON.stringify(ctx.response)") + let jResponse = parseJson($(ctx.duk_get_string(-1))) + ctx.duk_destroy_heap(); + result.code = HttpCode(jResponse["code"].getInt) + result.content = jResponse["content"].getStr + result.headers = newHttpHeaders() + for k, v in pairs(jResponse["headers"]): + result.headers[k] = v.getStr + result.headers["Content-Length"] = $result.content.len
M src/litestorepkg/lib/duktape.nimsrc/litestorepkg/lib/duktape.nim

@@ -29,7 +29,11 @@ import strutils

const sourcePath = currentSourcePath().split({'\\', '/'})[0..^2].join("/") {.passC: "-I\"" & sourcePath & "/../vendor/duktape\"".} const headerduktape = sourcePath & "/../vendor/duktape/duktape.h" +const headerconsole = sourcePath & "/../vendor/duktape/extras/console/duk_console.h" +const headerprintalert = sourcePath & "/../vendor/duktape/extras/print-alert/duk_print_alert.h" {.compile: "../vendor/duktape/duktape.c".} +{.compile: "../vendor/duktape/extras/console/duk_console.c".} +{.compile: "../vendor/duktape/extras/print-alert/duk_print_alert.c".} const DUK_VERSION* = 20201 DUK_DEBUG_PROTOCOL_VERSION* = 2

@@ -702,6 +706,13 @@ proc duk_eval_string*(ctx: DTContext, s: cstring) {.header: headerduktape.}

proc duk_pcompile_string*(ctx: DTContext, flags: duk_uint_t, s: cstring): duk_int_t {.header: headerduktape.} proc duk_safe_to_string*(ctx: DTContext, idx: duk_idx_t): cstring {.header: headerduktape.} # proc duk_to_string*(ctx: DTContext, index: cint): cstring {.header: headerduktape.} + +## Extras + +proc duk_console_init*(ctx: DTContext, flags: duk_uint_t = 0) {.stdcall, +importc: "duk_console_init", header: headerconsole.} +proc duk_print_alert_init*(ctx: DTContext, flags: duk_uint_t = 0) {.stdcall, +importc: "duk_print_alert_init", header: headerprintalert.} type DTCFunction* = duk_c_function
A src/litestorepkg/vendor/duktape/extras/console/duk_console.c

@@ -0,0 +1,185 @@

+/* + * Minimal 'console' binding. + * + * https://github.com/DeveloperToolsWG/console-object/blob/master/api.md + * https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference + * https://developer.mozilla.org/en/docs/Web/API/console + */ + +#include <stdio.h> +#include <stdarg.h> +#include "duktape.h" +#include "duk_console.h" + +/* XXX: Add some form of log level filtering. */ + +/* XXX: Should all output be written via e.g. console.write(formattedMsg)? + * This would make it easier for user code to redirect all console output + * to a custom backend. + */ + +/* XXX: Init console object using duk_def_prop() when that call is available. */ + +static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) { + duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx); + FILE *output = (flags & DUK_CONSOLE_STDOUT_ONLY) ? stdout : stderr; + duk_idx_t n = duk_get_top(ctx); + duk_idx_t i; + + duk_get_global_string(ctx, "console"); + duk_get_prop_string(ctx, -1, "format"); + + for (i = 0; i < n; i++) { + if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) { + /* Slow path formatting. */ + duk_dup(ctx, -1); /* console.format */ + duk_dup(ctx, i); + duk_call(ctx, 1); + duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */ + } + } + + duk_pop_2(ctx); + + duk_push_string(ctx, " "); + duk_insert(ctx, 0); + duk_join(ctx, n); + + if (error_name) { + duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1)); + duk_push_string(ctx, "name"); + duk_push_string(ctx, error_name); + duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */ + duk_get_prop_string(ctx, -1, "stack"); + } + + fprintf(output, "%s\n", duk_to_string(ctx, -1)); + if (flags & DUK_CONSOLE_FLUSH) { + fflush(output); + } + return 0; +} + +static duk_ret_t duk__console_assert(duk_context *ctx) { + if (duk_to_boolean(ctx, 0)) { + return 0; + } + duk_remove(ctx, 0); + + return duk__console_log_helper(ctx, "AssertionError"); +} + +static duk_ret_t duk__console_log(duk_context *ctx) { + return duk__console_log_helper(ctx, NULL); +} + +static duk_ret_t duk__console_trace(duk_context *ctx) { + return duk__console_log_helper(ctx, "Trace"); +} + +static duk_ret_t duk__console_info(duk_context *ctx) { + return duk__console_log_helper(ctx, NULL); +} + +static duk_ret_t duk__console_warn(duk_context *ctx) { + return duk__console_log_helper(ctx, NULL); +} + +static duk_ret_t duk__console_error(duk_context *ctx) { + return duk__console_log_helper(ctx, "Error"); +} + +static duk_ret_t duk__console_dir(duk_context *ctx) { + /* For now, just share the formatting of .log() */ + return duk__console_log_helper(ctx, 0); +} + +static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) { + duk_push_c_function(ctx, func, DUK_VARARGS); + duk_push_string(ctx, "name"); + duk_push_string(ctx, name); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */ + duk_set_magic(ctx, -1, (duk_int_t) flags); + duk_put_prop_string(ctx, -2, name); +} + +void duk_console_init(duk_context *ctx, duk_uint_t flags) { + duk_uint_t flags_orig; + + /* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified, + * just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY. + */ + if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) { + flags &= ~DUK_CONSOLE_STDOUT_ONLY; + } + /* Remember the (possibly corrected) flags we received. */ + flags_orig = flags; + + duk_push_object(ctx); + + /* Custom function to format objects; user can replace. + * For now, try JX-formatting and if that fails, fall back + * to ToString(v). + */ + duk_eval_string(ctx, + "(function (E) {" + "return function format(v){" + "try{" + "return E('jx',v);" + "}catch(e){" + "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */ + "}" + "};" + "})(Duktape.enc)"); + duk_put_prop_string(ctx, -2, "format"); + + flags = flags_orig; + if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { + /* No output indicators were specified; these levels go to stdout. */ + flags |= DUK_CONSOLE_STDOUT_ONLY; + } + duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags); + duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags); + duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */ + duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags); + duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags); + + flags = flags_orig; + if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { + /* No output indicators were specified; these levels go to stderr. */ + flags |= DUK_CONSOLE_STDERR_ONLY; + } + duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags); + duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags); + duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */ + duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags); + + duk_put_global_string(ctx, "console"); + + /* Proxy wrapping: ensures any undefined console method calls are + * ignored silently. This was required specifically by the + * DeveloperToolsWG proposal (and was implemented also by Firefox: + * https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is + * apparently no longer the preferred way of implementing console. + * When Proxy is enabled, whitelist at least .toJSON() to avoid + * confusing JX serialization of the console object. + */ + + if (flags & DUK_CONSOLE_PROXY_WRAPPER) { + /* Tolerate failure to initialize Proxy wrapper in case + * Proxy support is disabled. + */ + (void) duk_peval_string_noresult(ctx, + "(function(){" + "var D=function(){};" + "var W={toJSON:true};" /* whitelisted */ + "console=new Proxy(console,{" + "get:function(t,k){" + "var v=t[k];" + "return typeof v==='function'||W[k]?v:D;" + "}" + "});" + "})();" + ); + } +}
A src/litestorepkg/vendor/duktape/extras/console/duk_console.h

@@ -0,0 +1,29 @@

+#if !defined(DUK_CONSOLE_H_INCLUDED) +#define DUK_CONSOLE_H_INCLUDED + +#include "duktape.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */ +#define DUK_CONSOLE_PROXY_WRAPPER (1U << 0) + +/* Flush output after every call. */ +#define DUK_CONSOLE_FLUSH (1U << 1) + +/* Send output to stdout only (default is mixed stdout/stderr). */ +#define DUK_CONSOLE_STDOUT_ONLY (1U << 2) + +/* Send output to stderr only (default is mixed stdout/stderr). */ +#define DUK_CONSOLE_STDERR_ONLY (1U << 3) + +/* Initialize the console system */ +extern void duk_console_init(duk_context *ctx, duk_uint_t flags); + +#if defined(__cplusplus) +} +#endif /* end 'extern "C"' wrapper */ + +#endif /* DUK_CONSOLE_H_INCLUDED */
A src/litestorepkg/vendor/duktape/extras/print-alert/duk_print_alert.c

@@ -0,0 +1,127 @@

+/* + * Duktape 1.x compatible print() and alert() bindings. + */ + +#include <stdio.h> +#include <string.h> +#include "duktape.h" +#include "duk_print_alert.h" + +#define DUK_PRINT_ALERT_FLUSH /* Flush after stdout/stderr write (Duktape 1.x: yes) */ +#undef DUK_PRINT_ALERT_SMALL /* Prefer smaller footprint (but slower and more memory churn) */ + +#if defined(DUK_PRINT_ALERT_SMALL) +static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) { + duk_idx_t nargs; + + nargs = duk_get_top(ctx); + + /* If argument count is 1 and first argument is a buffer, write the buffer + * as raw data into the file without a newline; this allows exact control + * over stdout/stderr without an additional entrypoint (useful for now). + * Otherwise current print/alert semantics are to ToString() coerce + * arguments, join them with a single space, and append a newline. + */ + + if (nargs == 1 && duk_is_buffer_data(ctx, 0)) { + buf = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &sz_buf); + fwrite((const void *) buf, 1, (size_t) sz_buf, fh); + } else { + duk_push_string(ctx, " "); + duk_insert(ctx, 0); + duk_concat(ctx, nargs); + fprintf(fh, "%s\n", duk_require_string(ctx, -1)); + } + +#if defined(DUK_PRINT_ALERT_FLUSH) + fflush(fh); +#endif + + return 0; +} +#else +/* Faster, less churn, higher footprint option. */ +static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) { + duk_idx_t nargs; + const duk_uint8_t *buf; + duk_size_t sz_buf; + const char nl = '\n'; + duk_uint8_t buf_stack[256]; + + nargs = duk_get_top(ctx); + + /* If argument count is 1 and first argument is a buffer, write the buffer + * as raw data into the file without a newline; this allows exact control + * over stdout/stderr without an additional entrypoint (useful for now). + * Otherwise current print/alert semantics are to ToString() coerce + * arguments, join them with a single space, and append a newline. + */ + + if (nargs == 1 && duk_is_buffer_data(ctx, 0)) { + buf = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &sz_buf); + } else if (nargs > 0) { + duk_idx_t i; + duk_size_t sz_str; + const duk_uint8_t *p_str; + duk_uint8_t *p; + + sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */ + for (i = 0; i < nargs; i++) { + (void) duk_to_lstring(ctx, i, &sz_str); + sz_buf += sz_str; + } + + if (sz_buf <= sizeof(buf_stack)) { + p = (duk_uint8_t *) buf_stack; + } else { + p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf); + } + + buf = (const duk_uint8_t *) p; + for (i = 0; i < nargs; i++) { + p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str); + memcpy((void *) p, (const void *) p_str, sz_str); + p += sz_str; + *p++ = (duk_uint8_t) (i == nargs - 1 ? '\n' : ' '); + } + } else { + buf = (const duk_uint8_t *) &nl; + sz_buf = 1; + } + + /* 'buf' contains the string to write, 'sz_buf' contains the length + * (which may be zero). + */ + + if (sz_buf > 0) { + fwrite((const void *) buf, 1, (size_t) sz_buf, fh); +#if defined(DUK_PRINT_ALERT_FLUSH) + fflush(fh); +#endif + } + + return 0; +} +#endif + +static duk_ret_t duk__print(duk_context *ctx) { + return duk__print_alert_helper(ctx, stdout); +} + +static duk_ret_t duk__alert(duk_context *ctx) { + return duk__print_alert_helper(ctx, stderr); +} + +void duk_print_alert_init(duk_context *ctx, duk_uint_t flags) { + (void) flags; /* unused at the moment */ + + /* XXX: use duk_def_prop_list(). */ + duk_push_global_object(ctx); + duk_push_string(ctx, "print"); + duk_push_c_function(ctx, duk__print, DUK_VARARGS); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); + duk_push_string(ctx, "alert"); + duk_push_c_function(ctx, duk__alert, DUK_VARARGS); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); + duk_pop(ctx); +}
A src/litestorepkg/vendor/duktape/extras/print-alert/duk_print_alert.h

@@ -0,0 +1,18 @@

+#if !defined(DUK_PRINT_ALERT_H_INCLUDED) +#define DUK_PRINT_ALERT_H_INCLUDED + +#include "duktape.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* No flags at the moment. */ + +extern void duk_print_alert_init(duk_context *ctx, duk_uint_t flags); + +#if defined(__cplusplus) +} +#endif /* end 'extern "C"' wrapper */ + +#endif /* DUK_PRINT_ALERT_H_INCLUDED */